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'; import { initCfgData } from './common/GConfig' import { Schedule } from './clock/Schedule' import { QCategoryCache } from './services/QCategoryCache' 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}); 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('find api router', data.method || 'all', data.path, 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, 'api/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.code) { 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(); initCfgData(); await new QCategoryCache().init(); 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); }) }); } }