- 任务右键菜单:实现打开输出目录(inputs/outputs/.cache 结构)、
复制资源链接、重新编辑、再次生成、拉取结果、重新下载、收藏、删除记录
- 主进程下载:通过 Electron net 模块绕过 CORS,支持 CDN 媒体文件下载
- sharp 缩略图:替换 nativeImage,支持 4K/8K+ 大尺寸素材的流式处理
- IPC 精简:40+ 冗余通道 → 15 个分组通道,移除未使用的 preload 桥接方法
- 任务存储:新增收藏字段、schema 迁移、task-store 持久化
- 清理废弃模块:canvas-ipc / updater / request / pricing
refactor: IPC/Preload/Services 模块化拆分 — 单一职责 + 向后兼容
主要变更:
- shared/constants/ipc-channels.ts → ipc/ 模块 (base/storage/canvas/updater)
- electron/preload.ts → preload/ 模块 (base/safe-storage/file-storage/canvas)
- electron/main/ipc/canvas-ipc.ts → canvas/ 模块 (types/walk/sidecar/mime/thumbnail)
- electron/main/ipc/storage-ipc.ts → 提取 utils/ (path/download/thumbnail)
- electron/main/updater.ts → updater/ 模块 (provider/platform-updaters)
- src/services/request.ts → request/ 模块 (token/interceptors/methods)
- src/shared/utils/pricing.ts → pricing/ 模块 (types/helpers/format/calculate/fields)
- src/modules/home/center/views/ModelInputForm.tsx → 提取 utils + DependentFormItem
架构优化:
- 所有拆分均保持向后兼容(旧导入路径仍可用)
- TypeScript 编译通过 ✓
- 每个模块单一职责,平均 ~130 行
220 lines
8.0 KiB
TypeScript
220 lines
8.0 KiB
TypeScript
// ============================================================
|
||
// updater/index.ts — 自动更新主入口
|
||
//
|
||
// 流程图:调用自有 API → 获取版本信息 → electron-updater 差分下载 → SHA512 校验 → 安装
|
||
// ============================================================
|
||
|
||
import { BrowserWindow, ipcMain } from 'electron';
|
||
import type { NsisUpdater, MacUpdater, AppImageUpdater } from 'electron-updater';
|
||
import type { UpdateInfo } from 'builder-util-runtime';
|
||
|
||
import { APP_VERSION } from '../../../shared/constants/version';
|
||
import { MAIN_TO_RENDERER_UPDATER, RENDERER_TO_MAIN_UPDATER } from '../../../shared/constants/ipc/updater';
|
||
import { logger } from '../logger';
|
||
import { HeiXiuProvider, getApiBaseUrl, getDeviceFingerprint, BUILD_CHANNEL, BUILD_EDITION } from './provider';
|
||
import { createPlatformUpdater } from './platform-updaters';
|
||
|
||
// ---------- 状态 ----------
|
||
|
||
let updateWindow: BrowserWindow | null = null;
|
||
let provider: HeiXiuProvider | null = null;
|
||
let platformUpdater: NsisUpdater | MacUpdater | AppImageUpdater | null = null;
|
||
|
||
// ============================================================
|
||
// 初始化
|
||
// ============================================================
|
||
|
||
export function initUpdater(mainWindow: BrowserWindow): void {
|
||
updateWindow = mainWindow;
|
||
|
||
console.log('[updater] initUpdater 被调用');
|
||
console.log(`[updater] API URL: ${getApiBaseUrl()}`);
|
||
console.log(`[updater] 渠道: ${BUILD_CHANNEL}`);
|
||
console.log(`[updater] 版本类型: ${BUILD_EDITION}`);
|
||
console.log(`[updater] 当前版本: ${APP_VERSION}`);
|
||
console.log(`[updater] 设备指纹: ${getDeviceFingerprint()}`);
|
||
|
||
// 创建自定义 Provider
|
||
provider = new HeiXiuProvider({
|
||
isUseMultipleRangeRequest: true,
|
||
platform: process.platform as 'win32' | 'darwin' | 'linux',
|
||
executor: null as never,
|
||
});
|
||
|
||
// 创建平台 Updater
|
||
platformUpdater = createPlatformUpdater(provider);
|
||
|
||
// 将 electron-updater 事件桥接到现有 IPC 通道
|
||
platformUpdater.on('checking-for-update', () => {
|
||
logger.debug('updater', '正在检查更新');
|
||
});
|
||
|
||
platformUpdater.on('update-available', (info: UpdateInfo) => {
|
||
// releaseNotes 可能是 string | ReleaseNoteInfo[] | null,统一转为 string
|
||
let notes = '';
|
||
if (typeof info.releaseNotes === 'string') {
|
||
notes = info.releaseNotes;
|
||
} else if (Array.isArray(info.releaseNotes)) {
|
||
notes = info.releaseNotes.map((n) => n.note).filter(Boolean).join('\n');
|
||
}
|
||
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_AVAILABLE, {
|
||
version: info.version,
|
||
releaseDate: info.releaseDate,
|
||
releaseNotes: notes,
|
||
forceUpdate: false,
|
||
});
|
||
logger.info('updater', '发现新版本', {
|
||
version: info.version,
|
||
releaseNotes: notes ? `${notes.slice(0, 50)}...` : '(空)',
|
||
});
|
||
});
|
||
|
||
platformUpdater.on('update-not-available', (info: UpdateInfo) => {
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_NOT_AVAILABLE);
|
||
logger.info('updater', '当前已是最新版本', { currentVersion: info.version });
|
||
});
|
||
|
||
platformUpdater.on('download-progress', (progress: {
|
||
percent: number;
|
||
bytesPerSecond: number;
|
||
transferred: number;
|
||
total: number;
|
||
}) => {
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_PROGRESS, {
|
||
percent: Math.round(progress.percent),
|
||
bytesPerSecond: progress.bytesPerSecond,
|
||
transferred: progress.transferred,
|
||
total: progress.total,
|
||
});
|
||
});
|
||
|
||
platformUpdater.on('update-downloaded', () => {
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_DOWNLOADED);
|
||
logger.info('updater', '更新已下载完成');
|
||
});
|
||
|
||
platformUpdater.on('error', (error: Error) => {
|
||
logger.error('updater', '更新流程出错', error);
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_ERROR, {
|
||
message: error.message || '更新失败',
|
||
});
|
||
});
|
||
|
||
// 启动 5 秒后静默检查
|
||
setTimeout(() => {
|
||
checkForUpdates();
|
||
}, 5000);
|
||
}
|
||
|
||
// ============================================================
|
||
// 操作方法
|
||
// ============================================================
|
||
|
||
let updateChecked = false;
|
||
|
||
/** 静默检查更新(自动下载) */
|
||
export async function checkForUpdates(): Promise<void> {
|
||
if (updateChecked) {
|
||
logger.debug('updater', '已检查过更新,跳过');
|
||
return;
|
||
}
|
||
|
||
if (!platformUpdater || !provider) return;
|
||
|
||
try {
|
||
updateChecked = true;
|
||
|
||
// ① 先自行检查有无更新(不经过 electron-updater)
|
||
const info = await provider.getLatestVersion();
|
||
if (info.version === APP_VERSION) {
|
||
// 无更新 → 直接通知 UI,不触发 electron-updater 的任何事件
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_NOT_AVAILABLE);
|
||
logger.debug('updater', '当前已是最新版本', { version: APP_VERSION });
|
||
return;
|
||
}
|
||
|
||
// ② 有新版本 → 交给 electron-updater 触发 'update-available' 事件(不自动下载)
|
||
await platformUpdater.checkForUpdates();
|
||
} catch (error) {
|
||
updateChecked = false;
|
||
logger.error('updater', '更新流程失败', error as Error, { apiUrl: getApiBaseUrl() });
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_ERROR, {
|
||
message: (error as Error).message || '更新检查失败',
|
||
});
|
||
}
|
||
}
|
||
|
||
/** 手动检查更新(用户点击触发) */
|
||
export async function checkForUpdatesManual(): Promise<void> {
|
||
if (!platformUpdater || !provider) return;
|
||
|
||
try {
|
||
updateChecked = false;
|
||
|
||
// ① 先自行检查有无更新
|
||
const info = await provider.getLatestVersion();
|
||
if (info.version === APP_VERSION) {
|
||
// 无更新 → 直接通知 UI
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_NOT_AVAILABLE);
|
||
logger.debug('updater', '手动检查:当前已是最新版本', { version: APP_VERSION });
|
||
return;
|
||
}
|
||
|
||
// ② 有新版本 → 交给 electron-updater 触发 'update-available' 事件(不自动下载)
|
||
await platformUpdater.checkForUpdates();
|
||
} catch (error) {
|
||
logger.error('updater', '手动更新失败', error as Error);
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_ERROR, {
|
||
message: (error as Error).message || '检查更新失败',
|
||
});
|
||
}
|
||
}
|
||
|
||
/** 用户确认后开始下载更新 */
|
||
export async function downloadUpdate(): Promise<void> {
|
||
if (!platformUpdater) {
|
||
logger.warn('updater', 'downloadUpdate: Updater 未初始化');
|
||
return;
|
||
}
|
||
try {
|
||
await platformUpdater.downloadUpdate();
|
||
} catch (error) {
|
||
logger.error('updater', '下载更新失败', error as Error);
|
||
updateWindow?.webContents.send(MAIN_TO_RENDERER_UPDATER.UPDATE_ERROR, {
|
||
message: (error as Error).message || '下载更新失败',
|
||
});
|
||
}
|
||
}
|
||
|
||
/** 安装已下载的更新并退出应用 */
|
||
export function installUpdateNow(): void {
|
||
if (!platformUpdater) {
|
||
logger.warn('updater', 'Updater 未初始化');
|
||
return;
|
||
}
|
||
|
||
logger.info('updater', '开始安装更新并退出应用');
|
||
setImmediate(() => {
|
||
platformUpdater!.quitAndInstall(false, true);
|
||
});
|
||
}
|
||
|
||
// ============================================================
|
||
// IPC 注册
|
||
// ============================================================
|
||
|
||
export function registerUpdateIpcHandlers(): void {
|
||
ipcMain.handle(RENDERER_TO_MAIN_UPDATER.CHECK_FOR_UPDATE, async () => {
|
||
await checkForUpdatesManual();
|
||
});
|
||
|
||
ipcMain.handle(RENDERER_TO_MAIN_UPDATER.DOWNLOAD_UPDATE, async () => {
|
||
await downloadUpdate();
|
||
});
|
||
|
||
ipcMain.on(RENDERER_TO_MAIN_UPDATER.INSTALL_UPDATE, () => {
|
||
installUpdateNow();
|
||
});
|
||
}
|