完善discord登录流程

This commit is contained in:
zhl 2023-06-12 19:00:07 +08:00
parent 3621d8cc44
commit a2313ff701
3 changed files with 164 additions and 90 deletions

View File

@ -1,74 +1,89 @@
import fastify, { FastifyError, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify' import fastify, {
import helmet from '@fastify/helmet' FastifyError,
import { IncomingMessage, Server, ServerResponse } from 'http' FastifyInstance,
import { RouterMap } from 'decorators/router' FastifyReply,
import { mongoose } from '@typegoose/typegoose' FastifyRequest,
import logger from 'logger/logger' } from "fastify";
import config from 'config/config' import helmet from "@fastify/helmet";
import { ConnectOptions } from 'mongoose' import { IncomingMessage, Server, ServerResponse } from "http";
import { RouterMap } from "decorators/router";
import { mongoose } from "@typegoose/typegoose";
import logger from "logger/logger";
import config from "config/config";
import { ConnectOptions } from "mongoose";
import { DiscordSvr } from "services/discord.svr";
const zReqParserPlugin = require('plugins/zReqParser') const zReqParserPlugin = require("plugins/zReqParser");
const zTokenParserPlugin = require('plugins/zTokenParser') const zTokenParserPlugin = require("plugins/zTokenParser");
const apiAuthPlugin = require('plugins/apiauth') const apiAuthPlugin = require("plugins/apiauth");
const fs = require('fs') const fs = require("fs");
const join = require('path').join const join = require("path").join;
require('./common/Extend') require("./common/Extend");
export class ApiServer { export class ApiServer {
server: FastifyInstance<Server, IncomingMessage, ServerResponse> server: FastifyInstance<Server, IncomingMessage, ServerResponse>;
public constructor() { public constructor() {
this.server = fastify({ logger: true, trustProxy: true }) this.server = fastify({ logger: true, trustProxy: true });
this.registerPlugins() this.registerPlugins();
console.log('version::' + process.version) console.log("version::" + process.version);
} }
private registerPlugins() { private registerPlugins() {
this.server.register(require('@fastify/formbody')) this.server.register(require("@fastify/formbody"));
this.server.register(zReqParserPlugin) this.server.register(zReqParserPlugin);
this.server.register(helmet, { hidePoweredBy: false, contentSecurityPolicy: false }) this.server.register(helmet, {
this.server.register(zTokenParserPlugin) hidePoweredBy: false,
contentSecurityPolicy: false,
});
this.server.register(zTokenParserPlugin);
this.server.register(apiAuthPlugin, { this.server.register(apiAuthPlugin, {
secret: { private: config.api.token_secret_private, public: config.api.token_secret_public }, secret: {
private: config.api.token_secret_private,
public: config.api.token_secret_public,
},
expiresIn: config.api.token_expiresIn, expiresIn: config.api.token_expiresIn,
}) });
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== "production") {
this.server.register(require('@fastify/cors'), {}) this.server.register(require("@fastify/cors"), {});
} }
this.server.register(require('@fastify/view'), { this.server.register(require("@fastify/view"), {
engine: { engine: {
ejs: require('ejs'), ejs: require("ejs"),
}, },
}) });
} }
private registerRouter() { private registerRouter() {
logger.log('register api routers') logger.log("register api routers");
let self = this let self = this;
for (let [controller, config] of RouterMap.decoratedRouters) { for (let [controller, config] of RouterMap.decoratedRouters) {
for (let data of config.data) { for (let data of config.data) {
logger.info( logger.info(
'add router', "add router",
data.method || 'all', data.method || "all",
data.path, data.path,
`${data.target.constructor.name}.${controller.name}()`, `${data.target.constructor.name}.${controller.name}()`
) );
// @ts-ignore // @ts-ignore
self.server[data.method || 'all']( self.server[data.method || "all"](
data.path, data.path,
{ {
preValidation: async function (request: FastifyRequest, reply: FastifyReply) { preValidation: async function (
request.roles = config.roles request: FastifyRequest,
await this.apiAuth(request, reply) reply: FastifyReply
) {
request.roles = config.roles;
await this.apiAuth(request, reply);
}, },
}, },
controller, controller
) );
} }
} }
} }
@ -76,56 +91,64 @@ export class ApiServer {
* controller * controller
*/ */
initControllers() { initControllers() {
logger.info('Bootstrap controllers...') logger.info("Bootstrap controllers...");
const controllers = join(__dirname, './controllers') const controllers = join(__dirname, "./controllers");
fs.readdirSync(controllers) fs.readdirSync(controllers)
.filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/)) .filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/))
.forEach((file: any) => { .forEach((file: any) => {
// logger.log(file); // logger.log(file);
return require(join(controllers, file)) return require(join(controllers, file));
}) });
} }
initSchedules() {} initSchedules() {}
initServcers() {
new DiscordSvr().init();
}
async connectDB() { async connectDB() {
const options: ConnectOptions = { const options: ConnectOptions = {
minPoolSize: 5, minPoolSize: 5,
maxPoolSize: 10, maxPoolSize: 10,
keepAlive: true, keepAlive: true,
keepAliveInitialDelay: 300000, keepAliveInitialDelay: 300000,
} };
const uri = config.db_main const uri = config.db_main;
logger.info(`connect to ${uri} ...`) logger.info(`connect to ${uri} ...`);
try { try {
// await mongoose.createConnection(uri, options) // await mongoose.createConnection(uri, options)
await mongoose.connect(uri, options) await mongoose.connect(uri, options);
logger.log('DB Connected') logger.log("DB Connected");
} catch (err) { } catch (err) {
logger.log(`DB Connection Error: ${err.message}`) logger.log(`DB Connection Error: ${err.message}`);
} }
} }
private setErrHandler() { private setErrHandler() {
this.server.setNotFoundHandler(function ( this.server.setNotFoundHandler(function (
request: any, request: any,
reply: { send: (arg0: { errcode: number; errmsg: string }) => void }, reply: { send: (arg0: { errcode: number; errmsg: string }) => void }
) { ) {
reply.send({ errcode: 404, errmsg: 'page not found' }) reply.send({ errcode: 404, errmsg: "page not found" });
}) });
this.server.setErrorHandler(function (error: FastifyError, request: FastifyRequest, reply: FastifyReply) { this.server.setErrorHandler(function (
let statusCode = (error && error.statusCode) || 100 error: FastifyError,
request: FastifyRequest,
reply: FastifyReply
) {
let statusCode = (error && error.statusCode) || 100;
if (statusCode >= 500) { if (statusCode >= 500) {
logger.error(error) logger.error(error);
} else if (statusCode >= 400) { } else if (statusCode >= 400) {
logger.info(error) logger.info(error);
} else { } else {
logger.error(error) logger.error(error);
} }
reply.code(200).send({ reply.code(200).send({
errcode: statusCode, errcode: statusCode,
errmsg: error ? error.message : 'unknown error', errmsg: error ? error.message : "unknown error",
}) });
}) });
} }
/** /**
* , * ,
@ -137,40 +160,47 @@ export class ApiServer {
* @private * @private
*/ */
private setFormatSend() { private setFormatSend() {
this.server.addHook('preSerialization', async (request: FastifyRequest, reply: FastifyReply, payload) => { this.server.addHook(
reply.header('X-Powered-By', 'PHP/5.4.16') "preSerialization",
// @ts-ignore async (request: FastifyRequest, reply: FastifyReply, payload) => {
if (!payload.errcode) { reply.header("X-Powered-By", "PHP/5.4.16");
// @ts-ignore // @ts-ignore
if (payload.direct) { if (!payload.errcode) {
// @ts-ignore // @ts-ignore
delete payload.direct if (payload.direct) {
return payload // @ts-ignore
} delete payload.direct;
payload = { return payload;
errcode: 0, }
data: payload, payload = {
errcode: 0,
data: payload,
};
} }
return payload;
} }
return payload );
})
} }
public async start() { public async start() {
let self = this let self = this;
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
await self.connectDB() await self.connectDB();
self.initControllers() self.initControllers();
self.registerRouter() self.registerRouter();
self.setErrHandler() self.setErrHandler();
self.setFormatSend() self.setFormatSend();
self.initSchedules() self.initSchedules();
this.server.listen({ port: config.api.port, host: config.api.host }, (err: any, address: any) => { self.initServcers();
if (err) { this.server.listen(
logger.log(err) { port: config.api.port, host: config.api.host },
process.exit(0) (err: any, address: any) => {
if (err) {
logger.log(err);
process.exit(0);
}
resolve && resolve(address);
} }
resolve && resolve(address) );
}) });
})
} }
} }

View File

@ -2,7 +2,11 @@ import BaseController, { ROLE_ANON } from "common/base.controller";
import { ZError } from "common/ZError"; import { ZError } from "common/ZError";
import { role, router } from "decorators/router"; import { role, router } from "decorators/router";
import logger from "logger/logger"; import logger from "logger/logger";
import { exchangeDiscrodCodeForToken, userInfo } from "services/discord.svr"; import {
DiscordSvr,
exchangeDiscrodCodeForToken,
userInfo,
} from "services/discord.svr";
class DiscordController extends BaseController { class DiscordController extends BaseController {
@role(ROLE_ANON) @role(ROLE_ANON)
@ -14,9 +18,19 @@ class DiscordController extends BaseController {
if (code) { if (code) {
access_token = await exchangeDiscrodCodeForToken(code); access_token = await exchangeDiscrodCodeForToken(code);
let uinfo = await userInfo(access_token); let uinfo = await userInfo(access_token);
console.log(uinfo);
return res.view("/templates/discord_redirect.ejs"); return res.view("/templates/discord_redirect.ejs");
} else { } else {
return res.view("/templates/discord_redirect.ejs"); return res.view("/templates/discord_redirect.ejs");
} }
} }
@role(ROLE_ANON)
@router("get /discord/check_user_role")
async checkUserRole(req, res) {
// let { uid } = req.params;
let uid = "1034482894690861116";
let role = await new DiscordSvr().checkUserRole(uid);
return { role };
}
} }

View File

@ -1,3 +1,7 @@
import { ZError } from "common/ZError";
import { singleton } from "decorators/singleton";
const { Client, Events, GatewayIntentBits } = require("discord.js");
export async function exchangeDiscrodCodeForToken(code: string) { export async function exchangeDiscrodCodeForToken(code: string) {
const clientId = process.env.DISCORD_CLIENT_ID; const clientId = process.env.DISCORD_CLIENT_ID;
const clientSecret = process.env.DISCORD_CLIENT_SECRET; const clientSecret = process.env.DISCORD_CLIENT_SECRET;
@ -34,3 +38,29 @@ export async function userInfo(token: string) {
console.log(data); console.log(data);
return data; return data;
} }
@singleton
export class DiscordSvr {
private client: any;
private guild: any;
public async init() {
console.log("DiscordSvr init");
this.client = new Client({ intents: [GatewayIntentBits.Guilds] });
this.client.once(Events.ClientReady, async (c) => {
console.log(`Ready! Logged in as ${c.user.tag}`);
this.guild = await this.client.guilds.fetch(process.env.DISCROD_GUILD_ID);
});
this.client.login(process.env.DISCORD_BOT_TOKEN);
}
public async checkUserRole(uid: string) {
if (!this.guild) {
throw new ZError(10, "DiscordSvr not init");
}
const member = await this.guild.members.fetch(uid);
if (!member) {
return false;
}
return member.roles.cache.has(process.env.DISCORD_ROLE_ID);
}
}