| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import 'reflect-metadata'
- import { fastify, errorCodes } from 'fastify'
- import cors from '@fastify/cors'
- import jwt from '@fastify/jwt'
- import swagger from '@fastify/swagger'
- import swaggerUi from '@fastify/swagger-ui'
- import multipart from '@fastify/multipart'
- import fastifyEnv, { FastifyEnvOptions } from '@fastify/env'
- import { schema } from './config/env'
- import { createDataSource } from './config/database'
- import userRoutes from './routes/user.routes'
- import fileRoutes from './routes/file.routes'
- import sysConfigRoutes from './routes/sys-config.routes'
- import incomeRecordsRoutes from './routes/income-records.routes'
- import financeRoutes from './routes/finance.routes'
- import teamRoutes from './routes/team.routes'
- import teamMembersRoutes from './routes/team-members.routes'
- import promotionLinkRoutes from './routes/promotion-link.routes'
- import paymentRoutes from './routes/payment.routes'
- import memberRoutes from './routes/member.routes'
- import userShareRoutes from './routes/user-invite.routes'
- import teamDomainRoutes from './routes/team-domain.routes'
- import bannerRoutes from './routes/banner.routes'
- import { authenticate } from './middlewares/auth.middleware'
- import { createRedisClient, closeRedisClient } from './config/redis'
- import { BannerStatisticsScheduler } from './scheduler/banner-statistics.scheduler'
- const options: FastifyEnvOptions = {
- schema: schema,
- dotenv: {
- debug: false
- }
- }
- export const createApp = async () => {
- const app = fastify({
- disableRequestLogging: true,
- trustProxy: true,
- logger: {
- level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
- transport: {
- target: 'pino-pretty',
- options: {
- translateTime: 'yy/mm/dd HH:MM:ss Z',
- ignore: 'pid,hostname'
- }
- }
- }
- })
- await app.register(fastifyEnv, options)
- app.register(cors, {
- origin: true,
- methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
- })
- app.register(jwt, {
- secret: app.config.JWT_SECRET,
- sign: {
- expiresIn: app.config.JWT_EXPIRES_IN
- }
- })
- app.register(multipart, {
- limits: {
- fileSize: 200 * 1024 * 1024
- }
- })
- app.register(swagger, {
- swagger: {
- info: {
- title: 'Wuma API',
- description: 'Wuma API documentation',
- version: '1.0.0'
- },
- host: 'localhost',
- schemes: ['http'],
- consumes: ['application/json'],
- produces: ['application/json']
- }
- })
- if (app.config.NODE_ENV === 'development') {
- app.register(swaggerUi, {
- routePrefix: '/documentation'
- })
- }
- app.register(userRoutes, { prefix: '/api/users' })
- app.register(fileRoutes, { prefix: '/api/files' })
- app.register(sysConfigRoutes, { prefix: '/api/config' })
- app.register(incomeRecordsRoutes, { prefix: '/api/income' })
- app.register(financeRoutes, { prefix: '/api/finance' })
- app.register(teamRoutes, { prefix: '/api/teams' })
- app.register(teamMembersRoutes, { prefix: '/api/team-members' })
- app.register(teamDomainRoutes, { prefix: '/api/team-domains' })
- app.register(promotionLinkRoutes, { prefix: '/api/links' })
- app.register(paymentRoutes, { prefix: '/api/payment' })
- app.register(memberRoutes, { prefix: '/api/member' })
- app.register(userShareRoutes, { prefix: '/api/user-share' })
- app.register(bannerRoutes, { prefix: '/api/banners' })
- // 添加 /account 路由重定向到用户资料
- app.get('/account', { onRequest: [authenticate] }, async (request, reply) => {
- return reply.redirect('/api/users/profile')
- })
- const dataSource = createDataSource(app)
- await dataSource.initialize()
- app.decorate('dataSource', dataSource)
- // 初始化Redis(如果未配置则使用默认值 localhost:6379)
- try {
- const redis = createRedisClient({
- REDIS_HOST: app.config.REDIS_HOST || 'localhost',
- REDIS_PORT: app.config.REDIS_PORT || 6379,
- REDIS_PASSWORD: app.config.REDIS_PASSWORD,
- REDIS_DB: app.config.REDIS_DB || 0
- })
- app.decorate('redis', redis)
- if (app.config.REDIS_HOST) {
- app.log.info(`Redis connected successfully to ${app.config.REDIS_HOST}:${app.config.REDIS_PORT || 6379}`)
- } else {
- app.log.info('Redis connected successfully using default configuration (localhost:6379)')
- }
- } catch (error) {
- app.log.warn(`Redis connection failed, continuing without Redis: ${error instanceof Error ? error.message : String(error)}`)
- }
- // 初始化定时任务(需要Redis支持)
- let bannerStatisticsScheduler: BannerStatisticsScheduler | null = null
- if (app.redis) {
- try {
- bannerStatisticsScheduler = new BannerStatisticsScheduler(app)
- bannerStatisticsScheduler.start()
- app.decorate('bannerStatisticsScheduler', bannerStatisticsScheduler)
- } catch (error) {
- app.log.warn(`定时任务初始化失败: ${error instanceof Error ? error.message : String(error)}`)
- }
- }
- app.addHook('onClose', async () => {
- // 停止定时任务
- if (bannerStatisticsScheduler) {
- bannerStatisticsScheduler.stop()
- }
- await closeRedisClient()
- await dataSource.destroy()
- process.exit(0)
- })
- return app
- }
|