index.cjs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. const { ipcMain, shell } = require('electron');
  2. const https = require('node:https');
  3. const { registerAiIpc } = require('./aiIpc.cjs');
  4. const { registerConfigIpc } = require('./configIpc.cjs');
  5. const { registerDuplicateCheckIpc } = require('./duplicateCheckIpc.cjs');
  6. const { registerExportIpc } = require('./exportIpc.cjs');
  7. const { registerFileIpc } = require('./fileIpc.cjs');
  8. const { registerKnowledgeBaseIpc } = require('./knowledgeBaseIpc.cjs');
  9. const { registerTaskIpc } = require('./taskIpc.cjs');
  10. const { registerWorkspaceIpc } = require('./workspaceIpc.cjs');
  11. const { createAiService } = require('../services/aiService.cjs');
  12. const { createConfigStore } = require('../services/configStore.cjs');
  13. const { createDuplicateCheckService } = require('../services/duplicateCheckService.cjs');
  14. const { createExportService } = require('../services/exportService.cjs');
  15. const { createFileService } = require('../services/fileService.cjs');
  16. const { createKnowledgeBaseService } = require('../services/knowledgeBaseService.cjs');
  17. const { createTaskService } = require('../services/taskService.cjs');
  18. const { createWorkspaceStore } = require('../services/workspaceStore.cjs');
  19. function normalizeExternalUrl(value) {
  20. const raw = String(value || '').trim();
  21. if (!raw) return null;
  22. const candidate = /^www\./i.test(raw) ? `https://${raw}` : raw;
  23. try {
  24. const url = new URL(candidate);
  25. return ['http:', 'https:'].includes(url.protocol) ? url.toString() : null;
  26. } catch {
  27. return null;
  28. }
  29. }
  30. function registerIpcHandlers({ app, mainWindow, checkAndDownloadUpdate, triggerUpdateDownload, quitAndInstall }) {
  31. const configStore = createConfigStore(app);
  32. const aiService = createAiService({ app, configStore });
  33. const fileService = createFileService({ app, configStore });
  34. const exportService = createExportService();
  35. const knowledgeBaseService = createKnowledgeBaseService({ app, aiService, configStore });
  36. const workspaceStore = createWorkspaceStore(app);
  37. const duplicateCheckService = createDuplicateCheckService({ app, configStore, workspaceStore });
  38. const taskService = createTaskService({ aiService, workspaceStore, knowledgeBaseService });
  39. registerConfigIpc({ configStore, aiService });
  40. registerAiIpc({ aiService });
  41. registerFileIpc({ fileService });
  42. registerDuplicateCheckIpc({ duplicateCheckService });
  43. registerKnowledgeBaseIpc({ knowledgeBaseService });
  44. registerExportIpc({ exportService });
  45. registerWorkspaceIpc({ workspaceStore });
  46. registerTaskIpc({ taskService });
  47. ipcMain.handle('app:get-version', () => app.getVersion());
  48. ipcMain.handle('app:open-external', async (_event, url) => {
  49. const externalUrl = normalizeExternalUrl(url);
  50. if (!externalUrl) {
  51. return { success: false, message: '不支持的外部链接' };
  52. }
  53. try {
  54. await shell.openExternal(externalUrl);
  55. return { success: true };
  56. } catch (error) {
  57. const preview = externalUrl.length > 300 ? `${externalUrl.slice(0, 300)}...` : externalUrl;
  58. console.warn('[app] 打开外部链接失败', { url: preview, message: error.message || String(error) });
  59. return { success: false, message: '外部链接打开失败' };
  60. }
  61. });
  62. ipcMain.handle('app:get-latest-version', () => {
  63. return new Promise((resolve, reject) => {
  64. const url = 'https://api.github.com/repos/FB208/OpenBidKit_Yibiao/releases/latest';
  65. const request = https.get(url, { headers: { 'User-Agent': 'yibiao-client' } }, (response) => {
  66. let data = '';
  67. response.on('data', (chunk) => { data += chunk; });
  68. response.on('end', () => {
  69. try {
  70. const release = JSON.parse(data);
  71. resolve({
  72. version: release.tag_name?.replace(/^v/, '') || '',
  73. name: release.name || '',
  74. body: release.body || '',
  75. published_at: release.published_at || '',
  76. html_url: release.html_url || '',
  77. });
  78. } catch (error) {
  79. reject(new Error('解析 GitHub API 响应失败'));
  80. }
  81. });
  82. });
  83. request.on('error', (error) => reject(error));
  84. request.setTimeout(10000, () => {
  85. request.destroy();
  86. reject(new Error('请求超时'));
  87. });
  88. });
  89. });
  90. ipcMain.handle('app:quit-and-install', () => {
  91. quitAndInstall();
  92. });
  93. ipcMain.handle('app:check-update', (event) => {
  94. const webContents = event.sender;
  95. return checkAndDownloadUpdate({
  96. app,
  97. mainWindow,
  98. onProgress: (percent) => {
  99. webContents.send('app:update-progress', { percent });
  100. },
  101. onDownloaded: (version) => {
  102. webContents.send('app:update-downloaded', { version });
  103. },
  104. onError: (message) => {
  105. webContents.send('app:update-error', { message });
  106. },
  107. });
  108. });
  109. ipcMain.handle('app:start-update', (event) => {
  110. const webContents = event.sender;
  111. return triggerUpdateDownload({
  112. app,
  113. mainWindow,
  114. onProgress: (percent) => {
  115. webContents.send('app:update-progress', { percent });
  116. },
  117. onDownloaded: (version) => {
  118. webContents.send('app:update-downloaded', { version });
  119. },
  120. onError: (message) => {
  121. webContents.send('app:update-error', { message });
  122. },
  123. });
  124. });
  125. }
  126. module.exports = {
  127. registerIpcHandlers,
  128. };