task-svr/src/api.server.ts
2024-05-14 16:23:18 +08:00

190 lines
5.5 KiB
TypeScript

import fastify, { FastifyError, FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
import helmet from '@fastify/helmet'
import * as dotenv from 'dotenv'
const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development'
dotenv.config({ path: envFile })
import { IncomingMessage, Server, ServerResponse } from 'http'
import { mongoose } from '@typegoose/typegoose'
import logger from 'logger/logger'
import NonceRecordSchedule from 'schedule/noncerecord.schedule'
import { RouterMap, ZRedisClient } from 'zutils'
import { SyncLocker } from 'common/SyncLocker'
import CacheSchedule from 'schedule/cache.schedule'
import CodeTaskSchedule from 'schedule/codetask.schedule'
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<Server, IncomingMessage, ServerResponse>
public constructor() {
this.server = fastify({ logger: true, trustProxy: true, bodyLimit: 100 * 1024 * 1024 })
this.registerPlugins()
}
private registerPlugins() {
this.server.register(require('@fastify/formbody'))
this.server.register(require('fastify-xml-body-parser'))
this.server.register(zReqParserPlugin)
this.server.register(helmet, { hidePoweredBy: false })
this.server.register(zTokenParserPlugin)
this.server.register(apiAuthPlugin, {
secret: process.env.API_TOKEN_SECRET,
expiresIn: process.env.API_TOKEN_EXPIRESIN,
})
if (process.env.NODE_ENV !== 'production') {
this.server.register(require('@fastify/cors'), {})
mongoose.set('debug', true)
}
}
private registerRouter() {
logger.log('register api routers')
let self = this
this.server.get(
'/api/alive',
{
schema: {
// @ts-ignore
hide: true,
},
},
(_, res) => {
res.status(200).send()
},
)
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,
maxPoolSize: 5,
useUnifiedTopology: true,
}
const uri = process.env.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}`)
}
let opts = { url: process.env.REDIS }
new ZRedisClient(opts)
logger.log('REDIS Connected')
}
private initSchedules() {
new CacheSchedule().scheduleAll()
new CodeTaskSchedule().scheduleAll()
new NonceRecordSchedule().scheduleAll()
}
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, req: FastifyRequest, reply: FastifyReply) {
let statusCode = (error && error.statusCode) || 100
if (statusCode >= 500) {
logger.error(error)
} else if (statusCode >= 400) {
logger.error(error)
} else {
logger.info(error?.message || error || 'unknown 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 (req: FastifyRequest, reply: FastifyReply, payload) => {
reply.header('X-Powered-By', 'PHP/5.4.16')
await new SyncLocker().unlock(req)
// @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()
self.initSchedules()
this.server.listen(
{ port: parseInt(process.env.API_PORT), host: process.env.API_HOST },
(err: any, address: any) => {
if (err) {
logger.log(err)
process.exit(0)
}
resolve && resolve(address)
},
)
})
}
}