import fastify, { 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 { AdminRole } from 'models/admin/AdminRole' import { Shop } from './models/shop/Shop' import { MongoTool } from './services/MongoTool' import { GameItem } from './models/content/GameItem' const rbacPlugin = require('plugins/zrbac') const zAuthPlugin = require('plugins/zauth') const zReqParserPlugin = require('plugins/zReqParser') const fs = require('fs') const join = require('path').join require('./common/Extend') export class AdminServer { server: FastifyInstance public constructor() { this.server = fastify({ logger: true, trustProxy: true }) // @ts-ignore this.server['jcname'] = '222' this.registerPlugins() } private registerPlugins() { this.server.register(require('fastify-formbody')) this.server.register(zReqParserPlugin) this.server.register(helmet, { hidePoweredBy: false }) this.server.register(zAuthPlugin, { secret: config.admin.token_secret, expiresIn: config.admin.token_expiresIn, }) this.server.register(require('fastify-cors'), {}) } private async registerRbac() { let roles = await AdminRole.allRbacJson() this.server.register(rbacPlugin, { roles: roles }) } 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 request.permissions = config.permissions request.depts = config.depts await this.zAuth(request, reply) }, }, controller, ) } } } /** * 加载所有的controller */ initControllers() { logger.info('Bootstrap controllers...') const controllers = join(__dirname, 'admin/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, reply) { reply.send({ code: 404, msg: 'page not found' }) }) this.server.setErrorHandler(function (error, request, reply) { let statusCode = error.statusCode || 100 if (statusCode >= 500) { logger.error(error) } else if (statusCode >= 400) { logger.info(error) } else { logger.error(error) } reply.code(200).send({ code: statusCode, msg: error.message }) }) } /** * 格式化接口返回数据, 统一封装成如下格式 * { * 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 = { code: 0, data: payload, } } return payload }) } private async initUniqueKeyCache() { let id = await Shop.maxNumKey() new MongoTool().init('shop', id) id = await GameItem.maxNumKey() new MongoTool().init('gameitem', id) } public async start() { let self = this return new Promise(async (resolve, reject) => { await self.connectDB() await self.registerRbac() await self.initUniqueKeyCache() self.initControllers() self.registerRouter() self.setErrHandler() self.setFormatSend() this.server.listen({ port: config.admin.port }, (err: any, address: any) => { if (err) { logger.log(err) process.exit(0) } resolve && resolve(address) }) }) } }