- AttachmentMetadata discriminated union (image/video/audio/document/file) in zod/openapi; attachments table gains metadata + metadata_kind (0003) - create/PATCH accept client-supplied metadata; finalize auto-extracts image width/height/format from R2 head bytes via image-size (no client override) - serializeAttachment surfaces parsed metadata in all responses Co-Authored-By: Claude <noreply@anthropic.com>
cfw-attachment
独立 Cloudflare 附件管理服务。认证通过 AUTH service binding 调用 cfw-auth,附件业务数据保存在本服务自己的 D1/R2。
附件表(元数据、分类、标签、业务关联、审计)全部归属当前登录用户:所有 D1 查询都带 user_id 条件,R2 object key 也按 users/{userId}/... 隔离。
架构边界
cfw-auth:只负责登录鉴权,本服务不修改它。cfw-attachment:独立 Worker,绑定自己的 D1(DB)/ R2(ATTACHMENTS),通过AUTHservice binding 调cfw-auth的/api/auth/get-session获取当前用户。cfw-gateway:只把/api/attachments*、/api/attachment-categories*、/api/attachment-tags*代理到本服务,并把本服务 OpenAPI 纳入聚合。
本地检查
pnpm install
pnpm db:apply:local
pnpm ready
pnpm ready 会跑 typecheck 和 test。测试用内存 fake D1(node:sqlite 跑真实 SQL)和内存 fake R2,不需要 Cloudflare 连接。
关键接口
附件:
POST /api/attachments:创建附件元数据,返回upload_pendingPUT /api/attachments/:id/content:经 Worker 代理上传文件内容到 R2POST /api/attachments/:id/finalize:确认上传,状态变为availableGET /api/attachments/:id:读取详情PATCH /api/attachments/:id:更新 filename / visibility / description / categoryId / tagIdsDELETE /api/attachments/:id:软删除,并尽力删 R2 objectGET /api/attachments/:id/download:仅available可下载,否则409 object-not-availableGET /api/attachments/search?page=1&pageSize=20&q=&categoryId=&tag=&status=:分页检索当前用户附件
分类与标签:
POST /api/attachment-categories、GET /api/attachment-categories、PATCH /api/attachment-categories/:id、DELETE /api/attachment-categories/:idPOST /api/attachment-tags、GET /api/attachment-tags、PATCH /api/attachment-tags/:id、DELETE /api/attachment-tags/:id
分类/标签以 user_id + slug 唯一。删除分类会把当前用户下引用它的附件 category_id 置空;删除标签会先删当前用户下的标签关联再删标签。
部署
pnpm db:apply:remote # 远端 D1 应用 schema
pnpm deploy # 部署 Worker(含 AUTH service binding 到 cfw-auth)
Description
Languages
TypeScript
100%