Uninote
Uninote
用户根目录
brdr
common
programming
docs
tests
后端试题
问题讨论

用户数据存储与账号切换规划

返回文档导航

1. 状态与目标

  • 本文是待实施设计,尚未改变应用运行行为。
  • 2026-10-11 已确认:暂不迁移旧数据,保留旧数据,新结构独立启用。
  • 退出登录清除登录凭据、活动账号上下文和界面中已加载的用户数据。
  • 磁盘上保留该账号的笔记、附件、未同步修改、恢复稿、历史缓存和同步检查点。
  • 再次登录同一账号时,先恢复本地副本,再增量同步云端变化。
  • 登录其他账号时,只装载其独立数据,不能把前一账号的本地内容当作待上传内容。

2. 当前实现与缺口

位置 当前行为 需要解决的问题
SyncManager 客户端缓存和仓库同步命名空间包含服务地址、账号;退出撤销远端设备并清除本机凭据和客户端映射 账号切换没有统一卸载工作区;清空映射不等于终止已运行任务
SyncClient.initialize .mynote 内保存身份、基线、队列、合并基准和历史 同步元数据隔离,但根目录的笔记正文和附件仍可被其他账号复用
WorkspaceStateStore 全局 workspace-state.json 混合保存设置、最近仓库、文件和自动同步目标 切换账号后可能沿用旧账号的路径和后台目标
RepositoryFavoriteStore、FavoriteSync 收藏和墓碑按仓库 ID 存入一个全局文件 仓库 ID 不能代替账号归属,尤其是复制同一库目录后
PreferenceSync 同步副本按账号存储,通过 active 标记识别切换;实际设置和快捷键仍应用到全局状态 切换依赖联网同步成功,不能作为离线装载和退出卸载机制
RecoveryStore、RepositoryUiStateStore 恢复稿按路径区分,仓库界面状态按仓库 ID 区分 相同路径或仓库 ID 可能恢复其他账号的草稿和界面状态
localHistoryClient 未登录时可寻找唯一的本地账号历史副本 新设计中退出后不能通过这种推断重新暴露账号历史
logoutSync、registerSyncHandlers 刷新同步界面;退出前等待设置和收藏的 pending 没有覆盖所有窗口、编辑器、文件权限、文件同步和后台任务

现有游标、幂等队列、合并基准、历史分页检查点和稳定设备 ID 可继续使用。无需重写同步协议,也无需建立第二套下载缓存。

3. 数据归属与目录

3.1 账号键

  • 使用规范化服务地址和服务端返回的不可变 accountId 生成 accountKey。
  • accountKey = SHA-256(JSON.stringify([canonicalEndpoint, accountId])),使用完整摘要作为新目录名。
  • 邮箱只用于登录与设备身份查找,不作为账号目录主键。
  • 同一账号在不同服务地址下具有不同目录。
  • 哈希用于稳定命名和隐藏可读标识;账号访问权限由主进程验证。

3.2 建议目录

userData/
  profiles-v2/
    device/
      settings.json                 # 安装级设置,不含用户内容或账号列表
    session/
      credentials.json              # 系统安全存储加密的当前登录凭据
      device-identities.json        # 加密的服务地址、邮箱与稳定设备 ID 映射
    guest/
      workspace-state.json          # 独立的未登录本地工作区
      recovery/
    accounts/
      <accountKey>/
        profile.json                # schemaVersion、服务地址与 accountId
        workspace-state.json        # 本账号最近/打开仓库、路径映射、同步目标
        repository-favorites-v1.json
        repository-ui-state-v1.json
        command-history-v1.json
        preferences.json            # 本账号生效值
        preference-replica.json      # revisions、pending 等同步副本
        keybindings.json
        hotkeybindings.json
        secrets.json                # 本账号第三方服务密钥,系统安全存储加密
        recovery/
        logs/
        libraries/
          <libraryId>/              # 默认的账号专属可写副本
            notes-and-attachments...
            .mynote/
              library.json
              sync-hosted-v1/<namespace>/
                state.json          # cursor、files、queue、deviceId
                merge-bases/
                history-v1/
                logs/
  • profiles-v2 与原有文件并存;不覆盖、不删除、不自动导入旧数据。
  • 复用现有 Store 的目录注入方式,优先把账号目录传给现有 Store,保留其内部文件名和校验规则。
  • 上述设置拆分后的新文件名在实施时统一确定;不要为了目录整齐同时改写全部存储格式。
  • 笔记正文就是本地副本,既包含已同步内容,也可能包含尚未上传的用户创作,不能按普通缓存清理。
  • .mynote 跟随副本保存,保留现有移动目录和离线历史机制。
  • 不同账号可有相同 libraryId,但必须位于不同账号根目录或不同外部副本。

3.3 分类规则

数据 归属 退出后的处理
登录 token、当前会话中的邮箱和账号信息 活动会话 清除本机凭据和内存、界面展示
稳定 deviceId 映射 加密的本机身份索引 保留,不向游客或其他账号列出
最近仓库/文件、打开窗口、收藏、命令历史、草稿、布局 账号 落盘保留,卸载活动实例和界面
可同步设置、快捷键覆盖 账号 保留副本,恢复游客设置或默认值
第三方 API 密钥、含用户内容的片段/预设及收集记录 账号 保留所属账号的数据,卸载密钥和活动内容
笔记、图片、语音等附件、版本正文 账号和仓库副本 保留,不自动下载第二份
cursor、files、queue、合并基准、历史 manifest、删除墓碑 账号和仓库副本 原样保留,恢复时继续使用
窗口大小、系统集成、安装级工具路径等无用户内容的机器配置 设备 可以继续生效
游客工作区和游客草稿 游客 与所有账号独立,不自动上传

本机路径不能跨设备同步,但仍属于账号的本机工作区状态。不能因为“不同步”就归入设备共享数据。

3.4 外部目录与旧数据

  • 默认云端克隆使用账号专属目录;用户仍可选择独立的外部目录。
  • 外部可写副本必须登记账号、仓库与规范化真实路径的绑定。
  • 校验真实路径和链接,阻止通过路径别名把同一物理目录绑定给两个账号。
  • 其他账号打开已绑定目录时,提示使用独立副本,不能自动换绑或继续上传。
  • 游客目录和旧目录不自动归属登录账号;手动关联属于未来单独设计的导入操作。
  • 新结构不扫描旧 .mynote 猜测账号归属,不通过“最后登录邮箱”分配收藏或恢复稿。
  • 用户自行导出和另存的普通文件继续由用户管理,不因退出登录删除。
  • 保留原存储实现;若实施时需要删除旧方案代码,另行取得用户同意。

4. 登录与退出流程

4.1 统一生命周期

主进程持有唯一活动上下文:guest | authenticated(accountKey) | switching,并附带递增的 sessionGeneration。

  • Store、同步任务、窗口授权和账号请求固定绑定其创建时的上下文。
  • 任务不能在 await 后重新取得另一个账号的 Store 并继续写入。
  • 所有账号切换串行执行,切换期间拒绝新的账号读写和同步请求。
  • 异步结果提交到界面前校验账号和 generation;过期结果直接丢弃。
  • 主进程在 IPC 边界检查当前窗口授权,不能信任渲染端提交的账号或任意路径。

4.2 退出登录

  1. 进入 switching,禁止新同步、新文件写入和继续采集用户内容。
  2. 停止文件、设置、收藏定时任务及其新增队列入口。
  3. 在全部窗口保存恢复稿和会话,等待旧上下文中的文件写入、同步任务及日志落盘。
    • 不自动把未保存草稿覆盖到正式笔记。
    • 恢复稿写入失败时保留当前账号和文档,通过应用自定义对话框报告失败。
    • 网络任务需要超时与可取消边界;不能让退出无限等待。
    • 取消后保留可重试的队列和已提交检查点,不假定远端未收到请求。
  4. 在线时尝试撤销远端设备会话;本机退出不能依赖网络成功。
    • 撤销失败仍清除本机 token,明确显示“本机已退出,远端会话未确认撤销”。
    • 不保留 token 作为后台重试凭据;用户可通过其他已登录设备撤销远端会话。
  5. 清除本机登录凭据,卸载旧账号 Store、客户端、索引、搜索结果、预览、差异、历史、密钥和任务状态。
  6. 关闭全部窗口中的账号文档及辅助窗口,撤销对应 FileAccess 授权、文件监听、媒体 URL 和编辑器恢复入口。
  7. 装载游客上下文并广播切换完成,界面只展示游客数据和登录入口。
  • 保留磁盘账号目录及所有副本,不运行缓存清理或回收站操作。
  • 已经提交给远端的操作不能撤回;退出流程保证这些操作始终属于旧账号。
  • 广播前禁止旧窗口继续读写;单纯清空同步中心不足以完成退出。
  • 凭据删除失败时不能提示退出成功,保持 switching 并提供重试。
  • 过期会话、明确 401 和远程撤销当前设备同样进入卸载流程,避免留下可读的旧界面。
  • 暂时离线且本机会话仍有效时继续使用该账号离线副本,不误判为退出。

4.3 登录

  1. 在临时认证请求中校验输入,认证失败不改变当前活动目录、设置或登录凭据。
  2. 登录成功后,用服务端 accountId 确定账号目录;同一安装复用现有稳定 deviceId。
  3. 若已有其他活动账号,先完成其统一卸载;不复用其工作区和设置实例。
  4. 从目标账号目录加载设置、收藏、最近记录、恢复稿和本地副本;新账号使用默认值。
  5. 验证 profile 身份、仓库绑定和本机凭据一致,再发布 authenticated 上下文。
  6. 优先展示本地副本;后台刷新远端库列表,并对已有副本运行增量同步。
  • 登录后立刻断网,已有副本仍可使用;新账号不能退回前一账号的缓存。
  • 尚未下载的远端库只展示入口,沿用显式克隆交互,避免登录后下载所有仓库。
  • 重启时先完成会话校验和上下文选择,再恢复窗口,不能先打开最后一个账号的笔记再判断登录状态。
  • 已退出时,即使只有一份账号历史缓存,也不能自动提供给游客;对应离线历史入口随上下文收紧。

5. 缓存复用与增量同步

5.1 原样复用的检查点

  • files:上次接受的本地/远端基线。
  • cursor:已成功应用的远端 revision。
  • queue:未得到持久确认的本地操作及稳定操作 ID。
  • merge-bases:识别双方修改并进行合并的基准。
  • history-v1:已连续缓存的历史元数据、正文和分页检查点。
  • 收藏墓碑、设置 revisions/pending:防止删除复活或覆盖未上传设置。

退出、关闭窗口和重新登录均不能重建这些检查点。复用前须验证账号、仓库与设备身份;设备 ID 被服务端替换时显式处理副本身份不匹配,不静默丢弃队列或全量重置。

5.2 再次登录示例

  • A 退出时,本地 cursor = 120,历史已缓存到 revision 120,仍有两项未确认上传。
  • A 离线期间云端增加到 revision 125,B 登录使用 B 的独立目录。
  • A 再次登录时立即显示 A 的本地内容,并从 120 继续请求远端变化。
  • 两项本地操作使用原操作 ID 重试;冲突继续沿用现有处理规则,并保护未保存恢复稿。
  • 只补齐新增或缺失的历史 revision,不重复获取 1–120 的完整历史。
  • 未改变的正文和附件不重复下载;必要的账号校验、库列表和变更元数据请求仍会发生。

继续使用现有协议和同步状态机。本文不承诺零网络请求,也不把一次登录当作必须全量重建副本的理由。

5.3 失败和删除边界

  • 缓存损坏时保留诊断副本,只重建必要部分;不把丢失基线直接解释成远端文件删除。
  • 云端仓库已删除时保留本地内容,标记不可继续同步,不自动重新创建同 ID 的远端库。
  • 用户主动清除某账号本地数据属于独立操作,需要明确处理未上传修改;普通退出不执行此操作。
  • 保留的普通磁盘文件仍可被有相应操作系统权限的人读取;本次目标是应用内账号隔离,不是磁盘内容加密。

6. 实施顺序与影响范围

阶段 改动范围 完成条件
1. 账号存储根与生命周期 src/main.ts、SyncManager、Store 构造入口、凭据和设备身份存储 新结构独立启动;身份校验、目录绑定、切换锁和 generation 生效
2. 完整卸载与装载 工作区、窗口恢复、FileAccess、恢复稿、收藏、设置/快捷键、命令历史、密钥、片段及辅助窗口入口 所有带用户内容的服务按上下文创建或停用,退出后无旧账号可见入口
3. 同步接入 sync-handlers、调度器、PreferenceSync、FavoriteSync、SyncClient、离线历史入口 暂停和恢复任务,沿用副本及检查点,禁止跨账号路径上传
4. 界面和验证 预加载 IPC、登录/退出与多窗口渲染、命令面板及测试夹具 自定义确认、错误展示和账号切换一致;各入口共用同一操作
  • 各阶段可分提交开发,但前三阶段及安全回归完成前,不开放给用户使用账号切换新结构。
  • 优先复用现有 Store、文件校验、原子写入、安全凭据和同步实现;不引入数据库或跨账号共享内容池。
  • 测试夹具需提供两个账号、可碰撞的仓库 ID、独立目录和可延迟的远端响应。
  • 代码实施后在 FEATURES.md 更新已交付行为,在 sync-flow.md 更新调用链;设计文档不提前改写现有功能说明。
  • 主进程和预加载层改动部署后需要重启桌面应用,服务端协议预计无需变化。

7. 验收清单

场景 必须满足的结果
A → 退出 → B B 看不到 A 的笔记、收藏、最近记录、草稿、历史、密钥、日志和搜索结果
B 与 A 使用同一仓库 ID 仍使用不同副本、收藏和状态,不能互相上传
B 选择 A 的物理目录或路径别名 主进程拒绝跨账号绑定,不改写 A 的文件
A → 退出 → A 恢复原副本,保留稳定 deviceId、queue、cursor 和历史检查点
A 离线期间云端新增内容 A 登录后只补增量;远端请求计数证明未重取已缓存历史和未变正文
退出时存在未上传编辑或草稿 文件和草稿保留到 A 的目录,B 不恢复、不上传这些内容
登录成功后断网 装载目标账号本地副本;新账号无缓存时展示空状态
登录失败或账号认证响应延迟 不切换当前账号,过期响应不覆盖较新的会话
同步、收藏、设置 GET/PUT 进行中退出 新任务停止,旧任务不会写入新账号状态或刷新新窗口
多窗口及辅助窗口 所有账号文档同时卸载,旧 IPC、文件授权、媒体和采集流程失效
401、设备撤销、离线退出 进入正确的卸载流程,明确区分本机退出和远端撤销结果
退出过程中落盘失败或应用崩溃 不丢未保存内容;重启按有效凭据恢复单一上下文,不显示错账号数据
保留旧数据的新版本启动 旧文件不变,新目录独立,既不自动导入也不自动上传
历史缓存损坏、设备身份变化、远端库删除 保留可恢复内容,不误删云端或静默清空待上传队列

验证分为 Store/生命周期单元测试、双账号同步集成测试和真实 Electron 多窗口冒烟测试。退出保留性检查应比较关键文件内容与检查点,不只检查目录是否存在。

README

admin-api

点赞(0) 阅读(15) 举报
目录
标题