import express from 'express'; import cors from 'cors'; import dotenv from 'dotenv'; import path from 'path'; import { logger } from './utils/logger'; import { errorHandler } from './middleware/errorHandler'; import { initSMSService } from './services/smsService'; import { initAppleAuthService } from './services/appleAuthService'; // Load environment variables dotenv.config(); // 初始化短信服务 initSMSService(); // 初始化 Apple 认证服务 initAppleAuthService(); const app = express(); const PORT = process.env.PORT || 3000; // Middleware app.use(cors()); // ✅ 增加 JSON body 大小限制(支持最大 500KB,足够处理 100K 字符的内容) app.use(express.json({ limit: '100mb' })); app.use(express.urlencoded({ extended: true, limit: '100mb' })); // ✅ 捕获 body-parser 的 413 错误(请求体过大) app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => { if (err.type === 'entity.too.large' || err.status === 413) { logger.warn(`[Request] 请求体过大: path=${req.path}, method=${req.method}, contentLength=${req.headers['content-length']}`); return res.status(413).json({ success: false, error: { message: '请求内容过长,请缩短后重试(建议不超过10万字)', }, }); } next(err); }); // 静态文件服务(用于课程录入工具) app.use(express.static('public')); // Request logging app.use((req, res, next) => { logger.info(`${req.method} ${req.path}`); next(); }); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok', message: 'Wild Growth API is running' }); }); // Support page route (for App Store Connect requirement) app.get('/support', (req, res) => { const supportPath = path.join(process.cwd(), 'public', 'support.html'); res.sendFile(supportPath); }); // Marketing/Homepage route (for App Store Connect Marketing URL) app.get('/', (req, res) => { const indexPath = path.join(process.cwd(), 'public', 'index.html'); res.sendFile(indexPath); }); // API Routes import authRoutes from './routes/authRoutes'; import userRoutes from './routes/userRoutes'; import courseRoutes from './routes/courseRoutes'; import learningRoutes from './routes/learningRoutes'; import paymentRoutes from './routes/paymentRoutes'; import uploadRoutes from './routes/uploadRoutes'; import myCoursesRoutes from './routes/myCoursesRoutes'; import noteRoutes from './routes/noteRoutes'; import notebookRoutes from './routes/notebookRoutes'; import aiCourseRoutes from './routes/aiCourseRoutes'; import aiTopicsRoutes from './routes/aiTopicsRoutes'; import aiThinkingFlowRoutes from './routes/aiThinkingFlowRoutes'; import promptRoutes from './routes/promptRoutes'; import operationalBannerRoutes, { adminRouter as operationalBannerAdminRoutes } from './routes/operationalBannerRoutes'; import { adminCallRecordsRouter } from './routes/adminCallRecordsRoutes'; import playgroundRoutes from './routes/playgroundRoutes'; import analyticsRoutes from './routes/analyticsRoutes'; import { authenticate, optionalAuthenticate } from './middleware/auth'; app.use('/api/auth', authRoutes); app.use('/api/user', authenticate, userRoutes); // ✅ 移除全局认证,在路由文件中选择性应用(支持游客模式) app.use('/api/courses', courseRoutes); app.use('/api/lessons', learningRoutes); app.use('/api/payment', authenticate, paymentRoutes); app.use('/api/upload', uploadRoutes); app.use('/api/my-courses', myCoursesRoutes); app.use('/api/notes', noteRoutes); app.use('/api/notebooks', notebookRoutes); // ✅ Phase 2: 新增笔记本路由 app.use('/api/ai/courses', aiCourseRoutes); // ✅ AI 课程生成路由 app.use('/api/ai/topics', aiTopicsRoutes); // ✅ AI 热门主题路由 app.use('/api/ai/thinking-flow', aiThinkingFlowRoutes); // ✅ AI 思考流路由 app.use('/api/ai/prompts', promptRoutes); // ✅ Prompt 管理路由(V3.0) app.use('/api/operational-banners', operationalBannerRoutes); // 发现页运营位(公开 GET) app.use('/api/admin/operational-banners', operationalBannerAdminRoutes); // 运营位管理(后台) app.use('/api/admin/generation-tasks', adminCallRecordsRouter); // 调用记录(后台) app.use('/api/playground', playgroundRoutes); // 小红书封面模板 Playground app.use('/api/analytics', analyticsRoutes); // ✅ V1.0 埋点体系 // Error handling middleware (must be last) app.use(errorHandler); // 导出 app(用于测试) export { app }; // 启动服务器(仅在非测试环境) if (process.env.NODE_ENV !== 'test') { app.listen(PORT, () => { logger.info(`🚀 Server running on http://localhost:${PORT}`); logger.info(`📝 Environment: ${process.env.NODE_ENV || 'development'}`); }); } export default app;