feat: add object storage signing providers

This commit is contained in:
Codex
2026-06-22 00:58:36 +08:00
parent ef8afcdbc8
commit 492706e24f
13 changed files with 1650 additions and 79 deletions

View File

@@ -28,7 +28,7 @@
| 知识手册 | 可联调 | 科目、章节、条目、Markdown 内容、嵌套 JSON 导入 | 富文本资源、版本管理、附件/PDF 关联 |
| 分数线 | 可联调 | 院校、专业、动态字段、记录、年份、趋势、后台维护 | 批量导入、复杂筛选、AI 择校上下文 |
| 视频解析 | 部分完成 | 单题视频、批量查询、后台视频绑定 | 会员播放权限、播放次数扣减、签名 URL、防盗链、水印 |
| 资料下载 | 部分完成 | 资源台账、SVIP 权限校验、上传/下载签名占位 | 阿里云 OSS/腾讯 COS/Supabase Storage 真实签名、PDF 预览 |
| 资料下载 | 部分完成 | 资源台账、SVIP 权限校验、`local_dev`/阿里云 OSS/腾讯 COS/Supabase Storage 上传下载签名 | 上传后对象校验、PDF 预览、防盗链、视频水印 |
| 会员与订单 | 基础完成 | 下单、手动确认、激活码兑换、权益发放 | 微信支付、支付宝、退款、webhook 验签和幂等 |
| 登录认证 | 迁移期可用 | 短信 mock、迁移期 session、OAuth 配置表 | 阿里云/腾讯云短信、微信小程序/网页登录、QQ 登录、Supabase Auth |
| 销售/代理/CRM | 基础完成 | 邀请码、首绑保护、团队关系、销售统计、CRM 入队 | 小程序码真实生成、分佣结算、钉钉/飞书/企微 worker |
@@ -76,7 +76,7 @@
### P0上云测试和前端主链路前必须处理
- 生产鉴权:用 Supabase Auth/JWT 或服务端 session 替换 `x-tenant-id``x-user-id``x-platform-admin-key`
- 对象存储:接入阿里云 OSS、腾讯云 COSSupabase Storage完成上传、下载、PDF 预览、视频播放签名。
- 对象存储:上传/下载签名已接入阿里云 OSS、腾讯云 COSSupabase Storage;继续完成上传后对象校验、PDF 预览、视频播放签名、防盗链和水印
- 真实数据 dry-run导出 PocketBase 用户、题库、单词、知识手册、分数线、订单、权益,跑迁移和校验报告。
- 生产环境配置:补 `.env` 模板、数据库迁移流程、备份恢复、日志、告警和 API 容器部署说明。
- Taro scaffold建立 `apps/taro`,先完成租户解析、首页、题库、背单词、知识手册、个人中心主链路。

View File

@@ -175,13 +175,13 @@ GET /api/tenant-admin/audit-logs
- 当前默认角色:`tenant_owner`/`tenant_admin` 全权限,`tenant_operator` 可维护内容和活动,`teacher` 可维护内容,`sales` 可维护激活码和优惠券,`agent` 只读部分兑换码/优惠券。
- 销售/代理客资采用首绑保护:普通扫码/分享事件不会覆盖已有归属,只有具备 `referral:write` 的租户成员可手动强制补绑。
- CRM 当前完成配置、密钥入私密表、客资入队和队列查询;真实 webhook 发送、重试、签名在后续 `apps/worker` 中实现。
- 内容资源当前完成台账、租户后台维护、上传/下载签名占位和学生端 SVIP 下载权限真实对象存储签名、PDF 预览渲染防盗链在 provider/worker 中实现
- 内容资源当前完成台账、租户后台维护、学生端 SVIP 下载权限,以及 `local_dev`、阿里云 OSS、腾讯 COS、Supabase Storage 的上传/下载签名 provider。真实对象存在性校验、PDF 预览渲染防盗链、水印和大文件上传后 worker 校验仍需继续补
- 题库内容导航当前以 `content_entries/content_nodes` 为主模型,可表达“入口 -> 多级分类 -> 院校/专业/学科/销售意向标记”;题目集合和练习方式由 `question_collections/practice_blueprints` 管理,练习 session 会保存当次题目 ID 快照。
- 批量导入当前支持题目、单词、知识手册 JSON 预览、逐行 issue、job/item 台账、执行导入、幂等跳过,并可落到新内容入口和分类节点。旧单词模板的 `vocabulary_units_示例数据` / `vocabulary_示例数据`、知识手册的书籍/章节/小节/知识点嵌套结构都由后端规范化。Excel/CSV、分数线/视频导入会继续复用同一套 `content_import_jobs` 管线。
## 下一步
1. 完善内容导入和文件上传Excel/CSV、分数线、视频导入真实 OSS/COS/Supabase Storage 签名
1. 完善内容导入和文件上传Excel/CSV、分数线、视频导入对象存储上传后校验、PDF 预览、防盗链和视频水印
2. 接入真实短信 provider阿里云/腾讯云,密钥放 `app_private.tenant_secrets` 或生产 Vault。
3. 接入真实 OAuth provider微信网页、微信小程序、QQ并处理旧 PocketBase 身份映射。
4. 增加真实支付 providerXPay、微信支付、支付宝并完善 webhook 幂等。

View File

@@ -31,8 +31,8 @@
- 做一轮真实 JWT + RLS 回归测试。
2. 对象存储
- 接阿里云 OSS、腾讯云 COSSupabase Storage。
- 完成上传签名、下载签名、PDF 预览地址、视频播放签名。
- 接阿里云 OSS、腾讯云 COSSupabase Storage 的上传/下载签名 provider
- 继续补上传后对象存在性校验、PDF 预览地址、视频播放签名、防盗链、水印和 worker 校验
- `content_assets` 继续作为资源台账,不允许前端绕过台账直接访问私有资源。
3. 真实导入 dry-run
@@ -167,7 +167,7 @@
## 推荐下一步顺序
1. 先把当前后端代码推到 Gitea。
2. 云服务器部署 Supabase/PostgreSQL 和 API`check:refactor` 的远程等价测试。
2. 云服务器部署 Supabase/PostgreSQL 和 API配置对象存储生产环境变量,`check:refactor` 的远程等价测试。
3. 导出现有 PocketBase 数据,做完整 dry-run 迁移。
4. 开始 `apps/taro`,先接租户解析、首页、题库、背单词、知识手册。
5. 并行补对象存储、真实登录、支付 adapter。

View File

@@ -0,0 +1,108 @@
# 对象存储接入说明
更新时间2026-06-22
## 目标
题库里的图片、PDF、视频、音频、资料包等媒体资源统一走 `content_assets` 台账和后端签名接口。前端不直接保存或读取云厂商密钥,也不直接拼接私有资源 URL。
已接入的 provider
- `local_dev`:本地开发占位签名,便于前后端联调。
- `aliyun_oss`:阿里云 OSS 官方 Node.js SDK `signatureUrl`
- `tencent_cos`:腾讯云 COS XML API V5 签名 URL服务端用 Node `crypto` 实现,避免引入当前 COS Node SDK 的高危依赖。
- `supabase_storage`Supabase Storage 官方 `createSignedUploadUrl` / `createSignedUrl` 语义。
- `external_url`:外部公开或厂商托管 URL只允许作为已管理资源的下载地址不支持后端直传签名。
## API
后台申请上传签名:
```text
POST /api/tenant-content/assets/sign-upload
```
后台登记资源:
```text
PUT /api/tenant-content/assets
```
学生端下载:
```text
GET /api/catalog/assets/download?assetId=...
```
后台管理员下载:
```text
POST /api/tenant-content/assets/sign-download
```
## 安全规则
- 只有租户内容维护权限用户可以申请上传签名。
- `objectKey` 默认必须以当前 `tenantId/` 开头,防止跨租户覆盖或读取。
- 禁止 `..`、反斜杠、编码斜杠等危险 object key。
- 上传会校验 MIME 类型和文件大小。
- 下载必须先经过 API 权限判断,再下发短期签名 URL。
- 云厂商 AccessKey、SecretKey、Service Role Key 只存在服务端环境变量,不返回前端。
- `content_assets` 是资源唯一台账,前端不得绕过台账直接访问私有 bucket。
## 环境变量
通用:
```text
STORAGE_DEFAULT_PROVIDER=local_dev
STORAGE_DEFAULT_BUCKET=tenant-assets
STORAGE_PUBLIC_BASE_URL=
STORAGE_MAX_UPLOAD_BYTES=524288000
STORAGE_ALLOWED_MIME_PREFIXES=image/,video/,audio/
STORAGE_ALLOWED_MIME_TYPES=application/pdf,application/json,application/zip,application/x-zip-compressed,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/octet-stream,text/plain,text/markdown,text/csv
STORAGE_REQUIRE_TENANT_PREFIX=true
```
阿里云 OSS
```text
ALIYUN_OSS_REGION=oss-cn-hangzhou
ALIYUN_OSS_ENDPOINT=
ALIYUN_OSS_ACCESS_KEY_ID=
ALIYUN_OSS_ACCESS_KEY_SECRET=
ALIYUN_OSS_STS_TOKEN=
ALIYUN_OSS_INTERNAL=false
```
腾讯云 COS
```text
TENCENT_COS_REGION=ap-shanghai
TENCENT_COS_APP_ID=
TENCENT_COS_SECRET_ID=
TENCENT_COS_SECRET_KEY=
TENCENT_COS_SECURITY_TOKEN=
```
Supabase Storage
```text
SUPABASE_STORAGE_URL=https://your-project.supabase.co/storage/v1
SUPABASE_STORAGE_SERVICE_KEY=
```
## 生产建议
- 阿里云和腾讯云生产环境优先用 STS/临时密钥或 RAM/CAM 最小权限账号。
- bucket 默认私有,公开资源也建议先经过 CDN/防盗链策略,不让前端直接持有写权限。
- 图片、PDF、视频分别设置合理的 CORS只允许前端域名和小程序业务域名访问。
- 开启对象版本控制、生命周期、跨区域复制或定时备份,满足后续容灾要求。
- 视频资源建议后续接入转码、水印、防盗链、播放日志和播放次数扣减。
- 大文件上传后应由 worker 校验对象是否真实存在、大小/hash 是否匹配,再把资源状态从 `draft` 发布为 `active`
## 官方依据
- 阿里云 OSS Node.js SDK 支持通过 `signatureUrl` 为上传或下载生成带过期时间的签名 URL。
- 腾讯云 COS XML API V5 签名由 `q-sign-algorithm``q-ak``q-sign-time``q-key-time``q-header-list``q-url-param-list``q-signature` 等字段组成,可用于预签名 URL。
- Supabase Storage 提供 `createSignedUploadUrl``createSignedUrl`,分别用于签名上传和签名下载;私有 bucket 仍应配合 RLS 和服务端权限控制。