import { BrowserWindow, ipcMain, shell, Notification, app } from "electron"; import { setStoreValue, getStoreValue } from './store.js' import { createNewWindow, createWindow, getMainWindow, getDrageWindow, closeApiConfigWindow, closeConfigWindow } from './window.js' import { difyRetryRequestTimer } from './dify.js' import axios from 'axios' import log from 'electron-log/main'; import fs from 'fs'; import os from 'os'; import path from 'path'; log.initialize(); import {checkForUpdates} from "./utils/updateUtils" import logger from './utils/logger' import dayjs from 'dayjs' function isValidUrl(_url) { const containsApp = _url.includes('/app/'); const containsConfigurationOrWorkflow = _url.includes('/configuration') || _url.includes('/workflow'); // 只有当 URL 包含 'app' 并且包含 'configuration' 或 'workflow' 时,才返回 true return containsApp && containsConfigurationOrWorkflow; } export function setupIPC() { ipcMain.handle('initClientData', (event, data) => { logger.info('=======================================initClientData') logger.info(data) setStoreValue("h5_data", data); setStoreValue("token", data.token); setStoreValue("dify_access_token:", data.dify_access_token); setStoreValue("dify_refresh_token", data.dify_refresh_token); setStoreValue("dify_csrf_token", data.dify_csrf_token); setStoreValue("userInfo", data.userInfo); setStoreValue("apiUrl", data.apiUrl); setStoreValue("difySite", data.difySite); setStoreValue("lastAliveTime",dayjs()) difyRetryRequestTimer() return 'pong' }) ipcMain.handle('openNewWindow', (event, _url, dify_access_token, refresh_token) => { logger.info('=======================================openNewWindow') logger.info(_url) const _sandbox=isValidUrl(_url) logger.info('=======================================openNewWindow _sandbox:',_sandbox) createNewWindow(_url, dify_access_token, refresh_token,_sandbox) return 'pong' }) ipcMain.handle('setMainWindowTitle', (event, title) => { const mainWindow = getMainWindow(); if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.setTitle(title); } return 'pong' }) ipcMain.handle('setLocalStorage', (event, key, value) => { setStoreValue("token", data.token); return 'pong' }) ipcMain.handle('setGuiLocalStorage', (event, key, value) => { logger.info("=============================================: setGuiLocalStorage key", key) logger.info("=============================================: setGuiLocalStorage value", value) setStoreValue(key, value); return 'pong' }) ipcMain.handle('getGuiLocalStorage', (event, key) => { return getStoreValue(key) }) ipcMain.handle('difyWebUploadFileToApi', async (event, datasetId, data) => { logger.info("=============================================: difyWebUploadFileToApi"); logger.info("=============================================: datasetId: " + datasetId); logger.info("=============================================: data: "); logger.info(data) try { const java_api = getStoreValue("apiUrl") + "/bqw-ai" + "/app/api/knowledge/addDocumentToKnowledge"; const token = getStoreValue("token"); if (!java_api || !token) { throw new Error("API URL or Token is missing in the store."); } const params = { kg_id: datasetId, files: datasetId, knowledgeDocumentParams: JSON.parse(data) }; logger.info("=======================java_api:"+java_api); logger.info("=======================params:") logger.info(params) const response = await axios.post(java_api, params, { headers: { 'x-access-token': `${token}`, 'Content-Type': 'application/json' } }); logger.info("Response from server:", response.data); // 获取当前窗口并关闭 const currentWindow = event.sender.getOwnerBrowserWindow(); if (currentWindow && !currentWindow.isDestroyed()) { currentWindow.close(); } return response.data; } catch (error) { console.error("Error making POST request:", error.message); throw error; } }); ipcMain.on('difyWebUpdateDatasetsInfo', (event) => { return 'pong' }) ipcMain.on('app:window:set-position', (event, x, y) => { const drageWindow = getDrageWindow(); if (drageWindow) { drageWindow.setPosition(x, y) } return 'pong' }) ipcMain.handle('app:window:switch-show-main-window', (event) => { const mainWindow = getMainWindow(); if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow.isVisible()) { mainWindow.hide(); } else { mainWindow.show(); } } else { createWindow(); } }); ipcMain.handle('app:window:get-position', (event) => { const drageWindow = getDrageWindow(); if (drageWindow) { const [x, y] = drageWindow.getPosition(); return { x, y } } return { x: 0, y: 0 } }) // 获取存储值 ipcMain.handle('getStoreValue', (event, key) => { return getStoreValue(key) }) // 设置存储值 ipcMain.handle('setStoreValue', (event, key, value) => { return setStoreValue(key, value) }) // 设置存储值 ipcMain.handle('setStoreValueByConfig', (event, key, value) => { return setStoreValue(key, value) }) // 关闭配置窗口 ipcMain.on('closeApiConfigWindow', () => { closeApiConfigWindow() }) ipcMain.on('closeConfigWindow', () => { closeConfigWindow() }) // 添加新的 IPC 监听处理函数 ipcMain.handle('openNewPage', (event, url) => { const newWindow = new BrowserWindow({ fullscreen: false, width: 1200, height: 700, minWidth: 1200, minHeight: 700, webPreferences: { webSecurity: false, // 禁用 Web 安全 nodeIntegration: true, contextIsolation: false, } }); newWindow.loadURL(url); // 添加重试机制的错误处理 let failLoadCount = 0; const maxRetries = 2; const retryDelay = 1000; newWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => { console.error('页面加载失败:', errorDescription); failLoadCount++; if (failLoadCount <= maxRetries) { console.log(`页面加载失败,${retryDelay}ms后进行第${failLoadCount}次重试...`); setTimeout(() => { newWindow.reload(); }, retryDelay); } else { console.error('页面加载失败次数超过最大重试次数'); } }); // 页面加载成功时重置失败计数 newWindow.webContents.on('did-finish-load', () => { if (failLoadCount > 0) { console.log('页面加载成功,重置失败计数'); failLoadCount = 0; } }); return 'success'; }); // 添加新的 IPC 监听处理函数 ipcMain.handle('openCSDNAiPage', (event, url) => { const newWindow = new BrowserWindow({ fullscreen: false, width: 1600, height: 900, webPreferences: { webSecurity: false, // 禁用 Web 安全 nodeIntegration: false, contextIsolation: false, } }); newWindow.loadURL(url); // 添加重试机制的错误处理 let failLoadCount2 = 0; const maxRetries2 = 2; const retryDelay2 = 1000; newWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => { console.error('页面加载失败:', errorDescription); failLoadCount2++; if (failLoadCount2 <= maxRetries2) { console.log(`页面加载失败,${retryDelay2}ms后进行第${failLoadCount2}次重试...`); setTimeout(() => { newWindow.reload(); }, retryDelay2); } else { console.error('页面加载失败次数超过最大重试次数'); } }); // 页面加载成功时重置失败计数 newWindow.webContents.on('did-finish-load', () => { if (failLoadCount2 > 0) { console.log('页面加载成功,重置失败计数'); failLoadCount2 = 0; } }); return 'success'; }); ipcMain.handle('openNewPagebySystemBrowser', (event, url) => { shell.openExternal(url); return 'success'; }); // 添加新的 IPC 监听处理函数 ipcMain.handle('updateToNewVersion', async (event) => { checkForUpdates({},false) }); // 下载文件并打开下载目录 ipcMain.handle('downloadFile', async (event, fileUrl) => { logger.info("=============================开始下载文件:",fileUrl) try { // 解码URL fileUrl = decodeURI(fileUrl); // 简单提取文件名:从URL路径的最后一部分获取 let fileName = ''; try { const url = new URL(fileUrl); const pathSegments = url.pathname.split('/').filter(segment => segment); fileName = pathSegments[pathSegments.length - 1] || ''; } catch (error) { // 如果URL解析失败,尝试直接从字符串中提取 const urlParts = fileUrl.split('/'); fileName = urlParts[urlParts.length - 1] || ''; } // 解码文件名,解决中文乱码问题 try { fileName = decodeURIComponent(fileName); } catch (error) { // 如果解码失败,保持原文件名 logger.warn(`文件名解码失败: ${fileName}`); } // 如果提取的文件名为空,使用时间戳生成默认名称 if (!fileName || fileName === '') { fileName = `downloaded_file_${Date.now()}`; } // 获取用户下载目录 const downloadDir = path.join(os.homedir(), 'Downloads'); // 确保下载目录存在 if (!fs.existsSync(downloadDir)) { fs.mkdirSync(downloadDir, { recursive: true }); } const outputLocationPath = path.join(downloadDir, fileName); const writer = fs.createWriteStream(outputLocationPath); let receivedBytes = 0; logger.info(`开始下载文件: ${fileUrl}`); logger.info(`提取的文件名: ${fileName}`); logger.info(`保存路径: ${outputLocationPath}`); const downloadResponse = await axios({ method: "get", url: fileUrl, responseType: "stream" }); const totalBytes = downloadResponse.headers["content-length"]; downloadResponse.data.on("data", (chunk) => { receivedBytes += chunk.length; if (totalBytes) { let progress = Math.floor((receivedBytes / totalBytes) * 100); // 发送进度给渲染进程 event.sender.send("download-progress", progress); } }); downloadResponse.data.pipe(writer); return new Promise((resolve, reject) => { writer.on("finish", async () => { logger.info(`文件下载完成: ${outputLocationPath}`); try { // 打开文件所在目录 await shell.showItemInFolder(outputLocationPath); resolve({ success: true, filePath: outputLocationPath, fileName: fileName }); } catch (error) { logger.error(`打开目录失败: ${error.message}`); reject(error); } }); writer.on("error", (error) => { logger.error(`文件写入失败: ${error.message}`); reject(error); }); }); } catch (error) { logger.error(`下载文件失败: ${error.message}`); throw error; } }); // 重启软件 ipcMain.handle('restartApp', async (event) => { logger.info("=============================重启软件"); try { // 标记为正在重启,避免其他退出逻辑干扰 app.isRestarting = true; // 确保所有窗口都被正确关闭 const windows = BrowserWindow.getAllWindows(); logger.info(`准备关闭 ${windows.length} 个窗口`); windows.forEach(window => { if (!window.isDestroyed()) { try { window.close(); logger.info('窗口关闭成功'); } catch (error) { logger.warn('关闭窗口时出错:', error); } } }); // 延迟执行重启,确保窗口关闭完成 setTimeout(() => { try { logger.info('执行应用重启'); app.relaunch(); app.exit(0); } catch (error) { logger.error('重启应用失败,尝试强制退出:', error); try { app.quit(); } catch (quitError) { logger.error('强制退出也失败:', quitError); process.exit(0); } } }, 1000); return { success: true }; } catch (error) { logger.error(`重启软件失败: ${error.message}`); throw error; } }); }