import { app, shell, BrowserWindow, ipcMain, Menu, session, screen, dialog } from 'electron' import { electronApp, optimizer } from '@electron-toolkit/utils' import Store from 'electron-store' import { createWindow, createDrageWindow, unregisterAllShortcuts, getMainWindow } from './window.js' import { setupIPC } from './ipc.js' import { createTray, destroyTray,clearBrowserCache } from './tray.js' import XEUtils from 'xe-utils' import fs from 'fs' import path from 'path' import dayjs from 'dayjs' import logger from './utils/logger' import { clearAllSessionData } from './utils/cacheUtils.js' import SingleInstanceManager from './utils/singleInstance.js' import { setStoreValue, getStoreValue ,deleteStore} from './store.js' import AutoLaunch from 'auto-launch' import axios from 'axios' import WebSocketClient from './utils/WebSocketClient'; let wsClient=null // 延迟创建WebSocket连接,等待应用完全启动后再连接 function createWebSocketClient() { try { wsClient = new WebSocketClient({ autoReconnect: true, autoReconnectAttempts: 9999, autoReconnectInterval: 5000, timeout: 30000, }); wsClient.on('open', () => { logger.info('WebSocket连接已打开') }); wsClient.on('error', (error) => { logger.error('WebSocket连接错误:', error) }); wsClient.on('close', (data) => { logger.info('WebSocket连接已关闭:', data) }); } catch (error) { logger.error('创建WebSocket客户端失败:', error) } } var minecraftAutoLauncher = new AutoLaunch({ name: '百千万AI智能体共创平台', path: app.getPath('exe') }); app.commandLine.appendSwitch('allow-insecure-localhost'); // 允许本地回环地址使用不安全连接 app.commandLine.appendSwitch('ignore-certificate-errors'); // 忽略证书错误(开发时可用) const h5_client_url=getStoreValue("h5_client_url") if(!XEUtils.isEmpty(h5_client_url)){ logger.info("=======================h5_client_url 非空,设置 unsafely-treat-insecure-origin-as-secure的值为:"+h5_client_url) app.commandLine.appendSwitch('unsafely-treat-insecure-origin-as-secure', h5_client_url); } let difySite=getStoreValue("difySite") if(!XEUtils.isEmpty(difySite)){ difySite = XEUtils.parseUrl(difySite) logger.info("=======================h5_client_url 非空,设置 unsafely-treat-insecure-origin-as-secure的值为:"+h5_client_url+","+difySite.origin) app.commandLine.appendSwitch('unsafely-treat-insecure-origin-as-secure', h5_client_url+","+difySite.origin); } // 设置控制台编码为 UTF-8 logger.info(`当前运行平台: ${process.platform}`) if (process.platform === 'win32') { logger.info('Windows 系统,设置控制台编码为 UTF-8') try { require('child_process').execSync('chcp 65001', { stdio: 'ignore' }) } catch (error) { logger.error('设置控制台编码失败:', error) } } else { logger.info('非 Windows 系统,使用默认编码') } logger.info('%cRed text. %cGreen text', 'color: red', 'color: green') try { const store = new Store() logger.info('Store 初始化成功') } catch (error) { logger.error('Store 初始化失败:', error) } app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors') app.commandLine.appendSwitch('ignore-certificate-errors') // 添加缓存相关配置,避免缓存错误 app.commandLine.appendSwitch('disable-http-cache') app.commandLine.appendSwitch('disable-background-timer-throttling') app.commandLine.appendSwitch('disable-renderer-backgrounding') try { app.disableHardwareAcceleration() logger.info('硬件加速已禁用') } catch (error) { logger.error('禁用硬件加速失败:', error) } logger.info(app.getPath('userData')) logger.info('应用启动,日志文件路径:', logger.getLogPath()) // 添加更多启动信息 logger.info('开始初始化应用...') // 清理缓存目录 function cleanupCacheDirectories() { try { const userDataPath = app.getPath('userData') const cachePaths = [ path.join(userDataPath, 'Cache'), path.join(userDataPath, 'Code Cache'), path.join(userDataPath, 'GPUCache'), path.join(userDataPath, 'Service Worker'), path.join(userDataPath, 'Session Storage') ] for (const cachePath of cachePaths) { if (fs.existsSync(cachePath)) { logger.info(`清理缓存目录: ${cachePath}`) try { // 递归删除目录 const deleteRecursive = (dirPath) => { if (fs.existsSync(dirPath)) { const files = fs.readdirSync(dirPath) for (const file of files) { const curPath = path.join(dirPath, file) if (fs.lstatSync(curPath).isDirectory()) { deleteRecursive(curPath) } else { try { fs.unlinkSync(curPath) } catch (error) { logger.warn(`无法删除缓存文件: ${curPath}`, error.message) } } } try { fs.rmdirSync(dirPath) } catch (error) { logger.warn(`无法删除缓存目录: ${dirPath}`, error.message) } } } deleteRecursive(cachePath) logger.info(`缓存目录清理完成: ${cachePath}`) } catch (error) { logger.error(`清理缓存目录失败: ${cachePath}`, error) } } } } catch (error) { logger.error('清理缓存目录失败:', error) } } /** * 单应用启动实现 * 请求单一实例锁, * 如果该方法返回`false`, * 则表示已经有一个实例在运行, * 可以通过`app.quit()`方法退出当前实例。 */ const gotTheLock = app.requestSingleInstanceLock() if (!gotTheLock) { logger.info('检测到已有实例运行,退出当前实例') app.quit() } else { logger.info('这是第一个实例,继续启动') // 监听第二个实例被运行时 app.on('second-instance', (event, commandLine, workingDirectory) => { logger.info('检测到第二个实例启动,激活现有实例') // 当有第二个实例被运行时,激活之前的实例并将焦点置于其窗口 const windows = BrowserWindow.getAllWindows() if (windows.length > 0) { const mainWindow = windows[0] if (mainWindow.isMinimized()) { mainWindow.restore() } mainWindow.show() mainWindow.focus() } }) app.whenReady().then(() => { logger.info('应用已准备就绪,开始初始化...'); // 清理缓存目录 cleanupCacheDirectories() // 获取默认会话并全局设置允许第三方 Cookie const defaultSession = session.defaultSession; // 设置 Cookie 策略以允许第三方 Cookie defaultSession.setPermissionRequestHandler((webContents, permission, callback) => { callback(true); }); electronApp.setAppUserModelId('com.huashiai.dify-market-manager-gui') app.on('browser-window-created', (_, window) => { optimizer.watchWindowShortcuts(window) }) // 根据存储的状态决定是否创建悬浮窗口 if (getStoreValue('showDrageWindow')) { createDrageWindow() } createWindow() createTray() setupIPC() // exe退出三秒之后即认为关闭过exe程序,退出登录 checkIsKeepAlive() // 延迟创建WebSocket连接 setTimeout(() => { createWebSocketClient() }, 2000); // 延迟2秒创建WebSocket连接 minecraftAutoLauncher.isEnabled() .then(function(isEnabled){ if(isEnabled){ return; } minecraftAutoLauncher.enable(); }) .catch(function(err){ logger.info(err) }); app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) logger.info('应用初始化完成'); }) // 修改窗口关闭行为 app.on('window-all-closed', async (event) => { if (process.platform !== 'darwin') { event.preventDefault(); // 如果是重启过程,不执行任何清理操作 if (app.isRestarting) { logger.info('检测到重启过程,跳过窗口关闭处理') return; } if (!app.isQuiting) { logger.info('用户关闭窗口,隐藏应用') // 如果不是主动退出,则隐藏所有窗口 BrowserWindow.getAllWindows().forEach(window => { window.hide() }) // 清除所有会话数据 try { await clearAllSessionData('window-all-closed'); } catch (error) { logger.error('窗口关闭时清理缓存失败:', error); } // 销毁托盘并退出应用 destroyTray() app.quit(); } else { logger.info('主动退出,完全关闭应用') // 如果是主动退出,则销毁托盘并退出应用 destroyTray() // 使用 exit 确保完全退出 app.exit(0) } } }) app.on('before-quit', async (event) => { // 如果是重启过程,不执行任何清理操作 if (app.isRestarting) { logger.info('检测到重启过程,跳过before-quit处理') return; } logger.info('应用即将退出,开始清理资源') // 在应用程序即将退出时执行操作,例如保存数据 event.preventDefault(); // 清除所有会话数据 try { await clearAllSessionData('before-quit'); } catch (error) { logger.error('应用退出时清理缓存失败:', error); } // 销毁托盘并退出应用 destroyTray() // 使用 exit 确保完全退出 app.exit(0); }); // 在应用退出时注销所有快捷键 app.on('will-quit', async (event) => { // 如果是重启过程,不执行任何清理操作 if (app.isRestarting) { logger.info('检测到重启过程,跳过will-quit处理') return; } logger.info('应用即将退出,注销快捷键') event.preventDefault(); // 清除所有会话数据 try { await clearAllSessionData('will-quit'); } catch (error) { logger.error('应用退出时清理缓存失败:', error); } unregisterAllShortcuts() destroyTray() // 清理完成后退出应用,使用 exit 确保完全退出 app.exit(0); }) // 监听进程退出信号,确保在系统强制关闭时也能清理缓存 process.on('SIGTERM', async () => { logger.info('收到 SIGTERM 信号,开始清理缓存'); try { await clearAllSessionData('SIGTERM'); } catch (error) { logger.error('SIGTERM 时清理缓存失败:', error); } destroyTray() process.exit(0); }); process.on('SIGINT', async () => { logger.info('收到 SIGINT 信号,开始清理缓存'); try { await clearAllSessionData('SIGINT'); } catch (error) { logger.error('SIGINT 时清理缓存失败:', error); } destroyTray() process.exit(0); }); // 监听未捕获的异常,确保应用崩溃时也能记录日志 process.on('uncaughtException', (error) => { logger.error('未捕获的异常:', error); // 不要立即退出,给应用一个恢复的机会 logger.error('应用遇到未捕获的异常,但将继续运行'); }); process.on('unhandledRejection', (reason, promise) => { logger.error('未处理的 Promise 拒绝:', reason); // 不要立即退出,给应用一个恢复的机会 logger.error('应用遇到未处理的 Promise 拒绝,但将继续运行'); }); } export function checkIsKeepAlive(){ const checkIsKeepAliveTimer=setInterval(async () => { const lastAliveTime = getStoreValue("lastAliveTime")||null if (lastAliveTime!=null){ const nowTime=dayjs(); const lastTime=dayjs(lastAliveTime); const diff=nowTime.diff(lastTime, 'second') // 上次在线事件在三秒之前,则认为关闭过exe程序 if (diff>5){ logger.info('上次在线事件在三秒之前,则认为关闭过exe程序') await clearAllSessionData('超时5秒'); deleteStore("lastAliveTime") }else{ setStoreValue("lastAliveTime",dayjs()) } } }, 1000 * 2) }