const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const morgan = require('morgan'); const path = require('path'); require('dotenv').config(); const connectDB = require('./config/database'); const errorHandler = require('./middleware/errorHandler'); const { notFound } = require('./middleware/notFound'); const authRoutes = require('./routes/auth'); const equityRoutes = require('./routes/equity'); const userRoutes = require('./routes/user'); const tradeRoutes = require('./routes/trade'); const userEquityRoutes = require('./routes/userEquity'); const ocrRoutes = require('./routes/ocr'); const presetRoutes = require('./routes/preset'); const equityDetailRoutes = require('./routes/equityDetail'); const membershipRoutes = require('./routes/membership'); const settingsRoutes = require('./routes/settings'); const subscribeRoutes = require('./routes/subscribe'); const uploadRoutes = require('./routes/upload'); const wechatMessageRoutes = require('./routes/wechatMessage'); const cron = require('node-cron'); const app = express(); connectDB(); app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'"], scriptSrcAttr: ["'unsafe-inline'"], styleSrc: ["'self'", "'unsafe-inline'", "https:"], imgSrc: ["'self'", "data:", "https:", "http:", "blob:"], connectSrc: ["'self'", "https:", "http:"], }, }, })); app.use(cors()); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); if (process.env.NODE_ENV === 'development') { app.use(morgan('dev')); } app.use('/uploads', express.static(path.join(__dirname, '../public/uploads'))); app.use('/admin', express.static(path.join(__dirname, '../public/admin'))); app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), service: 'quanyixiaozhushou-backend' }); }); app.use('/api/auth', authRoutes); app.use('/api/equity', equityRoutes); app.use('/api/user', userRoutes); app.use('/api/trade', tradeRoutes); app.use('/api/user-equity', userEquityRoutes); app.use('/api/ocr', ocrRoutes); app.use('/api/presets', presetRoutes); app.use('/api/equity-detail', equityDetailRoutes); app.use('/api/membership', membershipRoutes); app.use('/api/settings', settingsRoutes); app.use('/api/subscribe', subscribeRoutes); app.use('/api/upload', uploadRoutes); app.use('/wechat-message', wechatMessageRoutes); const adminRoutes = require('./routes/admin'); app.use('/api/admin', adminRoutes); const { sendExpiryReminders } = require('./routes/subscribe'); cron.schedule('40 9 * * *', async () => { console.log(`[${new Date().toISOString()}] ⏰ 到期提醒定时任务开始执行...`); try { const results = await sendExpiryReminders(); console.log(`[${new Date().toISOString()}] ✅ 到期提醒执行完成: 共${results.total}条, 发送成功${results.sent}条, 失败${results.failed}条`); if (results.errors.length > 0) { console.error('失败详情:', JSON.stringify(results.errors.slice(0, 10))); } } catch (error) { console.error(`[${new Date().toISOString()}] ❌ 到期提醒执行失败:`, error.message); } }, { timezone: 'Asia/Shanghai' }); app.use(notFound); app.use(errorHandler); const PORT = process.env.PORT || 3000; app.listen(PORT, '0.0.0.0', () => { console.log(`🚀 权益小助手后端服务运行在端口 ${PORT}`); console.log(`📊 环境: ${process.env.NODE_ENV || 'development'}`); console.log(`🌐 访问地址: https://api-miniapp.dxz99wyr.cn`); }); process.on('uncaughtException', (error) => { console.error('💥 未捕获异常 - 进程即将退出:', error.message); console.error('Stack:', error.stack); process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { console.error('💥 未处理的Promise拒绝:', reason); if (reason && reason.stack) console.error('Stack:', reason.stack); }); module.exports = app;