版控体系重构(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 新增已完成项
82 lines
3.2 KiB
TypeScript
82 lines
3.2 KiB
TypeScript
// ============================================================
|
|
// safe-storage-ipc — safeStorage 加密/解密 IPC 处理器
|
|
//
|
|
// 在主进程注册,渲染进程通过 preload 暴露的安全方法调用。
|
|
// 加密后的数据为 Base64 字符串,方便存储在 localStorage。
|
|
//
|
|
// 平台对应:
|
|
// Windows → DPAPI
|
|
// macOS → Keychain
|
|
// Linux → libsecret
|
|
//
|
|
// 注意:
|
|
// - safeStorage 加密绑定当前 OS 用户账户,无法跨用户/跨设备解密
|
|
// - 用户重装 OS 后加密数据将无法恢复(需重新登录)
|
|
// - safeStorage.isEncryptionAvailable() 在部分 Linux 发行版可能返回 false
|
|
// ============================================================
|
|
|
|
import { ipcMain, safeStorage } from 'electron';
|
|
import { BIDIRECTIONAL } from '../../../shared/constants/ipc-channels';
|
|
import { logger } from '../logger';
|
|
|
|
/** 检查 safeStorage 是否可用,不可用时记录警告 */
|
|
function checkAvailability(): boolean {
|
|
if (!safeStorage.isEncryptionAvailable()) {
|
|
logger.warn('safe-storage', 'safeStorage 加密不可用(可能缺少 libsecret 等系统密钥服务)');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 注册 safeStorage IPC 处理器
|
|
* 在 app.whenReady() 中调用
|
|
*/
|
|
export function registerSafeStorageIpcHandlers(): void {
|
|
// ---------- 加密 ----------
|
|
ipcMain.handle(BIDIRECTIONAL.SAFESTORAGE_ENCRYPT, (_event, plaintext: string) => {
|
|
if (typeof plaintext !== 'string' || plaintext.length === 0) {
|
|
logger.warn('safe-storage', '加密参数无效');
|
|
return { success: false, error: '参数无效:需要非空字符串' };
|
|
}
|
|
|
|
if (!checkAvailability()) {
|
|
return { success: false, error: 'safeStorage 不可用' };
|
|
}
|
|
|
|
try {
|
|
const encrypted = safeStorage.encryptString(plaintext);
|
|
const base64 = encrypted.toString('base64');
|
|
return { success: true, data: base64 };
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
logger.error('safe-storage', '加密失败', err instanceof Error ? err : new Error(message));
|
|
return { success: false, error: message };
|
|
}
|
|
});
|
|
|
|
// ---------- 解密 ----------
|
|
ipcMain.handle(BIDIRECTIONAL.SAFESTORAGE_DECRYPT, (_event, encryptedBase64: string) => {
|
|
if (typeof encryptedBase64 !== 'string' || encryptedBase64.length === 0) {
|
|
logger.warn('safe-storage', '解密参数无效');
|
|
return { success: false, error: '参数无效:需要非空字符串' };
|
|
}
|
|
|
|
if (!checkAvailability()) {
|
|
return { success: false, error: 'safeStorage 不可用' };
|
|
}
|
|
|
|
try {
|
|
const buffer = Buffer.from(encryptedBase64, 'base64');
|
|
const plaintext = safeStorage.decryptString(buffer);
|
|
return { success: true, data: plaintext };
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
logger.error('safe-storage', '解密失败', err instanceof Error ? err : new Error(message));
|
|
return { success: false, error: message };
|
|
}
|
|
});
|
|
|
|
logger.info('safe-storage', 'safeStorage IPC 处理器已注册');
|
|
}
|