feat(api-water): 支持上传水印图片
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ API 启动后会在 `/api-docs` 提供 Swagger UI,并在其 OpenAPI 文档中
|
||||
| [`conventions.md`](./conventions.md) | 部分已实现 | 鉴权、全局上下文、响应、分页、错误、并发与异步任务。 |
|
||||
| [`auth.md`](./auth.md) | 已实现 | 邮箱注册、登录、刷新会话、退出、当前用户。 |
|
||||
| [`stores-and-platforms.md`](./stores-and-platforms.md) | 部分已实现 | 平台目录、页头上下文、店铺授权、门店 CRUD、默认店铺、操作查询与仓库读取;平台实际验证与同步 Worker 待实现。 |
|
||||
| [`watermarks-and-assets.md`](./watermarks-and-assets.md) | 部分已实现 | 水印模板、字体、店铺推荐水印;素材直传与短时访问待实现。 |
|
||||
| [`watermarks-and-assets.md`](./watermarks-and-assets.md) | 部分已实现 | 水印模板、字体、店铺推荐水印,以及水印图片直传、校验和短时预览;图片渲染派生待实现。 |
|
||||
| [`pricing.md`](./pricing.md) | 已设计 | 汇率、物流价卡、即时试算、定价方案、报价快照衔接。 |
|
||||
| [`collection-and-extension.md`](./collection-and-extension.md) | 已设计 | 插件授权、采集写入、采集箱查询、素材转存与状态。 |
|
||||
| [`product-editor-and-publishing.md`](./product-editor-and-publishing.md) | 已设计 | 商品编辑、SKU、类目、属性、AI、媒体选择、定价确认与部分 SKU 首次上架。 |
|
||||
|
||||
@@ -22,7 +22,7 @@ Headers:`Idempotency-Key` 必填。
|
||||
}
|
||||
```
|
||||
|
||||
首期允许 `image/png`、`image/jpeg`、`image/webp`。服务端按用途校验大小/尺寸策略,生成随机 `object_key` 和短时预签名上传地址。返回 `201 Created`:
|
||||
首期允许 `image/png`、`image/jpeg`、`image/webp`,最大 5 MiB,图片宽高须为 1–8192 px。服务端按用途校验大小/尺寸策略,生成随机 `object_key` 和 10 分钟有效的预签名上传地址。返回 `201 Created`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -50,7 +50,7 @@ Headers:`Idempotency-Key` 必填。
|
||||
{ "checksum_sha256": "hex-sha256" }
|
||||
```
|
||||
|
||||
服务端检查对象存在、文件大小、MIME/魔数、哈希和图片尺寸;校验通过后创建/复用 `media_asset` 并返回 `201 Created`。图片解码或安全扫描耗时时可先返回 `202` 与 `media_processing` operation。
|
||||
服务端检查对象存在、文件大小、声明 MIME、文件魔数、哈希和图片尺寸;校验通过后创建/复用 `media_asset` 并返回 `201 Created`。校验失败时上传意图标记为失败,不能被水印模板引用。
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -67,7 +67,7 @@ Headers:`Idempotency-Key` 必填。
|
||||
}
|
||||
```
|
||||
|
||||
`object_key` 是服务端内部字段,不放在普通客户端响应。上传意图、完成状态和对象清理需要在实现时增加持久化记录;它不能只存在于 Redis 或预签名 token 中。
|
||||
`object_key` 是服务端内部字段,不放在普通客户端响应。上传意图和完成状态均持久化在数据库,不能只存在于 Redis 或预签名 token 中;过期和失败对象由后续清理任务回收。
|
||||
|
||||
### 1.3 临时预览 URL
|
||||
|
||||
@@ -77,7 +77,7 @@ Headers:`Idempotency-Key` 必填。
|
||||
{ "purpose": "preview" }
|
||||
```
|
||||
|
||||
返回短时 `url` 与 `expires_at`。仅当前工作空间的素材可签发;前端在 URL 过期后重新请求,不能把 URL 写回业务表或长期缓存。
|
||||
返回 10 分钟有效的短时 `url` 与 `expires_at`。仅当前工作空间的素材可签发;前端在 URL 过期后重新请求,不能把 URL 写回业务表或长期缓存。
|
||||
|
||||
## 2. 可用字体
|
||||
|
||||
|
||||
@@ -90,12 +90,13 @@ NestJS API/Worker 通过环境变量拿到连接信息,本地 `.env` 示例:
|
||||
|
||||
```bash
|
||||
# 对象存储(本地 MinIO / 生产 Kodo 只换这一组值)
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
S3_REGION=us-east-1 # MinIO 不校验 region,任意合法值即可
|
||||
S3_BUCKET=atlas-dev-assets
|
||||
S3_ACCESS_KEY=atlas-dev
|
||||
S3_SECRET_KEY=atlas-dev-secret
|
||||
S3_FORCE_PATH_STYLE=true # MinIO 必须用 path-style,见第 6.3 节
|
||||
OBJECT_STORAGE_ENDPOINT=http://localhost:9000
|
||||
OBJECT_STORAGE_BUCKET=atlas-dev-assets
|
||||
OBJECT_STORAGE_FORCE_PATH_STYLE=true
|
||||
# 生产或共享环境必须提供专用最小权限凭证:
|
||||
OBJECT_STORAGE_ACCESS_KEY=atlas-dev
|
||||
OBJECT_STORAGE_SECRET_KEY=atlas-dev-secret
|
||||
# 本地也可仅设置 MINIO_ROOT_USER / MINIO_ROOT_PASSWORD,由 API 作为兼容回退读取。
|
||||
```
|
||||
|
||||
## 4. 创建 Bucket
|
||||
@@ -159,24 +160,24 @@ import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
|
||||
const s3 = new S3Client({
|
||||
endpoint: process.env.S3_ENDPOINT!, // http://localhost:9000
|
||||
region: process.env.S3_REGION!, // MinIO 任意值即可
|
||||
endpoint: process.env.OBJECT_STORAGE_ENDPOINT!, // http://localhost:9000
|
||||
region: 'us-east-1',
|
||||
credentials: {
|
||||
accessKeyId: process.env.S3_ACCESS_KEY!,
|
||||
secretAccessKey: process.env.S3_SECRET_KEY!,
|
||||
accessKeyId: process.env.OBJECT_STORAGE_ACCESS_KEY!,
|
||||
secretAccessKey: process.env.OBJECT_STORAGE_SECRET_KEY!,
|
||||
},
|
||||
forcePathStyle: true, // MinIO 必须;Kodo 走虚拟主机式,配置驱动即可
|
||||
forcePathStyle: process.env.OBJECT_STORAGE_FORCE_PATH_STYLE === 'true',
|
||||
});
|
||||
|
||||
// 预签名上传 URL:前端拿到后直传,浏览器不保存永久密钥
|
||||
export async function presignUpload(objectKey: string, contentType: string) {
|
||||
const cmd = new PutObjectCommand({ Bucket: process.env.S3_BUCKET, Key: objectKey, ContentType: contentType });
|
||||
const cmd = new PutObjectCommand({ Bucket: process.env.OBJECT_STORAGE_BUCKET, Key: objectKey, ContentType: contentType });
|
||||
return getSignedUrl(s3, cmd, { expiresIn: 600 }); // 10 分钟
|
||||
}
|
||||
|
||||
// 预签名下载 URL:预览/下载私有文件
|
||||
export async function presignDownload(objectKey: string) {
|
||||
const cmd = new GetObjectCommand({ Bucket: process.env.S3_BUCKET, Key: objectKey });
|
||||
const cmd = new GetObjectCommand({ Bucket: process.env.OBJECT_STORAGE_BUCKET, Key: objectKey });
|
||||
return getSignedUrl(s3, cmd, { expiresIn: 900 }); // 15 分钟
|
||||
}
|
||||
```
|
||||
|
||||
@@ -64,7 +64,13 @@
|
||||
|
||||
- 已实现字体目录、按工作空间隔离的水印模板列表/详情/创建/编辑/软删除,以及店铺推荐水印绑定与解绑。文字/图片模板均创建不可变渲染版本;仅改名不新增版本,渲染参数或类型变化会新增版本。
|
||||
- 删除模板会解除所有当前店铺绑定,但不删除历史版本或图片资产。模板与店铺写操作均要求 `Idempotency-Key`;编辑要求 `If-Match`。
|
||||
- 图片模板可校验已有 `media_asset` 的归属;素材直传、图片校验和短时预览 URL 尚未实现,不能在前端自行拼接对象存储地址。
|
||||
- 图片模板只可引用当前工作空间内、已完成安全校验的 `media_asset`;素材直传、图片校验和短时预览 URL 的实现见 1.6,前端仍不能自行拼接对象存储地址。
|
||||
|
||||
### 1.6 开发进度(2026-09-04):水印图片素材直传
|
||||
|
||||
- 已实现仅供 `watermark_image` 使用的两阶段素材接口:创建 10 分钟有效的 MinIO/S3 预签名 PUT 上传意图、完成上传后验证对象存在、大小、SHA-256、PNG/JPEG/WebP 魔数与 1–8192 px 尺寸,并创建或复用工作空间内的 `media_asset`。
|
||||
- 已实现素材短时预览 URL(10 分钟);普通 API 返回永不包含 bucket、object key 或长期 URL。图片模板只接受当前工作空间内、类型为 PNG/JPEG/WebP 的已验证素材。
|
||||
- 本地环境默认使用 `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` 连接 `http://localhost:9000`;生产必须显式配置最小权限的 `OBJECT_STORAGE_ACCESS_KEY` / `OBJECT_STORAGE_SECRET_KEY`。
|
||||
|
||||
## 2. 已确认产品决策
|
||||
|
||||
|
||||
Reference in New Issue
Block a user