const { app, BrowserWindow, Tray, Menu } = require('electron'); const path = require('path'); const serverManager = require('./serverManager'); const menuManager = require('./menuManager'); const updateManager = require('./updateManager'); const constants = require('./constants'); const { getNetworkInterfaces } = require('./networkService'); const { exec } = require('child_process'); const net = require('net'); const os = require('os'); const utils = require('./utils'); let win = null; let tray = null; app.isQuitting = false; // 添加标志位 // 根据操作系统选择命令 const IS_WINDOWS = os.platform() === 'win32'; const FIND_PORT_COMMAND = IS_WINDOWS ? `netstat -ano | findstr :${constants.port}` : `lsof -i :${constants.port} -t`; const KILL_PROCESS_COMMAND = IS_WINDOWS ? `taskkill /PID {PID} /F` : `kill -9 {PID}`; // 创建 Electron 窗口的函数 const createWindow = async () => { try { win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, contextIsolation: false, }, }); win.webContents.openDevTools(); // 打开开发者工具 //win.webContents.session.setCertificateVerifyProc((request, callback) => { // // 始终返回 0 表示验证通过 // callback(0); //});///login/admin win.maximize(); win.loadFile('index.html'); // 模拟执行业务过程 StartProcess(); //win.loadURL(`${constants.baseUrl}/login/admin`, { // agent: constants.agent //}); // 监听窗口关闭事件,隐藏窗口而不是关闭 win.on('close', (event) => { if (!app.isQuitting) { event.preventDefault(); win.hide(); } }); } catch (error) { console.error('Error starting server or loading window:', error); } }; // 当 Electron 应用准备好时创建窗口 app.whenReady().then(() => { process.env.NODE_OPTIONS = '--tls-min-v1.2'; createWindow(); createTray(); // 创建菜单并传递回调函数 menuManager.createMenu(checkForUpdatesHandler); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); // 监听 before-quit 事件,关闭 IES.ExamServer.exe 进程 app.on('before-quit', async (event) => { if (app.isQuitting) { return; // 如果已经在退出流程中,则直接返回 } app.isQuitting = true; // 标记正在退出 event.preventDefault(); // 阻止默认的退出行为 await serverManager.shutdownServer(); // 关闭服务器 app.quit(); // 触发退出流程 }); }); // 当所有窗口关闭时退出应用(macOS 除外) app.on('window-all-closed', function () { if (process.platform !== 'darwin') { app.quit(); } }); // 模拟执行业务过程 async function StartProcess() { //步骤1 开始检查 sendLogMessage('检查评测服务是否启动...'); //步骤2 检查端口是否占用。 let serverStarted = false; try { const inUse = await isPortInUse(constants.port); if (inUse) { serverStarted = true; sendLogMessage(`检查到端口被占用...`); } else { sendLogMessage(`评测服务未启动...`); } } catch (err) { sendLogMessage(`检查端口时出错...`); // console.log(`检查评测服务状态出错...`, err); } //步骤3,尝试优雅关闭,如果不能关闭再通过 关闭进程号关闭端口 let needStart = false; if (serverStarted) { //检测是否是.net6的服务在线 sendLogMessage('检查是否是评测服务占用端口...'); const isServerRunning = await serverManager.checkServerHealth(); if (!isServerRunning) { //可能是其他程序占用 sendLogMessage('检测到其他程序占用端口...'); sendLogMessage('正在查找占用端口的进程...'); const pid = await findProcessUsingPort(); sendLogMessage(`找到占用端口的进程: ${pid}`); sendLogMessage('正在关闭进程...'); const killResult = await killProcess(pid); sendLogMessage(`进程关闭结果:${killResult}`); utils.delay(500); needStart = true; } else { sendLogMessage('评测服务正在运行中...'); sendLogMessage('如需强制退出,请点击<设置><退出>按钮...'); } } else { needStart = true; } if (needStart) { sendLogMessage('正在启动评测服务...'); await serverManager.startServer(); const isServerRunning = await serverManager.checkServerHealth(); if (isServerRunning) { sendLogMessage('本地IP域名映射配置成功...'); sendLogMessage('SSL安全证书安装成功...'); sendLogMessage('评测服务启动成功...'); sendLogMessage('正在加载登录页面...'); utils.delay(2000) win.loadURL(`${constants.baseUrl}/login/admin`, { agent: constants.agent }); } else { sendLogMessage('评测服务启动失败...'); sendLogMessage('请检查hosts文件是否自动映射了exam.habook.local域名...'); sendLogMessage("如果没有请手动执行脚本文件..."); } } else { sendLogMessage('正在加载登录页面...'); utils.delay(2000) win.loadURL(`${constants.baseUrl}/login/admin`, { agent: constants.agent }); } } // 发送消息到渲染进程 function sendLogMessage(message) { if (win) { win.webContents.send('log-message', message); } } const createTray = () => { const iconPath = path.join(__dirname, 'logo.ico'); // 你的托盘图标路径 tray = new Tray(iconPath); const contextMenu = Menu.buildFromTemplate([ { label: '显示', click: () => { if (win) { win.show(); } } }, { label: '退出', click: () => { require('electron').app.quit(); //app.isQuitting = true; //app.quit(); } } ]); tray.setToolTip('评测教师端'); tray.setContextMenu(contextMenu); // 监听双击托盘图标事件,恢复窗口 tray.on('double-click', () => { if (win) { if (win.isVisible()) { win.focus(); // 如果窗口已经显示,则聚焦窗口 } else { win.show(); // 如果窗口隐藏,则显示窗口 } } }); }; // 定义回调函数 const checkForUpdatesHandler = () => { updateManager.checkForUpdates(win, () => { menuManager.createMenu(checkForUpdatesHandler); // 重新创建菜单并传递回调函数 }); }; function isPortInUse(port) { return new Promise((resolve, reject) => { const server = net.createServer() .once('error', (err) => { if (err.code === 'EADDRINUSE') { resolve(true); // 端口被占用 } else { reject(err); } }) .once('listening', () => { server.close(); resolve(false); // 端口未被占用 }) .listen(port); }); } // 查找占用端口的进程 function findProcessUsingPort() { return new Promise((resolve, reject) => { exec(FIND_PORT_COMMAND, (error, stdout, stderr) => { if (error) { if (error.code === 1) { // 未找到占用端口的进程 resolve(null); } else { reject(`查找端口占用失败: ${stderr}`); } return; } // 提取 PID const pid = IS_WINDOWS ? stdout.trim().split(/\s+/).pop() // Windows: 取最后一列 : stdout.trim(); // macOS/Linux: 直接输出 PID resolve(pid); }); }); } // 关闭进程 function killProcess(pid) { return new Promise((resolve, reject) => { if (!pid) { resolve('未找到占用端口的进程'); return; } const command = KILL_PROCESS_COMMAND.replace('{PID}', pid); exec(command, (error, stdout, stderr) => { if (error) { reject(`关闭进程失败: ${stderr}`); return; } resolve(`已关闭进程: ${pid}`); }); }); } //// 启动 .NET Core 应用程序 //function startDotNetApp() { // return new Promise((resolve, reject) => { // const command = `dotnet run --urls=http://localhost:${PORT}`; // const options = { cwd: DOTNET_PROJECT_PATH }; // 设置工作目录为 .NET 项目路径 // exec(command, options, (error, stdout, stderr) => { // if (error) { // reject(`启动 .NET Core 应用程序失败: ${stderr}`); // return; // } // resolve(`.NET Core 应用程序已启动: ${stdout}`); // }); // }); //}