import fastify, { FastifyError, FastifyInstance, FastifyReply, FastifyRequest, } from "fastify"; import helmet from "@fastify/helmet"; 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"; const zReqParserPlugin = require("plugins/zReqParser"); const zTokenParserPlugin = require("plugins/zTokenParser"); const apiAuthPlugin = require("plugins/apiauth"); const fs = require("fs"); const join = require("path").join; require("./common/Extend"); export class ApiServer { server: FastifyInstance; public constructor() { this.server = fastify({ logger: true, trustProxy: true }); this.registerPlugins(); } private registerPlugins() { this.server.register(require("@fastify/formbody")); this.server.register(zReqParserPlugin); this.server.register(helmet, { hidePoweredBy: false }); this.server.register(zTokenParserPlugin); this.server.register(apiAuthPlugin, { secret: config.api.token_secret, expiresIn: config.api.token_expiresIn, }); this.server.register(require("@fastify/cors"), {}); } private registerRouter() { logger.log("register api routers"); let self = this; for (let [controller, config] of RouterMap.decoratedRouters) { for (let data of config.data) { logger.info( "add router", data.method || "all", data.path, `${data.target.constructor.name}.${controller.name}()` ); // @ts-ignore self.server[data.method || "all"]( data.path, { preValidation: async function ( request: FastifyRequest, reply: FastifyReply ) { request.roles = config.roles; await this.apiAuth(request, reply); }, }, controller ); } } } /** * 加载所有的controller */ initControllers() { logger.info("Bootstrap controllers..."); const controllers = join(__dirname, "./controllers"); fs.readdirSync(controllers) .filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/)) .forEach((file: any) => { // logger.log(file); return require(join(controllers, file)); }); } async connectDB() { const options = { useNewUrlParser: true, poolSize: 5, keepAlive: true, keepAliveInitialDelay: 300000, useUnifiedTopology: true, }; const uri = config.db_main; logger.info(`connect to ${uri} ...`); try { // await mongoose.createConnection(uri, options) await mongoose.connect(uri, options); logger.log("DB Connected"); } catch (err) { logger.log(`DB Connection Error: ${err.message}`); } } private setErrHandler() { this.server.setNotFoundHandler(function ( request: any, reply: { send: (arg0: { errcode: number; errmsg: string }) => void } ) { reply.send({ errcode: 404, errmsg: "page not found" }); }); this.server.setErrorHandler(function ( error: FastifyError, request: FastifyRequest, reply: FastifyReply ) { let statusCode = (error && error.statusCode) || 100; if (statusCode >= 500) { logger.error(error); } else if (statusCode >= 400) { logger.info(error); } else { logger.error(error); } reply.code(200).send({ errcode: statusCode, errmsg: error ? error.message : "unknown error", }); }); } /** * 格式化接口返回数据, 统一封装成如下格式 * { * code: 0, * msg?: '', * data: any * } * @private */ private setFormatSend() { this.server.addHook( "preSerialization", async (request: FastifyRequest, reply: FastifyReply, payload) => { reply.header("X-Powered-By", "PHP/5.4.16"); // @ts-ignore if (!payload.errcode) { payload = { errcode: 0, data: payload, }; } return payload; } ); } public async start() { let self = this; return new Promise(async (resolve, reject) => { await self.connectDB(); self.initControllers(); self.registerRouter(); self.setErrHandler(); self.setFormatSend(); // new Schedule().start(); this.server.listen( { port: config.api.port }, (err: any, address: any) => { if (err) { logger.log(err); process.exit(0); } resolve && resolve(address); } ); }); } }