feat: 支持画廊图片更新
This commit is contained in:
338
docs/STUDIO_COS_SETUP.md
Normal file
338
docs/STUDIO_COS_SETUP.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# 工作室画廊 COS 接入配置说明
|
||||
|
||||
本文档对应当前仓库当前实现。
|
||||
|
||||
现在已经不再使用 STS `AssumeRole`。
|
||||
当前方案改为:
|
||||
|
||||
- 服务端使用长期密钥直接签发 COS POST Policy
|
||||
- 管理中心小程序拿到表单签名后直传 COS
|
||||
- 工作室配置中的 `logo`、`bannerUrl`、`photos` 保存最终可访问 URL
|
||||
|
||||
当前实现代码入口:
|
||||
|
||||
- `packages/server/src/studio/studio-upload.service.ts`
|
||||
- `packages/server/src/studio/studio.controller.ts`
|
||||
- `packages/app/src/utils/studio-upload.ts`
|
||||
- `packages/app/src/pages/admin/studio.vue`
|
||||
|
||||
## 一、整体链路
|
||||
|
||||
1. 管理中心点击上传图片。
|
||||
2. 小程序请求服务端 `POST /api/admin/studio/upload-credentials`。
|
||||
3. 服务端用 `COS_SECRET_ID`、`COS_SECRET_KEY` 直接生成一组 POST Policy 表单字段。
|
||||
4. 服务端把 `uploadUrl`、`key`、`formData`、`fileUrl`、`expiresAt` 返回给小程序。
|
||||
5. 小程序使用 `uni.uploadFile` 直接上传到 COS。
|
||||
6. 上传成功后,把 URL 保存到工作室配置,再调用 `PUT /api/admin/studio/info` 落库。
|
||||
|
||||
这个方案没有临时密钥,也没有角色扮演。
|
||||
安全边界来自两层:
|
||||
|
||||
- 服务端只为单个对象 key 签发一次表单策略
|
||||
- 表单策略有明确过期时间,过期后自动失效
|
||||
|
||||
## 二、这个方案的本质
|
||||
|
||||
你现在选的是“服务端代签名”的直传方案。
|
||||
它和 STS 的差别是:
|
||||
|
||||
- STS:给前端一段时间内可用的短期密钥
|
||||
- 当前方案:不给前端密钥,只给前端一个短时有效的上传表单签名
|
||||
|
||||
所以结论很直接:
|
||||
|
||||
- 仍然有有效期
|
||||
- 但有效期作用在 POST Policy 上,不是作用在临时密钥上
|
||||
|
||||
当前代码里默认有效期是 `1800` 秒。
|
||||
环境变量:
|
||||
|
||||
- `COS_UPLOAD_DURATION_SECONDS`
|
||||
|
||||
当前实现限制范围:
|
||||
|
||||
- 最短 `300` 秒
|
||||
- 最长 `7200` 秒
|
||||
|
||||
## 三、你现在真正需要准备的东西
|
||||
|
||||
先确认下面几个信息:
|
||||
|
||||
- COS Bucket 名称,例如 `plates-1251306435`
|
||||
- COS 所在地域,例如 `ap-guangzhou`
|
||||
- 服务端使用的 COS 长期密钥 `SecretId` / `SecretKey`
|
||||
- 图片上传前缀,例如 `mp/studio`
|
||||
- 图片访问域名
|
||||
|
||||
建议约定:
|
||||
|
||||
- Bucket:`plates-1251306435`
|
||||
- Region:`ap-guangzhou`
|
||||
- Prefix:`mp/studio`
|
||||
|
||||
## 四、COS 控制台配置
|
||||
|
||||
### 1. 创建或确认 Bucket
|
||||
|
||||
控制台路径:`对象存储 COS`
|
||||
|
||||
建议:
|
||||
|
||||
- 地域选 `广州` 或你当前实际地域
|
||||
- 存储类型标准存储即可
|
||||
- Bucket 名称和环境变量保持完全一致
|
||||
|
||||
### 2. 图片访问方式
|
||||
|
||||
当前实现保存的是直接图片 URL。
|
||||
所以图片必须能被小程序和前台直接访问。
|
||||
|
||||
你有两种方式:
|
||||
|
||||
1. 直接使用 COS 源站并允许读
|
||||
2. 配 CDN / 自定义域名并让这个域名可直接访问图片
|
||||
|
||||
如果你什么都不配,上传成功后图片可能打不开。
|
||||
|
||||
最直接做法:
|
||||
|
||||
- 让这个图片 Bucket 对外可读
|
||||
|
||||
更稳妥做法:
|
||||
|
||||
- 单独图片 Bucket
|
||||
- 用 CDN 域名做 `COS_PUBLIC_BASE_URL`
|
||||
|
||||
### 3. 微信小程序合法域名
|
||||
|
||||
微信公众平台需要补白名单:
|
||||
|
||||
- `request 合法域名`:你的后端 API 域名
|
||||
- `uploadFile 合法域名`:`https://<bucket>.cos.<region>.myqcloud.com`
|
||||
- `downloadFile 合法域名`:图片访问域名
|
||||
|
||||
如果图片访问也走 COS 源站,那么 `downloadFile 合法域名` 同样加:
|
||||
|
||||
- `https://<bucket>.cos.<region>.myqcloud.com`
|
||||
|
||||
例如:
|
||||
|
||||
- `https://focus.richarjiang.com`
|
||||
- `https://plates-1251306435.cos.ap-guangzhou.myqcloud.com`
|
||||
|
||||
## 五、服务端账号需要什么权限
|
||||
|
||||
现在已经不需要:
|
||||
|
||||
- STS
|
||||
- CAM 角色
|
||||
- `AssumeRole`
|
||||
- 角色信任策略
|
||||
- `COS_UPLOAD_ROLE_ARN`
|
||||
|
||||
现在服务端只需要一对可以给目标 Bucket 生成上传签名的长期密钥。
|
||||
|
||||
最简单的做法是:
|
||||
|
||||
- 用你的主账号密钥
|
||||
|
||||
但生产上更合理的是:
|
||||
|
||||
- 建一个专用 CAM 用户,只给这个 Bucket 上传相关权限
|
||||
|
||||
### 推荐 CAM 用户权限策略
|
||||
|
||||
如果你要建专用 CAM 用户,给它绑定下面这类策略即可。
|
||||
|
||||
把下面真实值替换成你的实际资源:
|
||||
|
||||
- 地域:`ap-guangzhou`
|
||||
- AppId:`1251306435`
|
||||
- Bucket:`plates-1251306435`
|
||||
- Prefix:`mp/studio`
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2.0",
|
||||
"statement": [
|
||||
{
|
||||
"effect": "allow",
|
||||
"action": [
|
||||
"name/cos:PutObject",
|
||||
"name/cos:PostObject"
|
||||
],
|
||||
"resource": [
|
||||
"qcs::cos:ap-guangzhou:uid/1251306435:plates-1251306435/mp/studio/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
如果你后续还要服务端删除对象,再补:
|
||||
|
||||
- `name/cos:DeleteObject`
|
||||
|
||||
当前仓库实现不需要删除对象,所以先不要额外放大权限。
|
||||
|
||||
## 六、服务端环境变量
|
||||
|
||||
把下面变量配置到 `packages/server/.env` 或线上环境:
|
||||
|
||||
```env
|
||||
COS_SECRET_ID=your-cos-secret-id
|
||||
COS_SECRET_KEY=your-cos-secret-key
|
||||
COS_BUCKET=plates-1251306435
|
||||
COS_REGION=ap-guangzhou
|
||||
COS_PUBLIC_BASE_URL=https://plates-1251306435.cos.ap-guangzhou.myqcloud.com
|
||||
COS_UPLOAD_PREFIX=mp/studio
|
||||
COS_UPLOAD_DURATION_SECONDS=1800
|
||||
```
|
||||
|
||||
各字段含义:
|
||||
|
||||
- `COS_SECRET_ID`:用于签发 POST Policy 的长期密钥 ID
|
||||
- `COS_SECRET_KEY`:用于签发 POST Policy 的长期密钥 Key
|
||||
- `COS_BUCKET`:上传目标 Bucket
|
||||
- `COS_REGION`:Bucket 地域
|
||||
- `COS_PUBLIC_BASE_URL`:最终展示图片的访问域名
|
||||
- `COS_UPLOAD_PREFIX`:统一对象前缀
|
||||
- `COS_UPLOAD_DURATION_SECONDS`:Policy 有效期秒数
|
||||
|
||||
现在可以删除或忽略这些旧配置:
|
||||
|
||||
- `COS_UPLOAD_ROLE_ARN`
|
||||
- `COS_APP_ID`
|
||||
- `COS_UPLOAD_ROLE_SESSION_NAME`
|
||||
|
||||
它们对当前实现已经没用。
|
||||
|
||||
## 七、控制台操作清单
|
||||
|
||||
按这个顺序做:
|
||||
|
||||
1. 确认 COS Bucket 已存在。
|
||||
2. 确认图片访问域名对外可读。
|
||||
3. 在微信公众平台加好 `request` / `uploadFile` / `downloadFile` 合法域名。
|
||||
4. 准备一对 COS 长期密钥。
|
||||
5. 把 `COS_SECRET_ID`、`COS_SECRET_KEY`、`COS_BUCKET`、`COS_REGION`、`COS_PUBLIC_BASE_URL`、`COS_UPLOAD_PREFIX` 配到服务端。
|
||||
6. 重启服务端。
|
||||
7. 在管理中心上传一张图片测试。
|
||||
|
||||
## 八、接口返回内容说明
|
||||
|
||||
请求:
|
||||
|
||||
```http
|
||||
POST /api/admin/studio/upload-credentials
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer <admin-token>
|
||||
|
||||
{
|
||||
"fileName": "demo.jpg",
|
||||
"contentType": "image/jpeg",
|
||||
"assetType": "gallery"
|
||||
}
|
||||
```
|
||||
|
||||
正常返回会包含:
|
||||
|
||||
- `uploadUrl`
|
||||
- `fileUrl`
|
||||
- `key`
|
||||
- `assetType`
|
||||
- `expiresAt`
|
||||
- `formData`
|
||||
|
||||
`formData` 里会有这些字段:
|
||||
|
||||
- `key`
|
||||
- `policy`
|
||||
- `success_action_status`
|
||||
- `Content-Type`
|
||||
- `q-sign-algorithm`
|
||||
- `q-ak`
|
||||
- `q-key-time`
|
||||
- `q-sign-time`
|
||||
- `q-signature`
|
||||
|
||||
这就是小程序直传需要的全部内容。
|
||||
|
||||
## 九、怎么验证是否配置正确
|
||||
|
||||
### 1. 接口层验证
|
||||
|
||||
调用 `POST /api/admin/studio/upload-credentials`。
|
||||
|
||||
如果成功,说明:
|
||||
|
||||
- 服务端长期密钥有效
|
||||
- 服务端已经能正确签发 policy
|
||||
|
||||
### 2. 上传层验证
|
||||
|
||||
在管理中心上传一张图,检查:
|
||||
|
||||
1. COS Bucket 下是否出现对象
|
||||
2. 返回的 `fileUrl` 浏览器是否能直接访问
|
||||
3. 保存工作室设置后首页是否显示该图
|
||||
|
||||
### 3. 失败时怎么定位
|
||||
|
||||
如果 `upload-credentials` 接口失败,优先检查:
|
||||
|
||||
- `COS_SECRET_ID` / `COS_SECRET_KEY` 是否正确
|
||||
- `COS_BUCKET` / `COS_REGION` 是否正确
|
||||
- 服务端是否已经加载最新环境变量
|
||||
|
||||
如果接口成功但上传失败,优先检查:
|
||||
|
||||
- 小程序 `uploadFile 合法域名` 是否正确
|
||||
- Bucket 权限策略是否允许当前长期密钥上传到该前缀
|
||||
- `Content-Type` 是否被策略条件限制住
|
||||
|
||||
如果上传成功但图片打不开,优先检查:
|
||||
|
||||
- Bucket 或图片域名是否可公网访问
|
||||
- `COS_PUBLIC_BASE_URL` 是否正确
|
||||
- 小程序 `downloadFile 合法域名` 是否正确
|
||||
|
||||
## 十、当前实现的边界
|
||||
|
||||
当前仓库实现边界如下:
|
||||
|
||||
- 只支持 `jpg`、`jpeg`、`png`、`webp`、`heic`、`heif`
|
||||
- 单次上传大小上限 `10MB`
|
||||
- 只支持普通表单直传,不支持分片上传
|
||||
- 删除工作室图片时,只会从数据库配置里移除 URL,不会删除 COS 历史对象
|
||||
|
||||
最后一条是故意保守设计。
|
||||
原因很简单:
|
||||
|
||||
- 先保证配置删除安全
|
||||
- 避免误删真实文件
|
||||
|
||||
如果以后要做“删配置时同步删对象”,那时再单独加 `DeleteObject` 权限。
|
||||
|
||||
## 十一、初始化工作室画廊
|
||||
|
||||
如果你要把现在手工写死的图片 URL 一次性写入数据库,执行:
|
||||
|
||||
```bash
|
||||
pnpm --filter @mp-pilates/server studio:seed-gallery
|
||||
```
|
||||
|
||||
脚本文件:
|
||||
|
||||
- `packages/server/prisma/update-studio-gallery.ts`
|
||||
|
||||
## 十二、建议的生产做法
|
||||
|
||||
如果你后面要长期维护,建议:
|
||||
|
||||
1. 图片单独放一个 Bucket。
|
||||
2. 长期密钥不要直接用主账号,换成专用 CAM 用户。
|
||||
3. 对专用 CAM 用户只给 `mp/studio/*` 前缀上传权限。
|
||||
4. 用 CDN 域名作为 `COS_PUBLIC_BASE_URL`。
|
||||
|
||||
这样后面扩展、迁移、审计都会更稳。
|
||||
Reference in New Issue
Block a user