File size: 2,527 Bytes
94e1b2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import express, { type Request, type Response, type NextFunction } from 'express'
import path from 'path'
import { appConfig, isDevelopment, printConfig, validateConfig } from '../config/app'
import { corsMiddleware } from '../middlewares/cors'
import { errorHandler, notFoundHandler } from '../middlewares/error-handler'
import routes from '../routes'

interface LoggerLike {
  info(message: string, meta?: unknown): void
  error(message: string, meta?: unknown): void
}

function requestLoggerFactory(logger: LoggerLike) {
  return function requestLogger(req: Request, res: Response, next: NextFunction): void {
    const start = Date.now()

    res.on('finish', () => {
      const duration = Date.now() - start
      if (!req.path.includes('/jobs/')) {
        logger.info('Request completed', {
          method: req.method,
          path: req.path,
          status: res.statusCode,
          duration: `${duration}ms`
        })
      }
    })

    next()
  }
}

export async function initializeExpressApp(app: express.Express, logger: LoggerLike): Promise<void> {
  validateConfig()
  printConfig()

  app.use(express.json({ limit: '10mb' }))
  app.use(express.urlencoded({ extended: true, limit: '10mb' }))
  app.use(corsMiddleware)

  app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
    if (err instanceof SyntaxError && 'body' in err) {
      logger.error('JSON 解析错误', {
        method: req.method,
        path: req.path,
        error: err.message,
        body: req.body
      })
      return res.status(400).json({
        error: 'Invalid JSON',
        message: err.message
      })
    }
    next(err)
  })

  if (isDevelopment()) {
    app.use(requestLoggerFactory(logger))
  }

  app.use('/images', express.static(path.join(process.cwd(), 'public', 'images'), { fallthrough: false }))
  app.use('/videos', express.static(path.join(process.cwd(), 'public', 'videos'), { fallthrough: false }))
  app.use(express.static('public'))
  app.use(routes)

  app.get('*', (req, res) => {
    if (req.path.startsWith('/health') || req.path.startsWith('/api')) {
      return notFoundHandler(req, res, () => {})
    }
    if (path.extname(req.path)) {
      return notFoundHandler(req, res, () => {})
    }
    const indexPath = path.join(__dirname, '..', '..', 'public', 'index.html')
    res.sendFile(indexPath, (err) => {
      if (err) {
        return notFoundHandler(req, res, () => {})
      }
    })
  })

  app.use(errorHandler)
}

export { appConfig }