存储 descriptor 与字段规范化契约
本文档记录 AsterDrive 当前 storage policy connector descriptor 与 remote storage target driver descriptor 的开发约定。它是给后端和前端贡献者看的契约,不是用户手册。
当前有两类相近但不等价的 descriptor:
src/storage/connector_descriptor.rs与src/storage/connectors/:描述 storage policy 管理表单、连接测试、授权、policy action、上传工作流和 connector 能力。src/services/remote/storage_target/driver.rs:描述 follower remote storage target 可用的 driver、字段和本地归一化规则。
这两类 descriptor 可以共享命名和字段语义,但不要强行合成一个万能 descriptor。storage policy 描述的是主控侧策略和上传/下载工作流;remote storage target 描述的是 follower 接收远端写入时的落点配置。
共享字段语义和纯规范化 helper 放在 src/storage/field_contract.rs。产品侧 descriptor DTO 继续保持独立。
Descriptor 规则
Section titled “Descriptor 规则”- 管理端字段、动作、能力和 UI 辅助元数据必须优先来自后端 descriptor。前端不能用本地
driver_type白名单或矩阵推断连接测试、授权、上传策略、原生处理、远端绑定、字段可见性等能力。 label_key、help_key、placeholder、required_message_key和类似字段只表达稳定的本地化 key 或提示参数。具体文案由前端 i18n 决定,但字段是否存在、是否必填、是否敏感由后端 descriptor 决定。secret: true或 secret kind 表示前端必须使用敏感输入控件,后端日志和Debug输出不得打印明文。创建流程按 descriptor 的required和后端校验执行;编辑流程中,省略 secret 字段表示保留已有值,显式提供新值才替换。StorageConnectorFieldScope::PolicyOptions字段属于storage_policy.options,前端必须按 descriptor 渲染和归一化,不允许为某个 driver 额外写本地字段矩阵。SFTP 的sftp_host_key_fingerprint就是这种字段:后端声明字段、label key、trim 行为和校验规则,前端只负责展示与提交。- 不支持的 driver 必须由后端返回稳定错误。remote storage target 只能暴露已注册且远端 capability 声明支持的 known driver;未知 driver id 可以在 wire model 中保留,但不能被前端当成本地可配置 driver。
- descriptor 缺失、远端 capability 缺失或解析失败时,前端只能做保守兜底:隐藏高风险动作或显示不可用状态。不能在兜底路径里恢复本地 capability 矩阵。
- action descriptor 用来声明入口是否需要 saved policy、授权 credential,以及是否会修改远端状态。路由和 service 仍要做最终校验,不能只靠前端隐藏按钮。
- action 返回的结构化
output只允许按 descriptor 的output_fields临时展示;未声明字段、类型不匹配字段和其他 action 的结果一律忽略。输出不写回 policy config,也不跨 action 或对话框会话保存。 - delegated credential 的状态文案、语义色、说明、需要管理员关注的提示、净化后的原因映射、生命周期标签、重新授权文案和 redirect URI 辅助信息都由
credential_management描述。共享面板不得按 OneDrive、Microsoft Graph 或其他 provider ID 分支,也不得直接展示 wirestatus_reason。
文档事实投影
Section titled “文档事实投影”- 内置 connector 的身份、展示名称、
deployment_scope、credential_mode、capabilities和upload_workflows仍由运行时 descriptor/localization 唯一拥有,文档层不建立平行 capability 类型。 tests/storage_connector_docs.rs通过管理员实际消费的 catalog API 生成docs/generated/storage-connectors.json,并更新后端索引、策略 catalog 和能力矩阵中的 marker block。生成产物提交到仓库,和代码一起审查。- 教程 slug 和“适合场景”是 provider-owned 文档元数据,不属于 runtime capability。新增内置 connector 时必须显式补齐这两个字段和中英文教程;测试会拒绝缺项或不存在的教程路径。
- capability matrix 展示 connector 的静态能力上限。策略选项、远端节点 transport 和部署网络仍可能收窄实际能力,provider 教程负责解释这些条件。
- README、部署文档和教程中的 provider 名称是明确标注的非穷举示例,不作为 backend catalog;新增 connector 不要求替换所有上下文示例。
字段规范化规则
Section titled “字段规范化规则”字段规范化属于 backend use case 或 connector/driver-specific pure helper,不能散落在 handler 或前端组件里。
- local remote storage target
base_path使用normalize_relative_local_path:trim 空白,支持.当前目录归一化,拒绝空值、绝对路径、..、Windows prefix 和反斜杠逃逸,最终路径必须落在server.follower.remote_storage_target_local_root内。 - object-storage remote storage target
base_path当前按 prefix 处理:trim 空白并去掉首尾/,允许空 prefix 表示 bucket/container 根。 - storage policy object-storage endpoint/bucket 使用
normalize_s3_endpoint_and_bucket及 connector 错误码映射:endpoint 非空时必须是http://或https://且包含 hostname,bucket/container 不能为空。 - storage policy SFTP endpoint 由
parse_sftp_endpoint处理:允许sftp://host:port、裸host和host:port,默认端口22;只有出现真实://scheme 分隔符时才按 URL scheme 校验。路径、query、fragment 和 URL 内凭据无效,远程根目录必须走base_path。 - SFTP 主机密钥指纹存放在
storage_policy.options.sftp_host_key_fingerprint。未知或不匹配的 host key 必须 fail closed,并通过结构化SftpHostKeyRejected上下文暴露 actual / expected 指纹,测试不要解析错误文本。 - storage policy 的
max_file_size中,0表示不声明额外限制,负数无效;上传链路仍会在应用 policy 时执行最终大小校验。remote storage target 创建 DTO 不接受这个字段。 - 同 driver 编辑时,
access_key/secret_key/base_path/ endpoint / bucket 等可选字段省略表示保留已有值;显式提供字段则重新走对应 driver 的规范化和校验。 - 切换 remote storage target driver 时,旧 driver-specific 字段不能继承到新 driver:endpoint、bucket、access key、secret key 会重置为空;base path 回到新 driver 输入或默认根语义后再规范化。
- route 层只做协议适配、鉴权、参数提取和响应映射,不拼 descriptor,不做 driver-specific normalization。
- service 层负责 use case 编排:加载上下文、调用 normalization、检查 capability、调用 repo、执行必要 side effects。
src/storage/connectors/负责 storage policy connector descriptor、连接字段规范化、连接测试、授权和 connector action。src/services/remote/storage_target/driver.rs负责 remote storage target driver descriptor、driver-field normalization、target-to-policy materialization 和 driver build/validate。src/storage/remote_protocol/只处理 wire model、签名、path encoding、transport 和 response parsing,不决定 UI 字段和 policy target 选择。
修改 descriptor 或 normalization 时至少补对应的纯函数/单元测试:
- descriptor 必须覆盖每个内置 driver 的字段、secret 标记、action 和关键 capability。
- credential management 必须覆盖 missing、authorized 和需要重新授权的展示边界;action output 必须覆盖 draft/saved、缺失 output、未声明字段、类型不匹配字段和通用成功兜底。测试还必须证明失败 action 的 output 既不展示也不持久化,结果不写回 policy config,也不会保留到其他 action 或对话框会话。
- normalization 必须覆盖 trim、空值、逃逸路径、prefix 首尾斜杠、storage policy 负数
max_file_size、同 driver secret preserve、显式 secret replace、driver 切换字段重置。 - SFTP 必须覆盖裸 host、
host:port、sftp://host:port、错误协议、host key 指纹格式、未知 host key 拒绝和已确认指纹通过。 - storage policy descriptor 行为改变时,跑
cargo nextest run --lib storage::connectors或更小过滤;remote storage target 归一化改变时,跑cargo nextest run --lib remote::storage_target::tests::<filter>。 - 内置 connector 身份、localization 或上述文档事实改变时,运行
make storage-docs更新生成产物,再运行make storage-docs-check验证无漂移。 - 改 OpenAPI schema 后必须重新导出 OpenAPI、生成前端 SDK 并审查生成差异。