Files
ggl/start-production.cjs
2025-10-05 13:34:28 +08:00

131 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { spawn } = require('child_process');
const path = require('path');
const express = require('express');
const fs = require('fs');
// 加载生产环境配置文件
require('dotenv').config({ path: path.join(__dirname, '.env.production') });
// 生产环境配置 - 宝塔面板兼容
const API_PORT = process.env.PORT || 4001;
const FRONTEND_PORT = process.env.FRONTEND_PORT || 4002;
const NODE_ENV = 'production';
const DOMAIN = process.env.DOMAIN || 'ggl.xi.plus';
// 验证端口范围 (4001-4010)
if (API_PORT < 4001 || API_PORT > 4010) {
console.warn(`警告: API端口 ${API_PORT} 不在推荐范围 4001-4010 内`);
}
if (FRONTEND_PORT < 4001 || FRONTEND_PORT > 4010) {
console.warn(`警告: 前端端口 ${FRONTEND_PORT} 不在推荐范围 4001-4010 内`);
}
// 设置环境变量
process.env.NODE_ENV = NODE_ENV;
process.env.PORT = API_PORT;
process.env.DOMAIN = DOMAIN;
console.log(`=== 梦回高句丽 - 生产环境启动 ===`);
console.log(`域名: ${DOMAIN}`);
console.log(`后端API端口: ${API_PORT}`);
console.log(`前端静态文件端口: ${FRONTEND_PORT}`);
console.log(`工作目录: ${process.cwd()}`);
console.log(`Node.js版本: ${process.version}`);
console.log(`启动时间: ${new Date().toLocaleString('zh-CN')}`);
// 检查dist目录是否存在
const distPath = path.join(__dirname, 'dist');
if (!fs.existsSync(distPath)) {
console.error(`错误: dist目录不存在 (${distPath})`);
console.log('请先运行 npm run build 构建前端项目');
process.exit(1);
}
// 启动前端静态文件服务
const app = express();
// 安全头设置
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.use(express.static(distPath, {
maxAge: '1y',
etag: true,
lastModified: true
}));
// SPA路由处理
app.get('*', (req, res) => {
res.sendFile(path.join(distPath, 'index.html'));
});
const frontendServer = app.listen(FRONTEND_PORT, '0.0.0.0', () => {
console.log(`✅ 前端静态文件服务已启动`);
console.log(` - 端口: ${FRONTEND_PORT}`);
console.log(` - 访问地址: http://${DOMAIN}:${FRONTEND_PORT}`);
});
// 检查API服务器文件是否存在
const apiServerPath = path.join(__dirname, 'api', 'server.ts');
if (!fs.existsSync(apiServerPath)) {
console.error(`错误: API服务器文件不存在 (${apiServerPath})`);
process.exit(1);
}
// 启动后端API服务器
console.log(`🚀 启动后端API服务器...`);
const apiServer = spawn('node', ['--import', 'tsx/esm', 'api/server.ts'], {
stdio: 'inherit',
env: {
...process.env,
NODE_ENV: NODE_ENV,
PORT: API_PORT,
DOMAIN: DOMAIN
},
cwd: __dirname
});
// 处理API服务器进程退出
apiServer.on('close', (code) => {
console.log(`❌ API服务器进程退出退出码: ${code}`);
frontendServer.close();
process.exit(code);
});
// 处理API服务器错误
apiServer.on('error', (err) => {
console.error('❌ 启动API服务器时发生错误:', err);
frontendServer.close();
process.exit(1);
});
// API服务器启动成功提示
setTimeout(() => {
console.log(`✅ 后端API服务已启动`);
console.log(` - 端口: ${API_PORT}`);
console.log(` - API地址: http://${DOMAIN}:${API_PORT}/api`);
console.log(`\n🌐 服务器已完全启动,可通过宝塔面板管理`);
}, 2000);
// 优雅关闭
process.on('SIGINT', () => {
console.log('\n收到 SIGINT 信号,正在关闭服务器...');
frontendServer.close();
apiServer.kill('SIGINT');
});
process.on('SIGTERM', () => {
console.log('\n收到 SIGTERM 信号,正在关闭服务器...');
frontendServer.close();
apiServer.kill('SIGTERM');
});
// 处理前端服务器错误
frontendServer.on('error', (err) => {
console.error('前端静态文件服务器错误:', err);
});