2026-02-02 18:16:15 +08:00
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Standalone 模式启动脚本
|
|
|
|
|
|
* 自动复制 .next/static 和 public 到 standalone 目录后启动服务器
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
const path = require('path');
|
|
|
|
|
|
const { spawn } = require('child_process');
|
|
|
|
|
|
|
|
|
|
|
|
const rootDir = path.join(__dirname, '..');
|
|
|
|
|
|
const standaloneDir = path.join(rootDir, '.next', 'standalone');
|
|
|
|
|
|
const staticSrc = path.join(rootDir, '.next', 'static');
|
|
|
|
|
|
const staticDst = path.join(standaloneDir, '.next', 'static');
|
|
|
|
|
|
const publicSrc = path.join(rootDir, 'public');
|
|
|
|
|
|
const publicDst = path.join(standaloneDir, 'public');
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 递归复制目录
|
|
|
|
|
|
*/
|
|
|
|
|
|
function copyDir(src, dst) {
|
|
|
|
|
|
if (!fs.existsSync(src)) {
|
|
|
|
|
|
console.warn(`⚠️ 跳过复制:${src} 不存在`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除旧目录
|
|
|
|
|
|
if (fs.existsSync(dst)) {
|
|
|
|
|
|
fs.rmSync(dst, { recursive: true, force: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建目标目录
|
|
|
|
|
|
fs.mkdirSync(dst, { recursive: true });
|
|
|
|
|
|
|
|
|
|
|
|
// 复制文件
|
|
|
|
|
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
|
|
|
|
for (const entry of entries) {
|
|
|
|
|
|
const srcPath = path.join(src, entry.name);
|
|
|
|
|
|
const dstPath = path.join(dst, entry.name);
|
|
|
|
|
|
|
|
|
|
|
|
if (entry.isDirectory()) {
|
|
|
|
|
|
copyDir(srcPath, dstPath);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
fs.copyFileSync(srcPath, dstPath);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('🚀 准备启动 Standalone 服务器...\n');
|
|
|
|
|
|
|
|
|
|
|
|
// 检查 standalone 目录
|
|
|
|
|
|
if (!fs.existsSync(standaloneDir)) {
|
|
|
|
|
|
console.error('❌ 错误:未找到 .next/standalone 目录');
|
|
|
|
|
|
console.error(' 请先运行: pnpm build');
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 复制静态资源
|
|
|
|
|
|
console.log('📦 复制静态资源...');
|
|
|
|
|
|
console.log(' .next/static → .next/standalone/.next/static');
|
|
|
|
|
|
copyDir(staticSrc, staticDst);
|
|
|
|
|
|
|
|
|
|
|
|
console.log(' public → .next/standalone/public');
|
|
|
|
|
|
copyDir(publicSrc, publicDst);
|
|
|
|
|
|
|
|
|
|
|
|
console.log('✅ 静态资源复制完成\n');
|
|
|
|
|
|
|
|
|
|
|
|
// 启动服务器
|
|
|
|
|
|
const serverPath = path.join(standaloneDir, 'server.js');
|
|
|
|
|
|
// 优先使用环境变量 PORT,未设置时提示并退出
|
|
|
|
|
|
const port = process.env.PORT;
|
|
|
|
|
|
|
|
|
|
|
|
if (!port) {
|
|
|
|
|
|
console.error('❌ 错误:未设置 PORT 环境变量');
|
|
|
|
|
|
console.error(' 请设置端口后启动,例如:');
|
2026-02-02 18:27:48 +08:00
|
|
|
|
console.error(' PORT=30006 pnpm start');
|
2026-02-02 18:16:15 +08:00
|
|
|
|
console.error(' 或:');
|
2026-02-02 18:27:48 +08:00
|
|
|
|
console.error(' export PORT=30006 && pnpm start');
|
2026-02-02 18:16:15 +08:00
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`🌐 启动服务器: http://localhost:${port}`);
|
|
|
|
|
|
console.log('');
|
|
|
|
|
|
|
|
|
|
|
|
const server = spawn('node', [serverPath], {
|
|
|
|
|
|
stdio: 'inherit',
|
|
|
|
|
|
env: { ...process.env, PORT: port }
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
server.on('error', (err) => {
|
|
|
|
|
|
console.error('❌ 启动失败:', err);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
server.on('exit', (code) => {
|
|
|
|
|
|
if (code !== 0) {
|
|
|
|
|
|
console.error(`\n❌ 服务器异常退出,退出码: ${code}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
process.exit(code);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 处理 Ctrl+C
|
|
|
|
|
|
process.on('SIGINT', () => {
|
|
|
|
|
|
console.log('\n\n👋 停止服务器...');
|
|
|
|
|
|
server.kill('SIGINT');
|
|
|
|
|
|
});
|