Files
dify_market_manager_gui/src/main/index.js
2025-07-22 17:53:26 +08:00

400 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, 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)
}