Files
lyroc/electron-app/modules/backend-service.js

220 lines
6.5 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.
/**
* 后端服务管理模块
* 负责启动、管理和监控Deno后端服务
*/
const { spawn } = require("child_process");
const path = require("path");
const fs = require("fs");
const findProcess = require("find-process");
const getPort = require("get-port");
// 存储Deno进程引用
let denoProcess = null;
let backendPort = 5005;
let frontendPort = 5173;
/**
* 获取可用的后端端口
* @returns {Promise<number>} 可用端口号
*/
async function checkPort() {
try {
// 获取可用端口首选5005
backendPort = await getPort({ port: 5005 });
console.log(`后端将使用端口: ${backendPort}`);
return backendPort;
} catch (err) {
console.error("获取可用端口失败:", err);
throw err;
}
}
/**
* 获取可用的前端端口
* @returns {Promise<number>} 可用端口号
*/
async function getFrontendPort() {
try {
// 获取完全随机的可用端口,不指定首选端口
frontendPort = await getPort();
console.log(`前端将使用随机端口: ${frontendPort}`);
return frontendPort;
} catch (err) {
console.error("获取前端端口失败:", err);
throw err;
}
}
/**
* 清理已存在的Deno进程
* @returns {Promise<void>}
*/
async function cleanupExistingProcesses() {
try {
const processList = await findProcess("port", 5005);
for (const proc of processList) {
if (proc.name.includes("deno")) {
console.log(`杀死已存在的Deno进程: PID ${proc.pid}`);
process.kill(proc.pid, "SIGKILL");
}
}
} catch (err) {
console.error("清理进程失败:", err);
}
}
/**
* 启动Deno后端服务
* @param {boolean} isDev 是否为开发模式
* @returns {Promise<void>}
*/
async function startDenoBackend(isDev) {
try {
// 在开发模式和生产模式下使用不同的路径
let scriptPath;
if (isDev) {
// 开发模式使用项目中的Deno后端
scriptPath = path.join(__dirname, "..", "..", "backend-deno", "dist", "backend-deno");
} else {
// 生产模式使用打包的Deno后端
scriptPath = path.join(process.resourcesPath, "backend", "backend-deno");
}
console.log(`启动Deno后端: ${scriptPath} 在端口 ${backendPort}`);
console.log(`后端脚本路径存在: ${fs.existsSync(scriptPath)}`);
// 打印Deno脚本目录内容
try {
const scriptDir = path.dirname(scriptPath);
console.log(`后端目录内容: ${fs.readdirSync(scriptDir).join(", ")}`);
} catch (err) {
console.error(`无法读取后端目录: ${err}`);
}
// 启动子进程
denoProcess = spawn(scriptPath,
[],
{
env: { ...process.env, PORT: backendPort.toString(), USER_DATA_DIR: require("electron").app.getPath("userData"), },
stdio: "pipe", // 确保可以读取标准输出和错误
}
);
denoProcess.stderr.on("data", (data) => {
const message = data.toString().trim();
// 判断是否是错误日志还是普通日志
if (
message.includes("ERROR") ||
message.includes("CRITICAL") ||
message.includes("WARN")
) {
console.error(`Deno后端错误: ${message}`);
} else {
console.log(`Deno后端日志: ${message}`);
}
});
denoProcess.on("close", (code) => {
console.log(`Deno后端退出退出码: ${code}`);
// 如果应用仍在运行,尝试重启后端
if (global.appReady && code !== 0) {
console.log("尝试重启Deno后端...");
setTimeout(() => startDenoBackend(isDev), 1000);
}
});
// 等待后端启动 - 改为检测后端是否真正启动完成而非固定等待时间
return new Promise((resolve, reject) => {
// 后端启动超时时间,开发模式较短,生产模式较长
const maxTimeout = isDev ? 15000 : 30000;
const startTime = Date.now();
let backendReady = false;
// 添加额外的日志解析以检测后端就绪状态
denoProcess.stdout.on("data", (data) => {
const output = data.toString().trim();
console.log(`Deno后端输出: ${output}`);
// 检测后端就绪信号Deno输出"启动后端服务器在端口"表示应用已启动)
if (
output.includes("启动后端服务器在端口") ||
output.includes("Server running")
) {
console.log("检测到后端应用启动完成信号");
backendReady = true;
// 再等待短暂时间确保所有服务都初始化完成
setTimeout(() => {
console.log("后端已完全启动,准备创建前端窗口");
resolve();
}, 500);
}
});
// 实现HTTP测试以检测后端是否正常响应
const testBackendConnection = () => {
const http = require("http");
const testUrl = `http://127.0.0.1:${backendPort}/`;
// 如果已经检测到后端就绪,不再继续测试
if (backendReady) return;
// 检查是否超时
if (Date.now() - startTime > maxTimeout) {
console.warn(`后端启动超时(${maxTimeout}ms),将继续尝试启动前端`);
resolve();
return;
}
http
.get(testUrl, (res) => {
if (res.statusCode === 200 || res.statusCode === 404) {
// 404也表示服务器在运行只是路径不存在
console.log("通过HTTP检测确认后端已启动");
backendReady = true;
resolve();
} else {
console.log(`后端响应状态码: ${res.statusCode},继续等待...`);
// 短时间后再次测试
setTimeout(testBackendConnection, 1000);
}
})
.on("error", (err) => {
console.log(`后端连接测试失败: ${err.message}, 继续等待...`);
// 短时间后再次测试
setTimeout(testBackendConnection, 1000);
});
};
// 启动后端连接测试
setTimeout(testBackendConnection, 1000);
});
} catch (err) {
console.error("启动Python后端失败:", err);
throw err;
}
}
/**
* 停止Deno后端
*/
function stopDenoBackend() {
if (denoProcess) {
console.log("终止Deno后端进程...");
denoProcess.kill();
denoProcess = null;
}
}
module.exports = {
checkPort,
getFrontendPort,
cleanupExistingProcesses,
startDenoBackend,
stopDenoBackend,
getBackendPort: () => backendPort,
getFrontendPortNumber: () => frontendPort,
getDenoProcess: () => denoProcess,
};