存储插件引入与操作逻辑
以S3插件安装为例
安装插件
- 从仓库源安装s3插件
- 保存后存储源对s3插件生效
- 配置s3参数设置,点击保存生效插件
- 参数保存位置:
| 数据 | 保存位置 |
|---|---|
| 存储源配置 | 桌面端配置目录下的 storage-plugin-sources.json |
| Access Key、Secret Key、OAuth 等敏感凭据 | 操作系统凭据存储,不写入 JSON |
| 插件程序和 manifest | storage-plugins/ |
| 照片元数据 | 服务端 PostgreSQL 的 Photo 表 |
| 上传成功但尚未登记的任务 | pending-storage-registrations.json |
上传照片
- 选择照片
- 选择存储源(s3插件)
- 上传照片
云端照片(S3)限制
- 无法对通过插件上传的照片移动、删除,因为这涉及到对应存储源的操作,但可以修改其他不涉及到对存储源的操作,如相册、类型、备注等信息
插件交互方面
- 安装插件后,上传以及其他在使用插件的过程中,不得对插件进行操作,避免数据损坏
- 安装插件后,允许卸载插件,但若重新安装插件时,其配置信息可以恢复
考虑场景
官方存储源配置参数变更
桌面端配置目录由 /D:/Projects/mo-gallery/mo-gallery-web/desktop/config/config.go:313 决定:
- Windows:
%APPDATA%\mo-gallery-desktop - macOS:
~/Library/Application Support/mo-gallery-desktop - Linux:
~/.config/mo-gallery-desktop
所以 Windows 上通常是:
%APPDATA%\mo-gallery-desktop\storage-plugin-sources.json
其中保存的是类似下面的非敏感信息:
{ "id": "source-uuid", "name": "Official S3", "pluginId": "com.example.s3", "pluginVersion": "1.2.0", "config": { "endpoint": "https://s3.example.com", "bucket": "photos", "region": "auto", "basePath": "gallery/" }, "credentialRefs": { "accessKey": "..." }, "enabled": true }真正的密钥不会出现在这个文件里。代码会把凭据保存为类似:
mo-gallery-desktop/storage/<sourceId>/<credentialName>
的引用。Windows 当前使用 Windows Credential Manager;其他平台的凭据存储目前还没有完整实现。
因此,官方更换存储源参数时:
- endpoint、bucket、region、basePath 等非敏感参数在
storage-plugin-sources.json中更新; - Access Key、Secret Key 等需要重新写入系统凭据存储;
- 只要 source ID、Bucket 和对象 key 空间不变,照片的 Photo 记录和远程文件都不需要迁移;
- 如果换了 Bucket 或 key 规则,就必须迁移远程对象,并更新服务端 Photo.storageSourceId、storageKey 等字段。
还要注意:插件 manifest 中的 configSchema 只是描述“配置长什么样”,不是用户实际配置的存储位置。官方发布新版本时,应提供旧配置到 新配置的迁移逻辑;不能因为字段名改变,就直接丢弃用户本地的 source 配置。
另外,备份时不能只备份 storage-plugin-sources.json。它只包含凭据引用,不包含凭据值;完整恢复还需要同时处理操作系统凭据存储。
和StorageSource数据表的联系
- Web 存储源:服务端 StorageSource 表
prisma.schema 中的 StorageSource 表保存:
- type:local、github、s3
- endpoint、bucket、region、basePath
- 加密后的 Access Key、Secret Key
- public URL 等配置
当照片的:
storageRuntime = "web"
并且有 storageSourceId 时,服务端会把这个 ID 当作 StorageSource.id,通过 /D:/Projects/mo-gallery/mo-gallery-web/server/lib/ storage/config.ts:96 查询配置。
所以 Web S3 官方源修改参数时,直接更新同一条 StorageSource 记录即可。只要 Bucket 和 object key 没变,已有照片不需要修改。
- Desktop 插件存储源:桌面本地配置
当照片的: storageRuntime = "desktop-plugin"时,storageSourceId 保存的是桌面端插件 source 的 UUID。这个 UUID 对应的是:
%APPDATA%\mo-gallery-desktop\storage-plugin-sources.json中的 source,而不是服务端 StorageSource 表中的记录。
桌面端通过本地插件管理器解析这个 source,再从操作系统凭据存储中读取密钥。服务端只保存照片元数据,例如:
storageRuntimestoragePluginIdstorageSourceIdstorageKeythumbnailStorageKeyurlthumbnailUrl服务端不会保存 Desktop Credential Manager 中的实际凭据,也不能依靠 StorageSource 表去启动桌面插件。文档已经明确说明:桌面插件的storageSourceId 可以写入服务端,但不能假设它对应 StorageSource 行。
当前 Photo.storageSourceId 没有 Prisma 外键,原因就是它同时兼容两种运行时:
| storageRuntime | storageSourceId 的含义 |
|---|---|
| web | 服务端 StorageSource.id |
| desktop-plugin | 桌面本地插件 source UUID |
因此:
- 修改 Web 官方源:更新 PostgreSQL 的 StorageSource 行。
- 修改 Desktop 官方源:更新桌面本地
storage-plugin-sources.json和系统凭据。 - 两者都应尽量保持 source ID 不变。
- 不要为了“同步”而给每个桌面插件 source 在服务端 StorageSource 表里创建一条镜像记录,这会造成两套配置和凭据不一致。
- 如果删除并重建 source 导致 ID 改变,就需要批量迁移照片的 storageSourceId;否则旧照片仍会指向原 source。
所以 StorageSource 表保存的是 Web 端的连接配置,不是所有插件存储源的总表,更不是照片文件本身的存储位置。照片文件仍然在 S3/R2 等远程 Bucket 中。
逐条核对
- 安装:一致。必须先装插件(生产模式要求签名 verified)才能建存储源(desktop/storage_plugins/manager.go:401)。小修正:「保存」 只是落库,不会真正启动插件/校验配置;要等「连接测试」(TestSource → source.validate) 或首次上传时插件进程才启动生效。
- 上传:一致。补充两点:插件上传走「原图+缩略图双对象」,按 fileHash+sourceID+key 幂等;对象上传成功但登记失败会进 pending- storage-registrations.json 等待重试,不是直接失败。
- 云端限制:方向对,但要精确化。
- Web 端确实拒绝对象操作:删除带 deleteOriginal/deleteThumbnail、移动(改 storagePath)、重上传、缩略图生成、颜色重分析,都 返回 409(hono/photos.ts:1246、:1617、:1861、:1914、:2061)。
- 但「无法删除」只针对对象:Web 端仍可「仅解除关联」(两者都不勾时只删元数据,hono/photos.ts:1275);桌面端在插件在位时更是 可以正常「删对象+删元数据」(desktop/app.go:519)。
- 相册/类型/备注等纯元数据修改不受限,这条是对的。
- 补充:reanalyze-colors 也被拒,因为要重新读对象——这类「看着像元数据、实际碰对象」的操作需要归入限制范围。
- 交互 1「使用中不得操作插件」:目前只是约定,没有强制。安装/升级路径不会先停 runtime(InstallPluginPackage → commitInstalledPackage 直接换目录,Windows 上可能因文件占用失败或旧进程继续跑旧代码);回滚会停 runtime 但不检查是否有正在上 传的任务;卸载/删 source 也不检查 in-flight 上传和 pending 任务。要落实这条,需要在安装/升级/回滚/卸载前检查插件是否 busy(有 上传任务则拒绝或排队)。 与你的预期不符的一点
- 「允许卸载,重新安装时配置可恢复」:当前不满足。卸载前必须删掉所有 source;删 source 会同时删除配置(sources.json)和凭据 (desktop/storage_plugins/manager.go:452 → registry.remove → deleteSourceCredentials)。重新安装插件后,存储源配置和凭据都不 会自动恢复,用户要重建 source、重录凭据。
- 要满足你的要求,二选一:
- 卸载时保留 source 配置(标记为禁用/孤儿,不删凭据),重装后自动恢复;或
- 提供「插件与存储源配置导出/导入」,凭据只导出引用、恢复时重录(凭据值本身不进导出文件)。 其他建议补充
- 卸载前生成「受影响对象清单」:pending 登记任务 + 引用该插件的云端 Photo 数,确认后再删,避免远端对象变孤儿。
- 安装/卸载/回滚补审计日志(目前照片/故事有日志,插件操作没有)。
- 插件级「启用/停用」缺失(spec 承诺过):停用比卸载安全,可保留全部数据。
- 升级后 configSchema 可能变化,旧配置可能不被新版本接受;升级前应提示导出配置,升级后强制 re-validate。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!