Files
dootask/electron/electron.js
T
kuaifan 2163bb0bff fix(electron): 修复客户端下载功能无法启动的问题
- 将 onRenderer 参数从 mainWindow 改为 getMainWindow 函数,解决模块加载时 mainWindow 为 null 导致下载无法触发的问题
  - 处理 InterruptedError 错误,避免下载中断时抛出未处理异常
2026-01-16 00:20:52 +08:00

1064 lines
30 KiB
JavaScript
Vendored

// Node.js 核心模块
const fs = require('fs')
const os = require("os");
const path = require('path')
const spawn = require("child_process").spawn;
// Web 服务相关
const express = require('express')
const axios = require('axios');
// Electron 核心模块
const {
app,
ipcMain,
dialog,
clipboard,
nativeImage,
globalShortcut,
nativeTheme,
Tray,
Menu,
BrowserWindow
} = require('electron')
// 禁用渲染器后台化
app.commandLine.appendSwitch('disable-renderer-backgrounding');
app.commandLine.appendSwitch('disable-backgrounding-occluded-windows');
// Electron 扩展和工具
const {autoUpdater} = require("electron-updater")
const Store = require("electron-store");
const loger = require("electron-log");
const electronConf = require('electron-config')
const Screenshots = require("electron-screenshots-tool").Screenshots;
// 本地模块和配置
const utils = require('./lib/utils');
const navigation = require('./lib/navigation');
const config = require('./package.json');
const electronDown = require("./electron-down");
const electronMenu = require("./electron-menu");
const { startMCPServer, stopMCPServer } = require("./lib/mcp");
const {onRenderer, renderer} = require("./lib/renderer");
const {onExport} = require("./lib/pdf-export");
const {allowedCalls, isWin} = require("./lib/other");
const webTabManager = require("./lib/web-tab-manager");
const faviconCache = require("./lib/favicon-cache");
// 实例初始化
const userConf = new electronConf()
const store = new Store();
// 路径和缓存配置
const cacheDir = path.join(os.tmpdir(), 'dootask-cache')
const updaterLockFile = path.join(cacheDir, '.dootask_updater.lock');
// 应用状态标志
let isReady = false,
willQuitApp = false,
isDevelopMode = false;
// 服务器配置
let serverPort = 22223,
mcpPort = 22224,
serverPublicDir = path.join(__dirname, 'public'),
serverUrl = "",
serverTimer = null;
// 截图相关变量
let screenshotObj = null,
screenshotKey = null;
// 窗口实例变量
let mainWindow = null,
mainTray = null,
mediaWindow = null;
// 窗口配置和状态
let mediaType = null;
// 开发模式路径
let devloadPath = path.resolve(__dirname, ".devload");
// 开发模式加载
if (fs.existsSync(devloadPath)) {
let devloadContent = fs.readFileSync(devloadPath, 'utf8')
if (devloadContent.startsWith('http')) {
serverUrl = devloadContent;
isDevelopMode = true;
}
}
// 缓存目录检查
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
}
// 初始化下载
electronDown.initialize(() => {
if (mainWindow) {
mainWindow.webContents.send("openDownloadWindow", {})
}
})
/**
* 启动web服务
*/
async function startWebServer(force = false) {
if (serverUrl && !force) {
return Promise.resolve();
}
// 每次启动前清理缓存
utils.clearServerCache();
return new Promise((resolve, reject) => {
// 创建Express应用
const expressApp = express();
// 健康检查
expressApp.head('/health', (req, res) => {
res.status(200).send('OK');
});
// 使用express.static中间件提供静态文件服务
// Express内置了全面的MIME类型支持,无需手动配置
expressApp.use(express.static(serverPublicDir, {
// 设置默认文件
index: ['index.html', 'index.htm'],
// 启用etag缓存
etag: true,
// 设置缓存时间(开发环境可以设置较短)
maxAge: '1h',
// 启用压缩
dotfiles: 'ignore',
// 自定义头部
setHeaders: (res, path, stat) => {
const ext = path.split('.').pop().toLowerCase();
// HTML、JS、CSS文件禁用缓存,方便开发调试
if (['html', 'js', 'css'].includes(ext)){
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
res.set('Pragma', 'no-cache');
res.set('Expires', '0');
}
}
}));
// 404处理中间件
expressApp.use((req, res) => {
res.status(404).send('File not found');
});
// 错误处理中间件
expressApp.use((err, req, res, next) => {
// 不是ENOENT错误,记录error级别日志
if (err.code !== 'ENOENT') {
loger.error('Server error:', err);
res.status(500).send('Internal Server Error');
return;
}
// 没有path,说明是404错误
if (!err.path) {
loger.warn('File not found:', req.url);
res.status(404).send('File not found');
return;
}
// 不是临时文件错误,普通404
if (!err.path.includes('.com.dootask.task.')) {
loger.warn('File not found:', err.path);
res.status(404).send('File not found');
return;
}
// 防止死循环 - 如果已经是重定向请求,直接返回404
if (req.query._dt_restored) {
const redirectTime = parseInt(req.query._dt_restored);
const timeDiff = Date.now() - redirectTime;
// 10秒内的重定向认为是死循环,直接返回404
if (timeDiff < 10000) {
loger.warn('Recent redirect detected, avoiding loop:', timeDiff + 'ms ago');
res.status(404).send('File not found');
return;
}
}
loger.warn('Temporary file cleaned up by system:', err.path, req.url);
// 临时文件被系统清理,尝试从serverPublicDir重新读取并恢复
const requestedUrl = new URL(req.url, serverUrl);
const requestedFile = path.join(serverPublicDir, requestedUrl.pathname === '/' ? '/index.html' : requestedUrl.pathname);
try {
// 检查文件是否存在于serverPublicDir
fs.accessSync(requestedFile, fs.constants.F_OK);
// 确保目标目录存在
const targetDir = path.dirname(err.path);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, {recursive: true});
}
// 从ASAR文件中读取文件并写入到临时位置
fs.writeFileSync(err.path, fs.readFileSync(requestedFile));
// 文件恢复成功后,301重定向到带__redirect参数的URL
requestedUrl.searchParams.set('_dt_restored', Date.now());
res.redirect(301, requestedUrl.toString());
} catch (accessErr) {
// 文件不存在于serverPublicDir,返回404
loger.warn('Source file not found:', requestedFile, 'Error:', accessErr.message);
res.status(404).send('File not found');
}
});
// 启动服务器
const server = expressApp.listen(serverPort, 'localhost', () => {
loger.info(`Express static file server running at http://localhost:${serverPort}/`);
loger.info(`Serving files from: ${serverPublicDir}`);
serverUrl = `http://localhost:${serverPort}/`;
resolve(server);
// 启动健康检查定时器
serverTimeout();
});
// 错误处理
server.on('error', (err) => {
loger.error('Server error:', err);
reject(err);
});
});
}
/**
* 健康检查定时器
*/
function serverTimeout() {
clearTimeout(serverTimer)
serverTimer = setTimeout(async () => {
if (!serverUrl) {
return; // 没有服务器URL,直接返回
}
try {
const res = await axios.head(serverUrl + 'health')
if (res.status === 200) {
serverTimeout() // 健康检查通过,重新设置定时器
return;
}
loger.error('Server health check failed with status: ' + res.status);
} catch (err) {
loger.error('Server health check error:', err);
}
// 如果健康检查失败,尝试重新启动服务器
try {
await startWebServer(true)
loger.info('Server restarted successfully');
} catch (error) {
loger.error('Failed to restart server:', error);
}
}, 10000)
}
/**
* 创建主窗口
*/
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 1280,
height: 800,
minWidth: 360,
minHeight: 360,
center: true,
autoHideMenuBar: true,
backgroundColor: utils.getDefaultBackgroundColor(),
webPreferences: {
preload: path.join(__dirname, 'electron-preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: true,
backgroundThrottling: false,
}
})
mainWindow.on('page-title-updated', (event, title) => {
if (title == "index.html") {
event.preventDefault()
}
})
mainWindow.on('focus', () => {
mainWindow.webContents.send("browserWindowFocus", {})
})
mainWindow.on('blur', () => {
mainWindow.webContents.send("browserWindowBlur", {})
})
mainWindow.on('close', event => {
if (!willQuitApp) {
webTabManager.onBeforeUnload(event, mainWindow).then(() => {
if (['darwin', 'win32'].includes(process.platform)) {
if (mainWindow.isFullScreen()) {
mainWindow.once('leave-full-screen', () => {
mainWindow.hide();
})
mainWindow.setFullScreen(false)
} else {
mainWindow.hide();
}
} else {
app.quit();
}
})
}
})
// 设置 UA
const originalUA = mainWindow.webContents.session.getUserAgent() || mainWindow.webContents.getUserAgent()
mainWindow.webContents.setUserAgent(originalUA + " MainTaskWindow/" + process.platform + "/" + os.arch() + "/1.0");
// 新窗口处理
mainWindow.webContents.setWindowOpenHandler(({url}) => {
if (allowedCalls.test(url)) {
renderer.openExternal(url).catch(() => {})
} else {
utils.onBeforeOpenWindow(mainWindow.webContents, url).then(() => {
renderer.openExternal(url).catch(() => {})
})
}
return {action: 'deny'}
})
// 设置右键菜单
electronMenu.webContentsMenu(mainWindow.webContents)
// 设置导航快捷键(返回/前进)
navigation.setup(mainWindow)
// 加载地址
utils.loadUrl(mainWindow, serverUrl)
}
/**
* 创建更新程序子进程
*/
function createUpdaterWindow(updateTitle) {
// 检查平台是否支持
if (!['darwin', 'win32'].includes(process.platform)) {
return;
}
try {
// 构建updater应用路径
let updaterPath;
if (isWin) {
updaterPath = path.join(process.resourcesPath, 'updater', 'updater.exe');
} else {
updaterPath = path.join(process.resourcesPath, 'updater', 'updater');
}
// 检查updater应用是否存在
if (!fs.existsSync(updaterPath)) {
loger.error('Updater not found:', updaterPath);
return;
}
// 检查文件权限
try {
fs.accessSync(updaterPath, fs.constants.X_OK);
} catch (e) {
if (isWin) {
try {
spawn('icacls', [updaterPath, '/grant', 'everyone:F'], { stdio: 'inherit', shell: true });
} catch (e) {
loger.error('Failed to set executable permission:', e);
}
} else if (process.platform === 'darwin') {
try {
spawn('chmod', ['+x', updaterPath], {stdio: 'inherit'});
} catch (e) {
loger.error('Failed to set executable permission:', e);
}
}
}
// 创建锁文件
fs.writeFileSync(updaterLockFile, Date.now().toString());
// 启动子进程,传入锁文件路径作为第一个参数
const child = spawn(updaterPath, [updaterLockFile], {
detached: true,
stdio: 'ignore',
shell: isWin,
env: {
...process.env,
ELECTRON_RUN_AS_NODE: '1',
UPDATER_TITLE: updateTitle || ''
}
});
child.unref();
child.on('error', (err) => {
loger.error('Updater process error:', err);
});
} catch (e) {
loger.error('Failed to create updater process:', e);
}
}
/**
* 创建媒体浏览器窗口
* @param args
* @param type
*/
function createMediaWindow(args, type = 'image') {
if (mediaWindow === null) {
mediaWindow = new BrowserWindow({
width: Math.floor(args.width) || 970,
height: Math.floor(args.height) || 700,
minWidth: 360,
minHeight: 360,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
webSecurity: false,
plugins: true
},
show: false
});
// 监听关闭事件
mediaWindow.addListener('close', event => {
if (!willQuitApp) {
event.preventDefault()
if (mediaWindow.isFullScreen()) {
mediaWindow.once('leave-full-screen', () => {
mediaWindow.hide();
})
mediaWindow.setFullScreen(false)
} else {
mediaWindow.webContents.send('on-close');
mediaWindow.hide();
}
}
})
// 监听关闭事件
mediaWindow.addListener('closed', () => {
mediaWindow = null;
mediaType = null;
})
// 设置右键菜单
electronMenu.webContentsMenu(mediaWindow.webContents)
} else {
// 直接显示
mediaWindow.show();
}
// 加载图片浏览器的HTML
if (mediaType === type) {
// 更新窗口
mediaWindow.webContents.send('load-media', args);
} else {
// 重置窗口
mediaType = type;
let filePath = './render/viewer/index.html';
if (type === 'video') {
filePath = './render/video/index.html';
}
mediaWindow.loadFile(filePath, {}).then(_ => { }).catch(_ => { })
}
// 窗口准备好后事件
mediaWindow.removeAllListeners("ready-to-show");
mediaWindow.addListener('ready-to-show', () => {
mediaWindow.show();
mediaWindow.webContents.send('load-media', args);
});
}
/**
* 监听主题变化
*/
function monitorThemeChanges() {
let currentTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
nativeTheme.on('updated', () => {
const newTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
if (currentTheme === newTheme) {
return
}
currentTheme = newTheme;
// 更新背景
const backgroundColor = utils.getDefaultBackgroundColor()
mainWindow?.setBackgroundColor(backgroundColor);
mediaWindow?.setBackgroundColor(backgroundColor);
// 更新所有 webTab 窗口背景
for (const [, windowData] of webTabManager.getWebTabWindows()) {
windowData.window?.setBackgroundColor(nativeTheme.shouldUseDarkColors ? '#202124' : '#F1F3F4');
}
// 通知所有窗口
BrowserWindow.getAllWindows().forEach(window => {
window.webContents.send('systemThemeChanged', {
theme: currentTheme,
});
});
// 通知所有 webTab 视图(WebContentsView 中的网站内容)
for (const [, windowData] of webTabManager.getWebTabWindows()) {
windowData.views?.forEach(({ view }) => {
if (view && !view.webContents.isDestroyed()) {
view.webContents.send('systemThemeChanged', {
theme: currentTheme,
});
}
});
}
})
}
const getTheLock = app.requestSingleInstanceLock()
if (!getTheLock) {
app.quit()
} else {
app.on('second-instance', () => {
utils.setShowWindow(mainWindow)
})
app.on('ready', async () => {
isReady = true
isWin && app.setAppUserModelId(config.appId)
// 清理过期的 favicon 缓存
faviconCache.cleanExpiredCache()
// 初始化 webTabManager
webTabManager.init({
getServerUrl: () => serverUrl,
getUserConf: () => userConf,
isWillQuitApp: () => willQuitApp,
electronMenu: electronMenu,
})
webTabManager.registerIPC()
// 启动 Web 服务器
try {
await startWebServer()
} catch (error) {
dialog.showErrorBox('启动失败', `Web 服务器启动失败:${error.message}`);
app.quit();
return;
}
// SameSite
utils.useCookie()
// 创建主窗口
createMainWindow()
// 预热预加载池(延迟启动,避免影响主窗口加载)
webTabManager.warmupPreloadPool()
// 监听主题变化
monitorThemeChanges()
// 创建托盘
if (['darwin', 'win32'].includes(process.platform) && utils.isJson(config.trayIcon)) {
mainTray = new Tray(path.join(__dirname, config.trayIcon[isDevelopMode ? 'dev' : 'prod'][process.platform === 'darwin' ? 'mac' : 'win']));
mainTray.on('click', () => {
utils.setShowWindow(mainWindow)
})
mainTray.setToolTip(config.name)
if (process.platform === 'win32') {
const trayMenu = Menu.buildFromTemplate([{
label: '显示',
click: () => {
utils.setShowWindow(mainWindow)
}
}, {
label: '退出',
click: () => {
app.quit()
}
}])
mainTray.setContextMenu(trayMenu)
}
}
// 删除updater锁文件(如果存在)
if (fs.existsSync(updaterLockFile)) {
try {
fs.unlinkSync(updaterLockFile);
} catch (e) {
//忽略错误
}
}
// 截图对象
screenshotObj = new Screenshots({
singleWindow: true,
mainWindow: mainWindow
})
})
}
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
if (isReady) {
createMainWindow()
}
} else if (mainWindow) {
if (!mainWindow.isVisible()) {
mainWindow.show()
}
}
})
app.on('window-all-closed', () => {
if (willQuitApp || process.platform !== 'darwin') {
app.quit()
}
})
app.on('before-quit', () => {
willQuitApp = true
// 清理预加载池
webTabManager.clearPreloadPool()
})
app.on("will-quit", () => {
globalShortcut.unregisterAll();
})
/**
* 设置菜单语言包
* @param args {path}
*/
ipcMain.on('setMenuLanguage', (event, args) => {
if (utils.isJson(args)) {
electronMenu.setLanguage(args)
}
event.returnValue = "ok"
})
/**
* 打开文件
* @param args {path}
*/
ipcMain.on('openFile', (event, args) => {
utils.openFile(args.path)
event.returnValue = "ok"
})
/**
* 退出客户端
*/
ipcMain.on('windowQuit', (event) => {
event.returnValue = "ok"
app.quit();
})
/**
* 打开媒体浏览器
*/
ipcMain.on('openMediaViewer', (event, args) => {
createMediaWindow(args, ['image', 'video'].includes(args.type) ? args.type : 'image');
event.returnValue = "ok"
});
/**
* 隐藏窗口(mac、win隐藏,其他关闭)
*/
ipcMain.on('windowHidden', (event) => {
if (['darwin', 'win32'].includes(process.platform)) {
app.hide();
} else {
app.quit();
}
event.returnValue = "ok"
})
/**
* 关闭所有子窗口(包含所有 webTab 窗口、mediaWindow 和下载窗口)
*/
ipcMain.on('childWindowCloseAll', (event) => {
webTabManager.closeAll()
mediaWindow?.close()
electronDown.close()
event.returnValue = "ok"
})
/**
* 销毁所有子窗口(包含所有 webTab 窗口、mediaWindow 和下载窗口)
*/
ipcMain.on('childWindowDestroyAll', (event) => {
webTabManager.destroyAll()
mediaWindow?.destroy()
electronDown.destroy()
event.returnValue = "ok"
})
/**
* 设置窗口尺寸
* @param args {width, height, autoZoom, minWidth, minHeight, maxWidth, maxHeight}
*/
ipcMain.on('windowSize', (event, args) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
if (args.width || args.height) {
let [w, h] = win.getSize()
const width = args.width || w
const height = args.height || h
win.setSize(width, height, args.animate === true)
//
if (args.autoZoom === true) {
let move = false
let [x, y] = win.getPosition()
if (Math.abs(width - w) > 10) {
move = true
x -= (width - w) / 2
}
if (Math.abs(height - h) > 10) {
move = true
y -= (height - h) / 2
}
if (move) {
win.setPosition(Math.max(0, Math.floor(x)), Math.max(0, Math.floor(y)))
}
}
}
if (args.minWidth || args.minHeight) {
win.setMinimumSize(args.minWidth || win.getMinimumSize()[0], args.minHeight || win.getMinimumSize()[1])
}
if (args.maxWidth || args.maxHeight) {
win.setMaximumSize(args.maxWidth || win.getMaximumSize()[0], args.maxHeight || win.getMaximumSize()[1])
}
}
event.returnValue = "ok"
})
/**
* 设置窗口最小尺寸
* @param args {minWidth, minHeight}
*/
ipcMain.on('windowMinSize', (event, args) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
win.setMinimumSize(args.minWidth || win.getMinimumSize()[0], args.minHeight || win.getMinimumSize()[1])
}
event.returnValue = "ok"
})
/**
* 设置窗口最大尺寸
* @param args {maxWidth, maxHeight}
*/
ipcMain.on('windowMaxSize', (event, args) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
win.setMaximumSize(args.maxWidth || win.getMaximumSize()[0], args.maxHeight || win.getMaximumSize()[1])
}
event.returnValue = "ok"
})
/**
* 窗口居中
*/
ipcMain.on('windowCenter', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win) {
win.center();
}
event.returnValue = "ok"
})
/**
* 窗口最大化或恢复
*/
ipcMain.on('windowMax', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
if (win.isMaximized()) {
win.restore();
} else {
win.maximize();
}
event.returnValue = "ok"
})
/**
* 给所有窗口广播指令(除了本身)
* @param args {type, payload}
*/
ipcMain.on('broadcastCommand', (event, args) => {
const channel = args.channel || args.command
const payload = args.payload || args.data
// 广播给所有 BrowserWindow
BrowserWindow.getAllWindows().forEach(window => {
if (window.webContents.id !== event.sender.id) {
window.webContents.send(channel, payload)
}
})
// 广播给 webTabManager 中的所有 view
for (const [, windowData] of webTabManager.getWebTabWindows()) {
windowData.views?.forEach(({ view }) => {
if (view && !view.webContents.isDestroyed() && view.webContents.id !== event.sender.id) {
view.webContents.send(channel, payload)
}
})
}
event.returnValue = "ok"
})
/**
* 设置Dock标记(window闪烁、macos标记)
* @param args
*/
ipcMain.on('setDockBadge', (event, args) => {
if (process.platform === 'win32') {
// Window flash
if (!mainWindow.isFocused()) {
mainWindow.once('focus', () => mainWindow.flashFrame(false))
mainWindow.flashFrame(true)
}
return;
}
if (process.platform !== 'darwin') {
// Mac only
return;
}
let num = args;
let tray = true;
if (utils.isJson(args)) {
num = args.num
tray = !!args.tray
}
let text = typeof num === "string" ? num : (utils.runNum(num) > 0 ? String(num) : "")
app.dock.setBadge(text)
if (tray && mainTray) {
mainTray.setTitle(text)
}
event.returnValue = "ok"
})
/**
* MCP 服务器状态切换
* @param args
*/
ipcMain.on('mcpServerToggle', (event, args) => {
const { running } = args;
if (running === 'running') {
startMCPServer(mainWindow, mcpPort)
} else {
stopMCPServer()
}
})
/**
* 复制Base64图片
* @param args
*/
ipcMain.on('copyBase64Image', (event, args) => {
const { base64 } = args;
if (base64) {
const img = nativeImage.createFromDataURL(base64)
clipboard.writeImage(img)
}
event.returnValue = "ok"
})
/**
* 复制图片根据坐标
* @param args
*/
ipcMain.on('copyImageAt', (event, args) => {
try {
event.sender.copyImageAt(args.x, args.y);
} catch (e) {
loger.error('copyImageAt error:', e)
}
event.returnValue = "ok"
})
/**
* 保存图片
* @param args
*/
ipcMain.on('saveImageAt', async (event, args) => {
await electronMenu.saveImageAs(args.url, args.params)
event.returnValue = "ok"
})
/**
* 绑定截图快捷键
* @param args
*/
ipcMain.on('bindScreenshotKey', (event, args) => {
const { key } = args;
if (screenshotKey !== key) {
if (screenshotKey) {
globalShortcut.unregister(screenshotKey)
screenshotKey = null
}
if (key) {
screenshotKey = key
globalShortcut.register(key, () => {
screenshotObj.startCapture().then(_ => {
screenshotObj.view.webContents.executeJavaScript(`if(typeof window.__initializeShortcuts==='undefined'){window.__initializeShortcuts=true;document.addEventListener('keydown',function(e){console.log(e);if(e.keyCode===27){window.screenshots.cancel()}})}`, true).catch(() => {});
screenshotObj.view.webContents.focus()
})
})
}
}
event.returnValue = "ok"
})
/**
* 执行截图
*/
ipcMain.on('openScreenshot', (event) => {
if (screenshotObj) {
screenshotObj.startCapture().then(_ => {})
}
event.returnValue = "ok"
})
/**
* 关闭截图
*/
ipcMain.on('closeScreenshot', (event) => {
if (screenshotObj && screenshotObj.window?.isFocused()) {
screenshotObj.endCapture().then(_ => {});
}
event.returnValue = "ok"
})
/**
* 通知
*/
ipcMain.on('openNotification', (event, args) => {
utils.showNotification(args, mainWindow)
event.returnValue = "ok"
})
/**
* 保存缓存
*/
ipcMain.on('setStore', (event, args) => {
if (utils.isJson(args)) {
store.set(args.key, args.value)
}
event.returnValue = "ok"
})
/**
* 获取缓存
*/
ipcMain.handle('getStore', (event, args) => {
return store.get(args)
});
/**
* 清理服务器缓存
*/
ipcMain.on('clearServerCache', (event) => {
utils.clearServerCache();
event.returnValue = "ok";
});
//================================================================
// Update
//================================================================
let autoUpdating = 0
if (autoUpdater) {
autoUpdater.logger = loger
autoUpdater.autoDownload = false
autoUpdater.autoInstallOnAppQuit = true
autoUpdater.on('update-available', info => {
mainWindow.webContents.send("updateAvailable", info)
})
autoUpdater.on('update-downloaded', info => {
mainWindow.webContents.send("updateDownloaded", info)
})
}
/**
* 检查更新
*/
ipcMain.on('updateCheckAndDownload', (event, args) => {
event.returnValue = "ok"
if (autoUpdating + 3600 > utils.dayjs().unix()) {
return // 限制1小时仅执行一次
}
if (!autoUpdater) {
return
}
if (args.provider) {
autoUpdater.setFeedURL(args)
}
autoUpdater.checkForUpdates().then(info => {
if (!info) {
return
}
if (utils.compareVersion(config.version, info.updateInfo.version) >= 0) {
return
}
if (args.apiVersion) {
if (utils.compareVersion(info.updateInfo.version, args.apiVersion) <= 0) {
// 客户端版本 <= 接口版本
autoUpdating = utils.dayjs().unix()
autoUpdater.downloadUpdate().then(_ => {}).catch(_ => {})
}
} else {
autoUpdating = utils.dayjs().unix()
autoUpdater.downloadUpdate().then(_ => {}).catch(_ => {})
}
})
})
/**
* 将主窗口激活到顶层
*/
ipcMain.on('mainWindowTop', (event) => {
mainWindow.moveTop()
event.returnValue = "ok"
})
/**
* 将主窗口激活
*/
ipcMain.on('mainWindowActive', (event) => {
if (!mainWindow.isVisible()) {
mainWindow.show()
}
mainWindow.focus()
event.returnValue = "ok"
})
/**
* 退出并安装更新
*/
ipcMain.on('updateQuitAndInstall', (event, args) => {
if (!utils.isJson(args)) {
args = {}
}
event.returnValue = "ok"
// 关闭所有子窗口
willQuitApp = true
webTabManager.destroyAllWindowMode()
mediaWindow?.destroy()
electronDown.destroy()
// 启动更新子窗口
createUpdaterWindow(args.updateTitle)
// 退出并安装更新
setTimeout(_ => {
mainWindow.hide()
autoUpdater?.quitAndInstall(true, true)
}, 600)
})
//================================================================
//================================================================
//================================================================
onExport()
onRenderer(() => mainWindow)