3.1.0稳定版
This commit is contained in:
120
main.js
120
main.js
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user