docs/architecture/storage-runtime-architecture.md
本文档用于说明当前仓库在 Web / Extension / Desktop 三种运行环境下的真实存储结构,重点回答以下问题:
本文档描述的是 当前运行态事实,不是理想设计图。
当前系统必须严格区分两类数据:
结构化业务数据
图片二进制资产
这条边界是本轮存储事故复盘后的第一原则。
DexieStorageProviderPromptOptimizerDBstorage相关文件:
packages/core/src/services/storage/dexieStorageProvider.tsFileStorageProvider<userData>/prompt-optimizer-data.json<userData>/prompt-optimizer-data.json.backup相关文件:
packages/core/src/services/storage/fileStorageProvider.tspackages/desktop/main.jsImageStorageServicePromptOptimizerImageDB表结构分为两张表:
imageMetadataimageData相关文件:
packages/core/src/services/image/storage.tspackages/ui/src/composables/system/useAppInitializer.tsImageStorageServicePromptOptimizerFavoriteImageDB表结构同样分为两张表:
imageMetadataimageData相关文件:
packages/core/src/services/image/storage.tspackages/ui/src/composables/system/useAppInitializer.ts代码里还存在两个 provider,但不是当前线上主路径:
LocalStorageProvider
5MBMemoryStorageProvider
相关文件:
packages/core/src/services/storage/localStorageProvider.tspackages/core/src/services/storage/factory.ts虽然 Web 物理上只有 PromptOptimizerDB.storage 一张表,Desktop 物理上只有一个 JSON 文件,但逻辑上可以拆成以下几块。
pref:* 命名空间由 PreferenceService 统一管理。逻辑 key 会被自动加上 pref: 前缀后再落盘。
示例:
global-settings/v1 -> pref:global-settings/v1session/v1/basic-system -> pref:session/v1/basic-systemvariableManager.storage -> pref:variableManager.storage相关文件:
packages/core/src/services/preference/service.ts这些 key 不经过 PreferenceService,直接写入主存储:
modelsimage-modelsuser-templatesprompt_history相关文件:
packages/core/src/constants/storage-keys.ts上下文不是按多个 key 分散存,而是集中在一个文档里:
ctx:store这个文档同时保存:
相关文件:
packages/core/src/services/context/constants.tspackages/core/src/services/context/repo.ts收藏系统也是独立 key,不经过 PreferenceService:
favoritesfavorite_categoriesfavorite_statsfavorite_tagsfavorite_categories_initialized相关文件:
packages/core/src/services/favorite/manager.ts当前仓库仍存在一批历史 key 和新快照并存的情况,包括但不限于:
global-settings/v1app:settings:ui:function-modeapp:settings:ui:basic-sub-modeapp:settings:ui:pro-sub-modeapp:settings:ui:builtin-template-language需要特别注意:
imageSubMode 现在以路由为真源,不再直接依赖 preference 持久化global-settings/v1相关文件:
packages/ui/src/stores/settings/useGlobalSettings.tspackages/ui/src/composables/mode/useFunctionMode.tspackages/ui/src/composables/mode/useBasicSubMode.tspackages/ui/src/composables/mode/useProSubMode.tspackages/ui/src/composables/mode/useImageSubMode.tspackages/ui/src/components/app-layout/PromptOptimizerApp.vue各功能区会话存储在 pref:session/v1/* 下,包括:
这些快照保存的是结构化 JSON,例如:
相关文件:
packages/ui/src/stores/session/useSessionManager.tspackages/ui/src/stores/session/useBasicSystemSession.tspackages/ui/src/stores/session/useBasicUserSession.tspackages/ui/src/stores/session/useProMultiMessageSession.tspackages/ui/src/stores/session/useProVariableSession.tspackages/ui/src/stores/session/useImageText2ImageSession.tspackages/ui/src/stores/session/useImageImage2ImageSession.tspackages/ui/src/stores/session/useImageMultiImageSession.ts会话中真正的图片字节不应该放进 session 快照,而是写入 PromptOptimizerImageDB。
session 快照里应该只保留:
assetIdimage-ref图片写入和引用转换相关文件:
packages/ui/src/utils/image-asset-storage.tspackages/ui/src/stores/session/imageStorageMaintenance.ts收藏项本体仍保存在结构化主存储中的 favorites key 下。
其中包含:
注意:
favorites 不是 pref:* 命名空间的一部分PreferenceService 针对 session 的大小防线保护相关文件:
packages/core/src/services/favorite/manager.ts收藏相关图片应该写入 PromptOptimizerFavoriteImageDB,然后在收藏 metadata 中保存引用信息:
coverAssetIdassetIdsimageAssetIdsinputImageAssetIds相关文件:
packages/ui/src/utils/favorite-media.tspackages/ui/src/components/SaveFavoriteDialog.vuepackages/ui/src/composables/app/useAppPromptGardenImport.ts当前收藏元数据 schema 仍允许 URL 型 fallback:
coverUrlurls这意味着当前系统允许:
但现在已经明确禁止:
data:image/...;base64,... 这种 inline 数据 URL 被写入 favorite metadata相关文件:
packages/ui/src/utils/favorite-media.tspackages/core/src/services/favorite/manager.tsPrompt Garden 导入链路已经改成严格模式:
media fallback相关文件:
packages/ui/src/composables/app/useAppPromptGardenImport.ts这几类数据都属于结构化主存储:
modelsimage-modelsuser-templatesprompt_historyctx:store相关文件:
packages/core/src/services/model/manager.tspackages/core/src/services/template/manager.tspackages/core/src/services/history/manager.tspackages/core/src/services/context/repo.tsPreferenceService 现在对 session/* key 建立了硬限制:
1 MiB限制覆盖:
超限行为:
SessionManager 会清理超限的单个 session key,而不是清空整库相关文件:
packages/core/src/services/preference/service.tspackages/ui/src/stores/session/useSessionManager.tsPromptOptimizerImageDB 默认配额:
maxCacheSize = 50 MBmaxAge = 7 天maxCount = 100autoCleanupThreshold = 0.8清理顺序:
相关文件:
packages/core/src/services/image/storage.tspackages/ui/src/composables/system/useAppInitializer.tsPromptOptimizerFavoriteImageDB 默认配额:
maxCacheSize = 200 MBmaxAge = 365 天maxCount = 1000autoCleanupThreshold = 0.9这套配置比 session 图片库更宽松,因为收藏被视为长期保留资产。
相关文件:
packages/core/src/services/image/storage.tspackages/ui/src/composables/system/useAppInitializer.tsprompt_history 最多保留:
50 条记录相关文件:
packages/core/src/services/history/manager.tsDesktop 主存储没有应用层固定大小上限,但有运行时写盘策略:
500ms3s这意味着 Desktop 不是“无限安全”,只是没有像 session 那样的显式 size cap。
相关文件:
packages/core/src/services/storage/fileStorageProvider.tsLocalStorageProvider 报告的能力上限约为:
5MB但它不是当前主线路径。
相关文件:
packages/core/src/services/storage/localStorageProvider.ts当前 DataManager.exportAllData() 导出的不是整套运行态存储,而是一个经过裁剪的业务数据集合。
当前导出包含:
historymodelsuserTemplatesuserSettingscontexts当前不包含:
favoritesfavorite image assetssession snapshotssession image assets因此必须明确:
相关文件:
packages/core/src/services/data/manager.tspackages/core/src/services/preference/service.tssession/* 超过 1 MiB 直接拒绝data:image/... inline data URL 进入 metadata无论在 Web 还是 Desktop,本质上都还是单一主存储容器:
这意味着只要某条链路把大对象写错位置,影响范围就不是单个业务模块,而是整个主存储。
favorites 是直接 key,不走 PreferenceService,因此:
1 MiB 防线不覆盖 favorites已确认:
favorites 文本记录PromptOptimizerFavoriteImageDB 孤儿资产的强引用回收逻辑这意味着当前 favorite 图片库仍存在孤儿资产积累风险,更多依赖配额清理,而不是引用级删除。
相关文件:
packages/ui/src/stores/session/imageStorageMaintenance.tspackages/ui/src/components/FavoriteButton.vuepackages/ui/src/components/FavoriteManager.vuepackages/core/src/services/favorite/manager.ts当前存在:
global-settings/v1app:settings:ui:*这不一定立即导致数据损坏,但会提高认知成本,也会增加后续修改时误写双真源的风险。
当前导出文件不能还原:
这也是为什么“运行时数据完整性”和“导出文件完整性”必须分开讨论。
后续所有功能开发必须遵守以下规则:
禁止把以下内容直接写入主存储:
所有图片相关能力都必须遵循:
assetId 写入业务对象允许的 fallback:
禁止的 fallback:
data:image/... 写回 favorites 或 session metadata如果一个对象持有图片 assetId,那么删除该对象时必须明确以下策略之一:
不能只删文本记录,不考虑资产生命周期。
仅验证“调用链路经过某个 adapter”是不够的。
必须补齐:
目标:
建议:
PreferenceServiceFavoriteManager 或更底层 provider 边界增加单项和总量限制建议逐步统一:
global-settings/v1 主导建议至少覆盖:
当前系统的真实运行时存储结构可以总结为:
结构化主存储
Session 图片资产库
Favorite 图片资产库
当前已经建立了两条关键防线:
session/* 的 1 MiB 硬限制但要彻底避免同类事故再次发生,还需要继续补齐:
只有当“结构化数据”和“图片资产”在架构、代码、测试、删除回收四个层面都被强制分层后,这类问题才算真正从根上解决。