// | ------------------------------------------------------------ // | @版本: version 0.1 // | @创建人: 【Nie-x7129】 // | @E-mail: x71291@outlook.com // | @所在项目: 2160 // | @文件描述: app.js - // | @创建时间: 2024-01-13 21:05 // | @更新时间: 2024-01-13 21:05 // | @修改记录: // | -*-*-*- (时间--修改人--修改说明) -*-*-*- // | = // | ------------------------------------------------------------ // 加载时间工具 import '#tools/dateFormate.js'; // ! 读取ENV const ENV = process.argv[2]?.trim().toLowerCase() || 'production'; // ! 是否为开发环境 const isDev = ENV == 'development' || ENV == 'dev' || ENV == 'develop'; // ! 导入环境标识 global.isDev = isDev; // ! 加载配置文件 import {devConf, proConf} from '#config'; // ! 导入配置文件 global.conf = global.isDev ? devConf : proConf; // * 加载核心框架 import Fastify from 'fastify'; // ! 加载路由 import routes from '#routes/index.js'; // ! 获取当前设备IP import getLocalIPv4 from '#tools/getLocalIp.js'; // * ejs模板 import ejs from 'ejs'; // * ajv错误模板 import ajvErrors from 'ajv-errors'; // ! 自定义Ajv参数验证插件 import { isLowerCase, isTrim } from '#plugins/ajv/index.js'; async function start(){ // | 创建fastify实例 const fastify = new Fastify({ logger: { level: 'debug', serializers: { req(request) { // 不返回任何东西,实际上就是屏蔽了自动的请求日志 return {}; }, res(reply) { // 同理,不返回任何东西以屏蔽响应日志 return {}; } }, transport: { target: 'pino-pretty', options: { level: 'error', // 这个选项确保输出带颜色 colorize: true, translateTime: 'yyyy-mm-dd HH:MM:ss', // 对象信息打印在一行 singleLine: true } } }, ajv: { customOptions: { removeAdditional: true, useDefaults: true, coerceTypes: true, allErrors: true, // 允许使用联合模式 严格模式下 allowUnionTypes: true }, plugins: [ ajvErrors, // 这种方式完美解决,开心, 实际上这是一个函数,会传进去ajv实例对象 isLowerCase, isTrim, ] } }); // | 装饰器示例 // @ 配置文件 fastify.decorate('conf', global.conf); fastify.decorate('authenticate', async function(request, reply) { try { // 校验token,并生成request的user参数 await request.jwtVerify(); } catch (err) { // token校验未通过,记录错误 fastify.log.debug({ reqId: request.id, Auth: err.message }); reply.code(err.statusCode).send({ statusCode: err.statusCode, message: err.message, error: err.name }); } }); // | 生命周期 // @ 请求拦截 fastify.addHook('onRequest', async function (request, reply) { return; }); // @ 响应拦截 fastify.addHook('onResponse', async function (request, reply){ return; }); // @ 发送消息拦截 fastify.addHook('onSend', async (request, reply, payload) => { // fastify.log.info(reply.getHeader('content-type')); if(reply.statusCode === 200 && !request.url.includes('/api/file') ){ return `{"statusCode": 200, "status": "success", "data": ${payload}}`; } return payload; }); // @ 错误拦截 fastify.setErrorHandler(async (error, request, reply) => { // 自定义错误处理逻辑 fastify.log.error(error); reply .code(error.statusCode) .send({ statusCode: error.statusCode, message: error.message, error: error.name }); }); // | 插件 // @ 注册防止恶意请求封ip await fastify.register(import('@fastify/rate-limit'), global.conf.rateLimit); // @ 注册html模板 fastify.register(import('@fastify/view'), { engine: {ejs}, includeViewExtension: true, root: 'views' }); // @ 错误处理工具 fastify.register(import('@fastify/sensible')); // @ 注册redis await fastify.register(import('@fastify/redis'), { // 配置文件基础redis配置 host,port,password ...global.conf.redis, // fastify调用的名称空间 namespace: 'db1', // 指向的redis数据库0-15 db: 1, // 4 (IPv4) or 6 (IPv6) family: 4, // redis连接中的名字 connectionName: global.conf.projectName }); // fastify.log.info(await fastify.redis.db1.set('name', 'xsx', 'EX', 60)); // @ 注册jwt fastify.register(import('@fastify/jwt'), global.conf.jwt); // @ 注册路由 await fastify.register(routes); // | 监听指定端口 await fastify.listen(global.conf.listen); // | 输出监听地址和端口 getLocalIPv4().map(i => isDev && fastify.log.info(`http://${i}:${fastify.conf.listen.port}`)); // console.log(fastify.printPlugins()) // console.log(fastify.printRoutes({ commonPrefix: false })); // console.log(fastify.printRoutes({ method: 'GET' })); } start();