Files
ele-HeiXiu/electron/preload.ts
YoungestSongMo 8b697e775d feat: 去除构建时版控变量 + 窗口标题动态版控 + 退出登录保留缓存 (0.0.24)
版控体系重构(13 文件):
- 移除 build.mjs --personal/--enterprise 参数和 EDITION 环境变量
- 统一安装包文件名:船长-HeiXiu-{os}-{版本号}-Setup.{ext}
- 简化 package.json 构建脚本,移除 :enterprise 变体
- scripts/*.mjs 不再接受 edition 参数
- 移除 shared/constants/app.ts 的 parseEdition()

窗口标题动态版控:
- 新格式:船长-HeiXiu-{os}-{版本号}(登录前)/ ·{版控} 后缀(登录后)
- 链路:AppProvider(account_type) → useEffect → appRuntime IPC → main process
- 移除 updater.ts 更新检查 API 的 edition 查询参数

本地存储生命周期修正:
- 退出登录不再自动清空缓存(user_id 隔离,重新登录命中缓存)
- clearUserStorage 添加 isOpen() 竞态守卫
- 移除 closeDatabase() 避免退出→重新登录后数据库不可用
- 手动「清理缓存」按钮保留

文档:
- CHANGELOG.md 新增 0.0.24 条目
- TODO.md 新增已完成项
2026-06-22 13:36:42 +08:00

101 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ipcRenderer, contextBridge } from 'electron';
import { BIDIRECTIONAL, RENDERER_TO_MAIN } from '../shared/constants/ipc-channels';
// --------- Expose some API to the Renderer process ---------
contextBridge.exposeInMainWorld('ipcRenderer', {
on(...args: Parameters<typeof ipcRenderer.on>) {
const [channel, listener] = args;
return ipcRenderer.on(channel, (event, ...args) => listener(event, ...args));
},
off(...args: Parameters<typeof ipcRenderer.off>) {
const [channel, ...omit] = args;
return ipcRenderer.off(channel, ...omit);
},
send(...args: Parameters<typeof ipcRenderer.send>) {
const [channel, ...omit] = args;
return ipcRenderer.send(channel, ...omit);
},
invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
const [channel, ...omit] = args;
return ipcRenderer.invoke(channel, ...omit);
},
});
// --------- 安全加密 API基于 Electron safeStorage---------
contextBridge.exposeInMainWorld('safeStorage', {
/**
* 加密明文字符串
* @param plaintext - 待加密的明文
* @returns Base64 编码的密文,失败返回 null
*/
async encrypt(plaintext: string): Promise<string | null> {
const result = await ipcRenderer.invoke(BIDIRECTIONAL.SAFESTORAGE_ENCRYPT, plaintext);
return result?.success ? result.data : null;
},
/**
* 解密 Base64 密文
* @param encryptedBase64 - Base64 编码的密文
* @returns 解密后的明文,失败返回 null
*/
async decrypt(encryptedBase64: string): Promise<string | null> {
const result = await ipcRenderer.invoke(BIDIRECTIONAL.SAFESTORAGE_DECRYPT, encryptedBase64);
return result?.success ? result.data : null;
},
});
// --------- 本地存储文件 API主进程 fs 桥接)---------
contextBridge.exposeInMainWorld('fileStorage', {
/** 获取 heixiu-data 根目录路径 */
getDataDir(): Promise<string | null> {
return ipcRenderer.invoke(BIDIRECTIONAL.GET_DATA_DIR)
.then((r: { success: boolean; data: string }) => r?.success ? r.data : null);
},
/** 写入文件Base64 → 磁盘),返回写入的绝对路径 */
writeFile(relativePath: string, data: string, encoding?: 'base64' | 'utf8'): Promise<string | null> {
return ipcRenderer.invoke(BIDIRECTIONAL.FILE_WRITE, { relativePath, data, encoding })
.then((r: { success: boolean; data: string }) => r?.success ? r.data : null);
},
/** 读取文件(磁盘 → Base64 */
readFile(relativePath: string): Promise<string | null> {
return ipcRenderer.invoke(BIDIRECTIONAL.FILE_READ, { relativePath })
.then((r: { success: boolean; data: string }) => r?.success ? r.data : null);
},
/** 删除文件 */
deleteFile(relativePath: string): Promise<boolean> {
return ipcRenderer.invoke(BIDIRECTIONAL.FILE_DELETE, { relativePath })
.then((r: { success: boolean }) => r?.success ?? false);
},
/** 递归创建目录 */
makeDir(relativePath: string): Promise<string | null> {
return ipcRenderer.invoke(BIDIRECTIONAL.MAKE_DIR, { relativePath })
.then((r: { success: boolean; data: string }) => r?.success ? r.data : null);
},
/** 在文件管理器中显示文件(绝对路径) */
showItemInFolder(filePath: string): Promise<boolean> {
return ipcRenderer.invoke(BIDIRECTIONAL.SHOW_ITEM_IN_FOLDER, { filePath })
.then((r: { success: boolean }) => r?.success ?? false);
},
/** 获取文件大小(字节) */
fileSize(relativePath: string): Promise<number | null> {
return ipcRenderer.invoke(BIDIRECTIONAL.FILE_SIZE, { relativePath })
.then((r: { success: boolean; data: number }) => r?.success ? r.data : null);
},
/** 检查文件/目录是否存在 */
fileExists(relativePath: string): Promise<boolean> {
return ipcRenderer.invoke(BIDIRECTIONAL.FILE_EXISTS, { relativePath })
.then((r: { success: boolean; data: boolean }) => r?.success ? r.data : false);
},
});
// --------- 应用运行时 API ---------
contextBridge.exposeInMainWorld('appRuntime', {
/**
* 登录/登出后更新窗口标题中的版控后缀
* @param edition - 'personal' | 'enterprise' | nullnull 清除后缀)
*/
setWindowEdition(edition: string | null): void {
ipcRenderer.send(RENDERER_TO_MAIN.SET_WINDOW_TITLE, edition);
},
});