import express from 'express'; import bodyParser from 'body-parser'; import dotenv from 'dotenv'; import swaggerUi from 'swagger-ui-express'; import specs from './lib/swagger.js'; import cors from 'cors'; // 配置 dotenv dotenv.config(); // 处理API前缀配置 function getApiPrefix() { let prefix; if ('API_PREFIX' in process.env) { prefix = process.env.API_PREFIX; } else { prefix = '/api/v1'; } // 如果明确设置为空字符串,则返回空 if (prefix === '') { return ''; } // 标准化前缀格式:确保以斜杠开头且不以斜杠结尾 if (prefix && !prefix.startsWith('/')) { prefix = '/' + prefix; } if (prefix && prefix.length > 1 && prefix.endsWith('/')) { prefix = prefix.slice(0, -1); } return prefix; } // 处理CORS配置 function getCorsConfig() { const enableCors = process.env.ENABLE_CORS === 'true'; if (!enableCors) { return null; // 不启用CORS } const corsOrigins = process.env.CORS_ORIGINS; let origins = ['http://localhost:3000']; // 默认安全配置 if (corsOrigins) { try { // 支持多个源,用逗号分隔 origins = corsOrigins.split(',').map(origin => origin.trim()).filter(origin => origin); } catch (error) { console.error('CORS配置解析错误,使用默认配置:', error.message); } } return { origin: origins, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key'], credentials: true, optionsSuccessStatus: 200 // 支持老版本浏览器 }; } const apiPrefix = getApiPrefix(); const corsConfig = getCorsConfig(); import BrowserManager from './lib/browser.js'; import StorageManager from './lib/storage.js'; import {pdfHandler, posterHandler, statusHandler} from './lib/routes.js'; import apiKeyAuth from './lib/auth.js'; import {validationErrorResponse} from './lib/response.js'; // 初始化存储管理器 const storageManager = new StorageManager(process.env); // 初始化浏览器管理器 const browserManager = new BrowserManager(); await browserManager.initBrowser(2); const app = express(); // 应用CORS中间件(如果启用) if (corsConfig) { app.use(cors(corsConfig)); console.log('✅ CORS已启用,允许的源:', corsConfig.origin); } app.use(bodyParser.json({limit: '10mb'})); // 配置Swagger UI app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs)); // 提供上传文件的静态访问 app.use('/uploads', express.static('uploads')); /** * @swagger * /status: * get: * summary: 健康检查 * description: 检查服务是否正常运行 * tags: [Health] * responses: * 200: * description: 服务正常运行 * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * example: true * description: 请求是否成功 * data: * type: object * example: {} * description: 响应数据 * message: * type: string * example: "Service is running" * description: 响应消息 * code: * type: number * example: 200 * description: 响应代码 * 500: * description: 服务异常 */ // 健康检查接口 app.get('/status', statusHandler); // 添加安全头 app.use((req, res, next) => { res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('X-XSS-Protection', '1; mode=block'); next(); }); // 海报生成接口 app.post(`${apiPrefix}/poster`, apiKeyAuth, async function (req, res) { // 基本的安全检查 if (!req.headers['content-type'] || !req.headers['content-type'].includes('application/json')) { return res.status(400).json(validationErrorResponse('Content-Type must be application/json', 3002)); } await posterHandler( req, res, browserManager, storageManager ); }); // PDF生成接口 app.post(`${apiPrefix}/pdf`, apiKeyAuth, async function (req, res) { // 基本的安全检查 if (!req.headers['content-type'] || !req.headers['content-type'].includes('application/json')) { return res.status(400).json(validationErrorResponse('Content-Type must be application/json', 3002)); } await pdfHandler( req, res, browserManager, storageManager ); }); const port = process.env.PORT || 3000; // 服务器启动时显示配置信息 console.log('🚀 服务器配置信息:'); console.log(` 端口: ${port}`); console.log(` API前缀: ${apiPrefix}`); console.log(` CORS状态: ${corsConfig ? '已启用' : '已禁用'}`); if (corsConfig) { console.log(` 允许的源: ${corsConfig.origin.join(', ')}`); } console.log(' 健康检查: /status'); console.log(` 海报API: ${apiPrefix}/poster`); console.log(` PDF API: ${apiPrefix}/pdf`); console.log(' API文档: /api-docs'); app.listen(port, () => console.log(`\n✨ Puppeteer app listening on port ${port}!`));