Implement compression quota refunds and admin manual subscription

This commit is contained in:
2025-12-19 23:28:32 +08:00
commit 11f48fd3dd
106 changed files with 27848 additions and 0 deletions

685
docs/api.md Normal file
View File

@@ -0,0 +1,685 @@
# API 接口文档v1- ImageForge
面向两类使用者:
- **网站Web**:上传/批量/历史/账单等(可能包含匿名试用)。
- **对外 APIDeveloper API**API Key 调用、可计量可计费、适配 CI/CD 与服务端集成。
产品范围与计费口径见:
- `docs/prd.md`
- `docs/billing.md`
---
## 1. 基础信息
- **Base URL**: `https://your-domain.com/api/v1`
- **数据格式**: JSON除明确标注“返回二进制”接口
- **时间格式**: ISO 8601 / UTC`2025-01-15T10:30:00Z`
- **ID 格式**: UUID 字符串
---
## 2. 认证
支持三种身份:
### 2.1 JWT网站/管理后台)
```http
Authorization: Bearer <token>
```
### 2.2 API Key对外 API
```http
X-API-Key: <your-api-key>
```
> **注意**:仅 **Pro** 和 **Business** 套餐用户可创建 API Key。Free 用户尝试创建时返回 `FORBIDDEN`HTTP `403`)。
### 2.3 匿名试用(仅网站场景)
- 不提供 API Key
- 通过 Cookie 维持匿名会话(服务端签发),仅允许较小文件与较低频率。
- 每日 10 次(以成功压缩文件数计);超出返回 `QUOTA_EXCEEDED`HTTP `402`)。
- 日界自然日UTC+8次日 00:00 重置。
- **匿名试用硬限制Cookie + IP 双限制**(两者任一超出都拒绝),降低刷会话绕过风险。
---
## 3. 通用约定
### 3.1 幂等(强烈建议)
对会产生计费/创建任务的接口,建议客户端传:
```http
Idempotency-Key: <uuid-or-random-string>
```
规则(建议口径):
- 同一个 `Idempotency-Key` 在 TTL 内重复请求,若请求参数一致则返回首次结果(不重复扣费/不重复创建任务)。
- 若参数不一致,返回 `409 IDEMPOTENCY_CONFLICT`
### 3.2 限流Rate Limit
超出限制返回:
- HTTP `429`
- 头:`Retry-After: <seconds>`
建议头(可选):
- `RateLimit-Limit`
- `RateLimit-Remaining`
- `RateLimit-Reset`
### 3.3 配额Quota / Billing
配额不足(当期额度耗尽)返回:
- HTTP `402`
- 错误码:`QUOTA_EXCEEDED`
配额周期:
- Pro/Business付费按订阅周期重置`period_start` ~ `period_end`),不是自然月。
- Free未订阅按自然月UTC+8重置。
- 匿名试用按自然日UTC+8重置。
建议头(可选):
- `X-Quota-Limit`
- `X-Quota-Remaining`
- `X-Quota-Reset-At`
### 3.4 通用响应格式JSON
成功:
```json
{ "success": true, "data": {} }
```
错误:
```json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "错误描述",
"request_id": "req_..."
}
}
```
### 3.5 错误码(建议集合)
| 错误码 | HTTP | 说明 |
|---|---:|---|
| `INVALID_REQUEST` | 400 | 参数不合法 |
| `INVALID_IMAGE` | 400 | 图片解码失败/文件损坏 |
| `UNSUPPORTED_FORMAT` | 400 | 不支持的格式 |
| `TOO_MANY_PIXELS` | 400 | 像素超限(防图片炸弹) |
| `UNAUTHORIZED` | 401 | 未认证 |
| `FORBIDDEN` | 403 | 权限不足 |
| `NOT_FOUND` | 404 | 资源不存在 |
| `IDEMPOTENCY_CONFLICT` | 409 | 幂等 key 冲突 |
| `QUOTA_EXCEEDED` | 402 | 配额不足 |
| `FILE_TOO_LARGE` | 413 | 文件过大 |
| `RATE_LIMITED` | 429 | 请求过于频繁 |
| `EMAIL_NOT_VERIFIED` | 403 | 邮箱未验证 |
| `INVALID_TOKEN` | 400 | Token 无效或已过期 |
| `COMPRESSION_FAILED` | 500 | 压缩失败 |
| `STORAGE_UNAVAILABLE` | 503 | 存储不可用 |
| `MAIL_SEND_FAILED` | 500 | 邮件发送失败 |
---
## 4. 认证接口
### 4.1 用户注册
```http
POST /auth/register
Content-Type: application/json
```
请求体:
```json
{ "email": "user@example.com", "password": "securepassword123", "username": "myusername" }
```
响应:
```json
{
"success": true,
"data": {
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "myusername",
"email_verified": false,
"created_at": "2025-01-15T10:30:00Z"
},
"token": "eyJhbGciOi...",
"message": "注册成功,验证邮件已发送至您的邮箱"
}
}
```
> **注意**:注册后自动发送验证邮件。用户需验证邮箱后才能使用压缩功能(未验证时调用压缩接口返回 `EMAIL_NOT_VERIFIED`)。
### 4.2 用户登录
```http
POST /auth/login
Content-Type: application/json
```
请求体:
```json
{ "email": "user@example.com", "password": "securepassword123" }
```
响应:
```json
{
"success": true,
"data": {
"token": "eyJhbGciOi...",
"expires_at": "2025-01-22T10:30:00Z",
"user": { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "myusername", "role": "user" }
}
}
```
### 4.3 刷新 Token
```http
POST /auth/refresh
Authorization: Bearer <token>
```
### 4.4 登出
```http
POST /auth/logout
Authorization: Bearer <token>
```
### 4.5 发送验证邮件
用户注册后自动发送一次;此接口用于重新发送。
```http
POST /auth/send-verification
Authorization: Bearer <token>
```
**限流**:同一用户 1 分钟内最多 1 次
响应:
```json
{ "success": true, "data": { "message": "验证邮件已发送,请查收" } }
```
### 4.6 验证邮箱
```http
POST /auth/verify-email
Content-Type: application/json
```
请求体:
```json
{ "token": "verification-token-from-email" }
```
响应:
```json
{ "success": true, "data": { "message": "邮箱验证成功" } }
```
### 4.7 请求密码重置
```http
POST /auth/forgot-password
Content-Type: application/json
```
请求体:
```json
{ "email": "user@example.com" }
```
**限流**:同一 IP 1 分钟内最多 3 次
响应(无论邮箱是否存在都返回成功,防止枚举):
```json
{ "success": true, "data": { "message": "如果该邮箱已注册,您将收到重置邮件" } }
```
### 4.8 重置密码
```http
POST /auth/reset-password
Content-Type: application/json
```
请求体:
```json
{ "token": "reset-token-from-email", "new_password": "new-secure-password" }
```
响应:
```json
{ "success": true, "data": { "message": "密码重置成功,请重新登录" } }
```
---
## 5. 图片压缩接口
### 5.1 单图压缩(同步,返回 JSON + 下载链接)
适用于网站与轻量同步调用(服务端可选择是否落盘/落对象存储)。
```http
POST /compress
Content-Type: multipart/form-data
Authorization: Bearer <token> # X-API-Key
Idempotency-Key: <key> #
```
表单字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `file` | File | 是 | 图片文件 |
| `compression_rate` | Integer | 否 | 压缩率 1-100数值越大压缩越强优先级高于 `level` |
| `level` | String | 否 | `high` / `medium` / `low`(兼容参数,默认 `medium` |
| `output_format` | String | 否 | 已停用,仅支持保持原格式 |
| `max_width` | Integer | 否 | 最大宽度(等比缩放) |
| `max_height` | Integer | 否 | 最大高度(等比缩放) |
| `preserve_metadata` | Boolean | 否 | 是否保留元数据(默认 `false` |
响应:
```json
{
"success": true,
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440100",
"file_id": "550e8400-e29b-41d4-a716-446655440101",
"format_in": "png",
"format_out": "png",
"original_size": 1024000,
"compressed_size": 256000,
"saved_bytes": 768000,
"saved_percent": 75.0,
"download_url": "/downloads/550e8400-e29b-41d4-a716-446655440101",
"expires_at": "2025-01-15T11:30:00Z",
"billing": { "units_charged": 1 }
}
}
```
### 5.2 单图压缩(同步,直接返回二进制)
更贴近开发者体验,适用于 SDK/CI。
```http
POST /compress/direct
Content-Type: multipart/form-data
X-API-Key: <your-api-key> # Bearer token
Idempotency-Key: <key> #
```
成功响应:
- HTTP `200`
- Body压缩后的图片二进制
- `Content-Type`: `image/png` / `image/jpeg` / `image/webp` / `image/avif` / `image/gif` / `image/bmp` / `image/tiff` / `image/x-icon`
建议响应头(示例):
```http
ImageForge-Original-Size: 1024000
ImageForge-Compressed-Size: 256000
ImageForge-Saved-Bytes: 768000
ImageForge-Saved-Percent: 75.0
ImageForge-Units-Charged: 1
```
### 5.3 批量压缩(异步任务)
适用于多文件或大文件;由 Worker 处理并持续更新进度。
```http
POST /compress/batch
Content-Type: multipart/form-data
Authorization: Bearer <token> # X-API-Key
Idempotency-Key: <key> #
```
表单字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `files[]` | File[] | 是 | 图片文件数组(上限由套餐决定) |
| `compression_rate` | Integer | 否 | 压缩率 1-100数值越大压缩越强优先级高于 `level` |
| `level` | String | 否 | `high` / `medium` / `low`(兼容参数) |
| `output_format` | String | 否 | 已停用,仅支持保持原格式 |
| `preserve_metadata` | Boolean | 否 | 是否保留元数据(默认 `false` |
响应:
```json
{
"success": true,
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440200",
"total_files": 10,
"status": "pending",
"status_url": "/compress/tasks/550e8400-e29b-41d4-a716-446655440200"
}
}
```
配额规则补充:
- 若本周期剩余单位不足以覆盖本次上传的文件数,服务端应直接返回 `402 QUOTA_EXCEEDED`(不创建任务)。
### 5.4 查询任务状态
```http
GET /compress/tasks/{task_id}
Authorization: Bearer <token> # X-API-Key Cookie
```
响应:
```json
{
"success": true,
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440200",
"status": "completed",
"progress": 100,
"total_files": 10,
"completed_files": 10,
"failed_files": 0,
"files": [
{
"file_id": "550e8400-e29b-41d4-a716-446655440201",
"original_name": "photo1.png",
"original_size": 1024000,
"compressed_size": 256000,
"saved_percent": 75.0,
"status": "completed",
"download_url": "/downloads/550e8400-e29b-41d4-a716-446655440201"
}
],
"download_all_url": "/downloads/tasks/550e8400-e29b-41d4-a716-446655440200",
"created_at": "2025-01-15T10:30:00Z",
"completed_at": "2025-01-15T10:31:00Z",
"expires_at": "2025-01-22T10:30:00Z"
}
}
```
### 5.5 取消任务(可选)
```http
POST /compress/tasks/{task_id}/cancel
Authorization: Bearer <token>
```
### 5.6 删除任务与文件(隐私/合规)
```http
DELETE /compress/tasks/{task_id}
Authorization: Bearer <token>
```
---
## 6. 下载接口
### 6.1 下载单个文件
```http
GET /downloads/{file_id}
Authorization: Bearer <token> # X-API-Key Cookie
```
### 6.2 下载批量 ZIP
```http
GET /downloads/tasks/{task_id}
Authorization: Bearer <token> # X-API-Key Cookie
```
---
## 7. 用户接口
### 7.1 获取当前用户信息
```http
GET /user/profile
Authorization: Bearer <token>
```
响应(示例):
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "myusername",
"role": "user"
}
}
```
### 7.2 更新用户信息
```http
PUT /user/profile
Authorization: Bearer <token>
Content-Type: application/json
```
### 7.3 修改密码
```http
PUT /user/password
Authorization: Bearer <token>
Content-Type: application/json
```
### 7.4 获取压缩历史
```http
GET /user/history?page=1&limit=20
Authorization: Bearer <token>
```
---
## 8. API Key 管理
### 8.1 获取 API Key 列表
```http
GET /user/api-keys
Authorization: Bearer <token>
```
### 8.2 创建 API Key
```http
POST /user/api-keys
Authorization: Bearer <token>
Content-Type: application/json
```
请求体:
```json
{ "name": "Production Server", "permissions": ["compress", "batch_compress"] }
```
响应:
```json
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440300",
"name": "Production Server",
"key": "if_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"message": "请保存此 Key它只会显示一次"
}
}
```
### 8.3 轮换 API Key可选
```http
POST /user/api-keys/{key_id}/rotate
Authorization: Bearer <token>
```
### 8.4 删除/禁用 API Key
```http
DELETE /user/api-keys/{key_id}
Authorization: Bearer <token>
```
---
## 9. 计费与用量Billing
### 9.1 获取套餐列表(公开)
```http
GET /billing/plans
```
响应(示例):
```json
{
"success": true,
"data": {
"plans": [
{
"id": "550e8400-e29b-41d4-a716-446655440900",
"code": "pro_monthly",
"name": "Pro月付",
"currency": "CNY",
"amount_cents": 1999,
"interval": "monthly",
"included_units_per_period": 10000,
"max_file_size_mb": 20,
"max_files_per_batch": 50,
"retention_days": 7,
"features": { "webhook": true }
}
]
}
}
```
### 9.2 获取当前订阅
```http
GET /billing/subscription
Authorization: Bearer <token>
```
### 9.3 获取当期用量
```http
GET /billing/usage
Authorization: Bearer <token>
```
响应:
```json
{
"success": true,
"data": {
"period_start": "2025-01-01T00:00:00Z",
"period_end": "2025-02-01T00:00:00Z",
"used_units": 120,
"included_units": 10000,
"bonus_units": 500,
"total_units": 10500,
"remaining_units": 10380
}
}
```
### 9.4 创建 Checkout订阅/升级)
```http
POST /billing/checkout
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: <key>
```
请求体:
```json
{ "plan_id": "550e8400-e29b-41d4-a716-446655440900" }
```
响应:
```json
{ "success": true, "data": { "checkout_url": "https://pay.example.com/..." } }
```
### 9.5 打开客户 Portal管理支付方式/取消订阅)
```http
POST /billing/portal
Authorization: Bearer <token>
```
### 9.6 发票列表
```http
GET /billing/invoices?page=1&limit=20
Authorization: Bearer <token>
```
---
## 10. Webhooks支付回调
> 无需登录;必须验签与幂等处理,详见 `docs/billing.md` 与 `docs/security.md`。
### 10.1 Stripe 回调(示例)
```http
POST /webhooks/stripe
Content-Type: application/json
Stripe-Signature: t=...,v1=...
```
---
## 11. 管理员接口
> 需要管理员权限(`role: admin`
### 11.1 获取系统统计
```http
GET /admin/stats
Authorization: Bearer <admin_token>
```
### 11.2 用户管理(示例)
```http
GET /admin/users?page=1&limit=20&search=keyword
Authorization: Bearer <admin_token>
```
### 11.3 系统配置
```http
GET /admin/config
Authorization: Bearer <admin_token>
```
```http
PUT /admin/config
Authorization: Bearer <admin_token>
Content-Type: application/json
```
### 11.4 任务管理
```http
GET /admin/tasks?status=processing&page=1
Authorization: Bearer <admin_token>
```
```http
POST /admin/tasks/{task_id}/cancel
Authorization: Bearer <admin_token>
```
### 11.5 计费管理(建议)
```http
GET /admin/billing/subscriptions?page=1&limit=20
Authorization: Bearer <admin_token>
```
```http
POST /admin/billing/credits
Authorization: Bearer <admin_token>
Content-Type: application/json
```
---
## 12. WebSocket网站任务进度
网站侧可用 WebSocket 或 SSESSE 更易穿透代理)。当前先保留 WebSocket 方案:
```
ws://your-domain.com/ws/tasks/{task_id}?token=<jwt_token>
```
消息(示例):
```json
{ "type": "progress", "data": { "task_id": "550e8400-e29b-41d4-a716-446655440200", "progress": 50, "completed_files": 5 } }
```