430 lines
13 KiB
JavaScript
430 lines
13 KiB
JavaScript
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;
|
||
}
|
||
});
|
||
}
|