设置存储

带版本迁移的设置存储、设置中心界面、备份与安全存储。

用户偏好以带版本的 JSON 文件形式存放在主进程中。渲染进程只能通过 Conveyor 读写 它们,设置中心界面是一组可搜索的标签页,而秘密从不触及这个存储——它们改走 safeStorage

存储

lib/main/settings.tsSettingsStore)掌管 Electron userData 目录下的 settings.json。它以带版本的信封形式写入:

{
  "version": 10,
  "settings": { "theme": "dark", "locale": "system", "...": "..." }
}
操作系统位置
macOS~/Library/Application Support/<AppName>/settings.json
Windows%APPDATA%\<AppName>\settings.json
Linux~/.config/<AppName>/settings.json

每个字段都由 appSettingsSchemalib/conveyor/schemas/app-schema.ts)定义, 并在读写时校验,因此手动编辑或损坏的文件会回退到默认值,而不会让应用崩溃。

向前迁移

存储以一串向前迁移进行版本管理。CURRENT_SETTINGS_VERSION 是目标版本; SETTINGS_MIGRATIONS 中每一项把某个版本升级到下一个。加载时,migrateForward 从文件版本一路运行到当前版本的每个迁移器,随后重新校验:

// lib/main/settings.ts
export const CURRENT_SETTINGS_VERSION = 10

const SETTINGS_MIGRATIONS = {
  // ...
  7: (v7) => ({ ...v7, accentColor: 'default' }),
  8: (v8) => ({ ...v8, onboardingCompleted: false, eulaAcceptedVersion: 0 }),
  9: (v9) => ({ ...v9, zoomLevel: 0 }),
}

新增一个设置是一次小而机械的改动:

把字段加入 schema

app-schema.ts 中扩展 appSettingsSchemaappSettingsPatchSchema,并把它 加入 DEFAULT_SETTINGS

提升版本并添加迁移器

递增 CURRENT_SETTINGS_VERSION,并添加一个 SETTINGS_MIGRATIONS 项,在上一个 载荷之上补种新字段。

update() 中合并

添加 if (patch.<field> !== undefined) 一行,让渲染进程能写入它。

比应用更新的文件会被原样保留,存储回退到默认值,而不是悄悄降级它看不懂的数据。 一个遗留的无版本文件会被视作 v1 并向前迁移。

设置中心

app/routes/SettingsPage.tsx 以标签页形式渲染设置中心。共有八个标签页; 账户(Account)是它自己的页面(/account),并非设置标签页。

标签页Section 文件控件
通用GeneralSection语言、开机自启、最小化到托盘
外观AppearanceSection主题、强调色
通知NotificationsSection原生通知开关
快捷键ShortcutsSection键盘快捷键列表
代理ProxySection网络代理模式 + URL
存储StorageSection数据目录、备份导出/导入
更新UpdatesSection自动更新开关、更新通道(受标志开关控制
高级AdvancedSection错误上报、分析、恢复默认

更新标签页仅在 features.autoUpdate 开启时出现,因此剥离了自动更新的构建 不会留下孤立的标签页。

设置搜索

settings-search.ts 是一份逐项控件的小索引,每一项都指向其所属标签页。 SettingsSearch 用查询词去匹配已翻译的标签、描述与关键词,因此用户无需知道 某控件在哪个标签页也能跳转过去。隐藏的标签页(例如标志关闭时的"更新")会被排除 在结果之外。同一份索引也为命令面板的"设置"分组供数——参见 界面与页面

use-settings-form 模式

use-settings-form.ts 通过 Conveyor 加载一次设置,并暴露 updateSettings / resetSettings。每个 section 接收 settingsupdateSettings,并写入一个 局部补丁。端到端添加一个控件:

  1. 把字段加入存储(见上文)。
  2. 在正确的 section 中渲染控件,调用 updateSettings({ field })
  3. 添加一条 settings-search.ts 记录,使其可被搜索并出现在命令面板中。

备份

lib/main/backup.ts 驱动存储标签页中的导出/导入(app-export-data / app-import-data Conveyor 通道)。一份备份是一个 JSON 文件,包含你的设置外加 一份设备上存在哪些安全键的清单

备份从不包含解密后的秘密。Supabase 会话、令牌与许可证密钥通过 safeStorage 绑定到设备;以明文导出它们会破坏操作系统的加密。在新设备上恢复意味着重新登录 以重建它们。

导入用 parseBackup(Zod)校验文件,并通过 settingsStore.restore() 恢复设置, 重新应用托盘与更新器偏好。无效文件会返回一个干净的错误,而非恢复出垃圾数据。

安全存储

lib/main/secure-storage.tsSecureStorage)是设置存储的加密对应物,由 Electron 的 safeStorage(基于操作系统钥匙串的加密)支撑。它持久化到 userData 下的 secrets.bin,以 0o600 权限写入。渲染进程通过 secure Conveyor 领域 (window.conveyor.secure.get/set/delete)访问它。

已知的安全键是一个固定枚举:

// lib/conveyor/schemas/secure-schema.ts
export const secureKeySchema = z.enum([
  'access-token', 'refresh-token', 'supabase-session', 'license-key',
])

实践中,本模板在此存放 Supabase 会话——关于为什么 localStorage 在桌面端不够 用,参见认证

当加密不可用时(例如没有钥匙串的 Linux 机器),写入会抛出一个类型化的 SecureStorageError,错误码为 ENCRYPTION_UNAVAILABLE,而不是悄悄写入明文。 调用方会将其表现为一次无法跨重启保留的登录;参见 疑难排查

相关页面

On this page