3.1.0稳定版

This commit is contained in:
2025-12-01 20:04:52 +08:00
parent f7e1cad639
commit 55542dd212
3 changed files with 107 additions and 40 deletions

120
main.js
View File

@@ -17,8 +17,20 @@ Object.assign(console, log.functions)
// 存储 SSE 服务器的引用
let sseServer = null;
// ✅ 本地 HTTPS 白名单(按你的实际地址改端口)
const LOCAL_HTTPS_WHITELIST = [
'https://192.168.1.218:34567',
'https://192.168.1.231:34567',
'https://127.0.0.1:34567',
'https://localhost:34567'
]
let userData = { tenantId: null, userId: null }
const { exec, spawn, execFile } = require('child_process'); // 如果你用 exec 启动 scrcpy保留此行
// app.commandLine.appendSwitch('remote-debugging-port', '9222'); //远程控制台端口F12
app.commandLine.appendSwitch('enable-precise-memory-info')
app.commandLine.appendSwitch('js-flags', '--expose-gc --max-old-space-size=4096') // 以 MB 为单位,建议设置 2048-4096
@@ -45,7 +57,7 @@ const SAFE_MODE =
// (可选)允许命令行清除持久化标记:--clear-safe-mode
if (process.argv.includes('--clear-safe-mode')) {
const c = readCompat(); delete c.safeMode; writeCompat(c);
console.log('[Compat] cleared safeMode flag');
console.log('[兼容性] 已清除安全模式标志');
}
// === 兼容模式等价于 bat 里的那些开关(不含日志) ===
@@ -56,7 +68,7 @@ if (SAFE_MODE) {
app.commandLine.appendSwitch('disable-direct-composition'); // --disable-direct-composition
// 如需更稳再关沙箱:默认开着更安全;若你确实要关,则解除注释
// app.commandLine.appendSwitch('no-sandbox'); // --no-sandbox
console.log('[SAFE_MODE] enabled');
console.log('[安全模式] 已启用');
}
@@ -66,13 +78,13 @@ if (SAFE_MODE) {
function flushPendingNavs() {
const fns = pendingNavs.slice();
pendingNavs = [];
for (const fn of fns) { try { fn(); } catch (e) { console.warn('[Nav] deferred run err:', e); } }
for (const fn of fns) { try { fn(); } catch (e) { console.warn('[导航] 延迟运行错误:', e); } }
}
function safeLoadURL(win, url, onFail) {
if (!win || win.isDestroyed()) return;
win.loadURL(url).catch(err => {
console.warn('[loadURL 失败]', err);
console.warn('[加载url 失败]', err);
onFail?.(err);
});
}
@@ -90,7 +102,8 @@ app.on('web-contents-created', (_, contents) => {
console.error('⚠️ 渲染崩溃:', details);
if (['launch-failed', 'abnorm al-exit'].includes(details.reason)) {
const c = readCompat(); c.safeMode = true; writeCompat(c);
console.warn('[Compat] set safeMode=true (persisted), will auto-use SAFE_MODE next launch');
console.warn('[兼容性] 已设置安全模式为true已持久化下次启动时将自动使用安全模式');
}
});
});
@@ -99,10 +112,23 @@ app.on('child-process-gone', (_e, details) => {
if ((details.type === 'GPU' || details.type === 'Renderer') &&
['launch-failed', 'abnormal-exit'].includes(details.reason)) {
const c = readCompat(); c.safeMode = true; writeCompat(c);
console.warn('[Compat] GPU/Renderer gone, safeMode persisted');
console.warn('[兼容性] 已设置安全模式为true已持久化下次启动时将自动使用安全模式');
}
});
//本地https 证书跳过校验
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
// console.warn('[certificate-error]', url, error)
// 只放行你自己的本地 HTTPS
if (LOCAL_HTTPS_WHITELIST.some(prefix => url.startsWith(prefix))) {
event.preventDefault() // 告诉 Electron我来处理这个错误
callback(true) // ✅ 忽略证书错误,照样加载
} else {
// 其他地址还是正常校验证书,防止误放行
callback(false)
}
})
app.on('ready', () => {
@@ -179,7 +205,8 @@ app.on('ready', () => {
// 安装前做一次业务清理:但避免误杀自身或阻塞
await killIOSAI();
} catch (e) {
console.warn('[update] pre-install cleanup warning:', e);
console.warn('[更新] 预安装清理警告:', e);
}
// electron-updater 实际上传给 NSIS /S 静默参数,第二个 true 表示强制重启后运行新版本
autoUpdater.quitAndInstall(false, true);
@@ -207,7 +234,8 @@ app.whenReady().then(() => {
// globalShortcut.register('F12', () => toggleDevtools(BrowserWindow.getFocusedWindow()));
globalShortcut.register('Control+Shift+I', () => toggleDevtools(BrowserWindow.getFocusedWindow()));
globalShortcut.register('Control+Shift+i', () => toggleDevtools(BrowserWindow.getFocusedWindow()));
// globalShortcut.register('Control+Alt+m', () => toggleDevtools(BrowserWindow.getFocusedWindow()));
});
app.on('will-quit', () => globalShortcut.unregisterAll());
@@ -217,7 +245,8 @@ app.on('before-quit', async () => {
if (!installingNow) {
await killIOSAI();
} else {
console.log('[before-quit] skip killIOSAI because installingNow = true');
console.log('[退出前] 跳过 killIOSAI 因为 installingNow = true');
}
});
function toggleDevtools(win) {
@@ -273,33 +302,56 @@ function dumpAllMem() {
// console.log('> [MEM] SUM Renderer workingSetMB=', sum(renderers, 'workingSetMB'));
// if (gpu.length) console.log('> [MEM] GPU=', gpu);
} catch (e) {
console.warn('getAppMetrics error:', e);
console.warn('获取应用指标错误:', e);
}
}
// 在函数外定义计数器(或者放在函数内部,用闭包封装)
let consumeCount = 0;
async function setupMQConsumerAndPublisher(emitMessage, tenantId) {
// 启动持续消费:来一条 -> 立刻 SSE 广播
await mq.startConsumer(
const queueNames = [
`q.tenant.${tenantId}`,
async (msg) => {
const payload = msg.json ?? msg.text; // 数据
consumeCount++; // 每消费一条消息就加 1
`b.tenant.${tenantId}`, // 新增队列
];
console.log('消费到:', payload.hostsId, payload.country, '共' + consumeCount + '条数据');
for (const qName of queueNames) {
await mq.startConsumer(
qName,
async (msg) => {
const payload = msg.json ?? msg.text; // 原始业务数据
consumeCount++; // 所有队列共用计数器
// 广播到前端(事件名自定义为 'mq'
emitMessage(payload);
// 成功返回会在 mq 客户端内部自动 ack
},
{ prefetch: 1, requeueOnError: false, durable: true, assertQueue: true }
);
// 标记来源类型:普通队列 / burst 队列
const isBurstQueue = qName.startsWith('b.');
const meta = isBurstQueue ? 2 : 1;
// 供渲染进程发送消息到队列
console.log(
`[MQ消费] [${qName}]`,
payload?.hostsId,
payload?.country,
'共' + consumeCount + '条数据'
);
// ⚠️ 关键:在原有 payload 的基础上,增加 _mqMeta 字段
// 这样你之前前端用 payload.hostsId / payload.country 的地方完全不用改
const wrapped = {
...payload,
_mqMeta: meta
};
// 广播到前端
emitMessage(wrapped);
// 成功返回会在 mq 客户端内部自动 ack
},
{ prefetch: 1, requeueOnError: false, durable: true, assertQueue: true }
);
}
// 供渲染进程发送消息到队列(保持原来的 q.tenant.* 不变)
ipcMain.removeHandler('mq-send');
ipcMain.handle('mq-send', async (_event, user) => {
console.log('消息已发送', user)
console.log('消息已发送', user);
await mq.publishToQueue(`q.tenant.${tenantId}`, user);
return { ok: true };
});
@@ -342,10 +394,23 @@ function createWindow() {
win.setMenu(null); // 禁用菜单栏F12
win.maximize();
// ✅ 把渲染进程的 console.* 打进 main 的日志
win.webContents.on('console-message', (event, level, message, line, sourceId) => {
// level: 0=log,1=warn,2=error,3=info,4=debug
if (level == 1) {
console.log(
`[页面日志] ${message}`
);
}
});
// 自动判断环境使用不同的页面地址
const isProd = app.isPackaged;
const targetURL = isProd ? 'https://iosaitest.yolozs.com' : 'http://192.168.1.128:8080';
// const targetURL = isProd ? 'https://iosai.yolozs.com' : 'http://192.168.1.128:8080';
// const targetURL = isProd ? 'https://iosaitest.yolozs.com' : 'http://192.168.1.128:8080';
const targetURL = isProd ? 'https://iosai.yolozs.com' : 'http://192.168.1.128:8080';
console.log('[页面加载] 使用地址:', targetURL);
let retryIntervalId = null;
@@ -724,7 +789,7 @@ async function killIOSAI() {
}).then(response => {
console.log("发送登出请求成功")
}).catch(error => {
console.log("发送登出请求错误", error.message)
console.log("发送登出请求错误", error)
}).finally(error => {
// console.log("发送")
})
@@ -744,6 +809,7 @@ async function killIOSAI() {
exec('taskkill /IM IOSAI.exe /F');
exec('taskkill /IM iproxy.exe /F');
exec('taskkill /IM pythonw.exe /F');
exec('taskkill /IM ios.exe /F');
}
} catch (_) { }
iosAIProcess = null;