|
9 | 9 | */ |
10 | 10 |
|
11 | 11 | import { app, BrowserWindow, Menu, dialog, session } from 'electron'; |
| 12 | +import { writeFile, readFile } from 'fs/promises'; |
12 | 13 | import { safeHandle, setMainWindowId } from './ipcUtils.js'; |
13 | 14 | import { logger } from './logger.js'; |
14 | 15 | import { registerAIHandlers } from './ai/index.js'; |
@@ -196,6 +197,64 @@ if (!gotTheLock) { |
196 | 197 | return { success: true }; |
197 | 198 | }); |
198 | 199 |
|
| 200 | + // ================================================================ |
| 201 | + // v0.9.0: 备份相关 IPC handlers |
| 202 | + // ================================================================ |
| 203 | + |
| 204 | + /** |
| 205 | + * 显示保存对话框并将备份数据写入文件 |
| 206 | + */ |
| 207 | + safeHandle('backup:save', async (_event, data: string, defaultName?: string) => { |
| 208 | + const filename = defaultName || `keban-backup-${new Date().toISOString().slice(0, 10)}.json`; |
| 209 | + |
| 210 | + const result = await dialog.showSaveDialog({ |
| 211 | + title: '保存备份文件', |
| 212 | + defaultPath: filename, |
| 213 | + filters: [ |
| 214 | + { name: 'KeBan 备份文件', extensions: ['json'] }, |
| 215 | + { name: '所有文件', extensions: ['*'] }, |
| 216 | + ], |
| 217 | + }); |
| 218 | + |
| 219 | + if (result.canceled || !result.filePath) { |
| 220 | + return { success: false, canceled: true, path: null }; |
| 221 | + } |
| 222 | + |
| 223 | + try { |
| 224 | + await writeFile(result.filePath, data, 'utf-8'); |
| 225 | + return { success: true, canceled: false, path: result.filePath }; |
| 226 | + } catch (err) { |
| 227 | + const msg = err instanceof Error ? err.message : '写入失败'; |
| 228 | + return { success: false, canceled: false, path: null, error: msg }; |
| 229 | + } |
| 230 | + }); |
| 231 | + |
| 232 | + /** |
| 233 | + * 显示打开对话框,选择备份文件并读取内容 |
| 234 | + */ |
| 235 | + safeHandle('backup:open', async () => { |
| 236 | + const result = await dialog.showOpenDialog({ |
| 237 | + title: '选择备份文件', |
| 238 | + filters: [ |
| 239 | + { name: 'KeBan 备份文件', extensions: ['json'] }, |
| 240 | + { name: '所有文件', extensions: ['*'] }, |
| 241 | + ], |
| 242 | + properties: ['openFile'], |
| 243 | + }); |
| 244 | + |
| 245 | + if (result.canceled || result.filePaths.length === 0) { |
| 246 | + return { success: false, canceled: true, content: null }; |
| 247 | + } |
| 248 | + |
| 249 | + try { |
| 250 | + const content = await readFile(result.filePaths[0], 'utf-8'); |
| 251 | + return { success: true, canceled: false, content, path: result.filePaths[0] }; |
| 252 | + } catch (err) { |
| 253 | + const msg = err instanceof Error ? err.message : '读取失败'; |
| 254 | + return { success: false, canceled: false, content: null, error: msg }; |
| 255 | + } |
| 256 | + }); |
| 257 | + |
199 | 258 | app.on('activate', () => { |
200 | 259 | if (BrowserWindow.getAllWindows().length === 0) { |
201 | 260 | mainWindow = createMainWindow(isQuittingRef, performQuit); |
|
0 commit comments