mirror of https://github.com/djteang/OrangeTV.git
179 lines
4.8 KiB
JavaScript
179 lines
4.8 KiB
JavaScript
/**
|
||
* 生产模式下的服务器入口
|
||
* 使用 NODE_ENV=production node production.js 来启动
|
||
*/
|
||
process.env.NODE_ENV = 'production';
|
||
|
||
const { createServer } = require('http');
|
||
const { parse } = require('url');
|
||
const next = require('next');
|
||
const path = require('path');
|
||
const http = require('http');
|
||
const { createWebSocketServer } = require('./websocket');
|
||
|
||
// 调用 generate-manifest.js 生成 manifest.json
|
||
function generateManifest() {
|
||
console.log('Generating manifest.json for Docker deployment...');
|
||
|
||
try {
|
||
const generateManifestScript = path.join(
|
||
__dirname,
|
||
'scripts',
|
||
'generate-manifest.js'
|
||
);
|
||
require(generateManifestScript);
|
||
} catch (error) {
|
||
console.error('❌ Error calling generate-manifest.js:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 生成manifest
|
||
generateManifest();
|
||
|
||
const hostname = process.env.HOSTNAME || '0.0.0.0';
|
||
const port = process.env.PORT || 3000;
|
||
|
||
// 在生产模式下初始化 Next.js
|
||
const app = next({
|
||
dev: false,
|
||
hostname,
|
||
port
|
||
});
|
||
|
||
const handle = app.getRequestHandler();
|
||
|
||
app.prepare().then(() => {
|
||
const server = createServer(async (req, res) => {
|
||
try {
|
||
// 检查是否是WebSocket升级请求,如果是则跳过Next.js处理
|
||
const upgrade = req.headers.upgrade;
|
||
if (upgrade && upgrade.toLowerCase() === 'websocket') {
|
||
// 不处理WebSocket升级请求,让upgrade事件处理器处理
|
||
return;
|
||
}
|
||
|
||
// 使用Next.js处理所有非WebSocket请求
|
||
const parsedUrl = parse(req.url, true);
|
||
await handle(req, res, parsedUrl);
|
||
} catch (err) {
|
||
console.error('处理请求时出错:', req.url, err);
|
||
res.statusCode = 500;
|
||
res.end('内部服务器错误');
|
||
}
|
||
});
|
||
|
||
// 初始化 WebSocket 服务器
|
||
const wss = createWebSocketServer();
|
||
|
||
// 将 WebSocket 服务器实例存储到全局对象中,供 API 路由使用
|
||
global.wss = wss;
|
||
|
||
// 使用WeakSet来跟踪已处理的socket,避免重复处理
|
||
const handledSockets = new WeakSet();
|
||
|
||
// 处理 WebSocket 升级请求
|
||
server.on('upgrade', (request, socket, head) => {
|
||
// 如果socket已经被处理过,直接返回
|
||
if (handledSockets.has(socket)) {
|
||
return;
|
||
}
|
||
|
||
const pathname = parse(request.url).pathname;
|
||
|
||
if (pathname === '/ws') {
|
||
console.log('处理 WebSocket 升级请求:', pathname);
|
||
|
||
// 标记socket已被处理
|
||
handledSockets.add(socket);
|
||
|
||
// 处理WebSocket连接
|
||
try {
|
||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||
wss.emit('connection', ws, request);
|
||
});
|
||
} catch (error) {
|
||
console.error('WebSocket升级错误:', error);
|
||
socket.destroy();
|
||
}
|
||
} else {
|
||
console.log('未知的升级请求路径:', pathname);
|
||
// 不销毁socket,让它自然关闭
|
||
}
|
||
});
|
||
|
||
// 启动服务器
|
||
server.listen(port, (err) => {
|
||
if (err) throw err;
|
||
console.log(`> 服务已启动 (生产模式): http://${hostname}:${port}`);
|
||
console.log(`> WebSocket 服务已启动: ws://${hostname}:${port}/ws`);
|
||
|
||
// 设置服务器启动后的任务
|
||
setupServerTasks();
|
||
});
|
||
});
|
||
|
||
// 设置服务器启动后的任务
|
||
function setupServerTasks() {
|
||
// 每 1 秒轮询一次,直到请求成功
|
||
const TARGET_URL = `http://${process.env.HOSTNAME || 'localhost'}:${process.env.PORT || 3000}/login`;
|
||
|
||
const intervalId = setInterval(() => {
|
||
console.log(`Fetching ${TARGET_URL} ...`);
|
||
|
||
const req = http.get(TARGET_URL, (res) => {
|
||
// 当返回 2xx 状态码时认为成功,然后停止轮询
|
||
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||
console.log('Server is up, stop polling.');
|
||
clearInterval(intervalId);
|
||
|
||
setTimeout(() => {
|
||
// 服务器启动后,立即执行一次 cron 任务
|
||
executeCronJob();
|
||
}, 3000);
|
||
|
||
// 然后设置每小时执行一次 cron 任务
|
||
setInterval(() => {
|
||
executeCronJob();
|
||
}, 60 * 60 * 1000); // 每小时执行一次
|
||
}
|
||
});
|
||
|
||
req.setTimeout(2000, () => {
|
||
req.destroy();
|
||
});
|
||
}, 1000);
|
||
}
|
||
|
||
// 执行 cron 任务的函数
|
||
function executeCronJob() {
|
||
const cronUrl = `http://${process.env.HOSTNAME || 'localhost'}:${process.env.PORT || 3000}/api/cron`;
|
||
|
||
console.log(`Executing cron job: ${cronUrl}`);
|
||
|
||
const req = http.get(cronUrl, (res) => {
|
||
let data = '';
|
||
|
||
res.on('data', (chunk) => {
|
||
data += chunk;
|
||
});
|
||
|
||
res.on('end', () => {
|
||
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
||
console.log('Cron job executed successfully:', data);
|
||
} else {
|
||
console.error('Cron job failed:', res.statusCode, data);
|
||
}
|
||
});
|
||
});
|
||
|
||
req.on('error', (err) => {
|
||
console.error('Error executing cron job:', err);
|
||
});
|
||
|
||
req.setTimeout(30000, () => {
|
||
console.error('Cron job timeout');
|
||
req.destroy();
|
||
});
|
||
}
|