400 lines
12 KiB
JavaScript
400 lines
12 KiB
JavaScript
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)
|
||
|
||
} |