2026-03-12 11:26:49 +08:00
|
|
|
const express = require('express');
|
|
|
|
|
const cors = require('cors');
|
|
|
|
|
const path = require('path');
|
2026-03-19 11:35:12 +08:00
|
|
|
const morgan = require('morgan');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
|
|
|
|
// 初始化日志目录
|
|
|
|
|
const logsDir = path.join(__dirname, 'logs');
|
|
|
|
|
if (!fs.existsSync(logsDir)) {
|
|
|
|
|
fs.mkdirSync(logsDir, { recursive: true });
|
|
|
|
|
}
|
2026-03-12 11:26:49 +08:00
|
|
|
|
|
|
|
|
// 初始化数据库
|
|
|
|
|
require('./db/init');
|
|
|
|
|
|
|
|
|
|
const authRoutes = require('./routes/auth');
|
|
|
|
|
const ticketRoutes = require('./routes/tickets');
|
|
|
|
|
const lotteryRoutes = require('./routes/lottery');
|
|
|
|
|
const ocrRoutes = require('./routes/ocr');
|
2026-03-19 11:35:12 +08:00
|
|
|
const { standardLimiter } = require('./middleware/rateLimiter');
|
|
|
|
|
const errorHandler = require('./middleware/errorHandler');
|
|
|
|
|
const logger = require('./services/logger');
|
2026-03-12 11:26:49 +08:00
|
|
|
|
|
|
|
|
const app = express();
|
|
|
|
|
const PORT = process.env.PORT || 4444;
|
|
|
|
|
|
2026-03-19 11:35:12 +08:00
|
|
|
// 创建 morgan stream 用于日志
|
|
|
|
|
const morganStream = {
|
|
|
|
|
write: (message) => logger.http(message.trim())
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-12 11:26:49 +08:00
|
|
|
// 中间件
|
|
|
|
|
app.use(cors());
|
2026-03-19 11:35:12 +08:00
|
|
|
app.use(express.json({ limit: '10mb' }));
|
|
|
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
|
app.use(morgan('combined', { stream: morganStream }));
|
2026-03-12 11:26:49 +08:00
|
|
|
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
|
|
|
|
|
2026-03-19 11:35:12 +08:00
|
|
|
// 全局限流 (应用于所有API)
|
|
|
|
|
app.use('/api', standardLimiter);
|
|
|
|
|
|
2026-03-12 11:26:49 +08:00
|
|
|
// API 路由
|
|
|
|
|
app.use('/api/auth', authRoutes);
|
|
|
|
|
app.use('/api/tickets', ticketRoutes);
|
|
|
|
|
app.use('/api/lottery', lotteryRoutes);
|
|
|
|
|
app.use('/api/ocr', ocrRoutes);
|
|
|
|
|
|
|
|
|
|
// 健康检查
|
|
|
|
|
app.get('/api/health', (req, res) => {
|
|
|
|
|
res.json({ status: 'ok', time: new Date().toISOString() });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 静态文件服务 - 前端构建产物
|
|
|
|
|
const clientDist = path.join(__dirname, '../client/dist');
|
|
|
|
|
app.use(express.static(clientDist));
|
|
|
|
|
|
|
|
|
|
// SPA 路由 - 所有非 API 请求返回 index.html
|
|
|
|
|
app.get('*', (req, res) => {
|
|
|
|
|
res.sendFile(path.join(clientDist, 'index.html'));
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-19 11:35:12 +08:00
|
|
|
// 全局错误处理 (必须放在最后)
|
|
|
|
|
app.use(errorHandler);
|
|
|
|
|
|
2026-03-12 11:26:49 +08:00
|
|
|
app.listen(PORT, '0.0.0.0', () => {
|
2026-03-19 11:35:12 +08:00
|
|
|
logger.info(`服务器运行在 http://0.0.0.0:${PORT}`);
|
2026-03-12 11:26:49 +08:00
|
|
|
});
|