Files
dify_market_manager_gui/src/main/index.js
2025-07-18 16:40:49 +08:00

403 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 } 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 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('开始检查单实例锁...')
// 创建单实例管理器
const singleInstanceManager = new SingleInstanceManager()
// 清理缓存目录
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)
}
}
// 检查是否为第一个实例
const isFirstInstance = singleInstanceManager.checkSingleInstance()
if (!isFirstInstance) {
logger.info('检测到已有实例运行,尝试激活现有实例')
// 尝试激活第一个实例的窗口
try {
const windows = BrowserWindow.getAllWindows()
if (windows.length > 0) {
const mainWindow = windows[0]
if (mainWindow.isMinimized()) {
mainWindow.restore()
}
mainWindow.show()
mainWindow.focus()
logger.info('成功激活现有实例窗口')
app.quit() // 退出当前实例
} else {
logger.warn('未找到现有实例窗口,可能是锁未正确释放')
logger.info('强制退出当前实例,避免重复托盘')
app.quit() // 强制退出,避免创建重复托盘
}
} catch (error) {
logger.error('激活现有实例失败:', error)
logger.info('强制退出当前实例,避免重复托盘')
app.quit() // 强制退出,避免创建重复托盘
}
} else {
logger.info('这是第一个实例,继续启动')
// 这是第一个实例
// 监听第二个实例的启动
app.on('second-instance', (event, commandLine, workingDirectory) => {
// 当运行第二个实例时,将显示第一个实例的窗口
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.electron')
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.isQuiting) {
// 如果不是主动退出,则隐藏所有窗口
BrowserWindow.getAllWindows().forEach(window => {
window.hide()
})
// 清除所有会话数据
try {
await clearAllSessionData('window-all-closed');
} catch (error) {
logger.error('窗口关闭时清理缓存失败:', error);
}
// 销毁托盘并退出应用
destroyTray()
app.quit();
} else {
// 如果是主动退出,则销毁托盘并退出应用
destroyTray()
app.quit()
}
}
})
app.on('before-quit', async (event) => {
// 在应用程序即将退出时执行操作,例如保存数据
event.preventDefault();
// 清除所有会话数据
try {
await clearAllSessionData('before-quit');
} catch (error) {
logger.error('应用退出时清理缓存失败:', error);
}
// 销毁托盘并退出应用
destroyTray()
app.quit();
});
// 在应用退出时注销所有快捷键
app.on('will-quit', async (event) => {
event.preventDefault();
// 清除所有会话数据
try {
await clearAllSessionData('will-quit');
} catch (error) {
logger.error('应用退出时清理缓存失败:', error);
}
unregisterAllShortcuts()
destroyTray()
// 清理完成后退出应用
app.quit();
})
// 监听进程退出信号,确保在系统强制关闭时也能清理缓存
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)
}