122 lines
4.7 KiB
TypeScript
122 lines
4.7 KiB
TypeScript
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; |