Files
ystp/docs/api.md
2026-07-25 16:41:31 +08:00

20 KiB
Raw Blame History

API 接口文档v1- ImageForge

面向两类使用者:

  • 网站Web:上传/批量/历史/账单等(可能包含匿名试用)。
  • 对外 APIDeveloper APIAPI Key 调用、可计量可计费、适配 CI/CD 与服务端集成。

产品范围与计费口径见:

  • docs/prd.md
  • docs/billing.md

1. 基础信息

  • Base URL: https://your-domain.com/api/v1
  • 数据格式: JSON除明确标注“返回二进制”接口
  • 时间格式: ISO 8601 / UTC2025-01-15T10:30:00Z
  • ID 格式: UUID 字符串

2. 认证

支持三种身份:

2.1 JWT网站/管理后台)

Authorization: Bearer <token>

2.2 API Key对外 API

X-API-Key: <your-api-key>

注意:仅 ProBusiness 套餐用户可创建 API Key。Free 用户尝试创建时返回 FORBIDDENHTTP 403)。

2.3 匿名试用(仅网站场景)

  • 不提供 API Key
  • 通过 Cookie 维持匿名会话(服务端签发),仅允许较小文件与较低频率。
  • 每日 10 次(以成功压缩文件数计);超出返回 QUOTA_EXCEEDEDHTTP 402)。
  • 日界自然日UTC+8次日 00:00 重置。
  • 匿名试用硬限制Cookie + IP 双限制(两者任一超出都拒绝),降低刷会话绕过风险。

3. 通用约定

3.1 幂等(强烈建议)

对会产生计费/创建任务的接口,建议客户端传:

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

成功:

{ "success": true, "data": {} }

错误:

{
  "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 用户注册

POST /auth/register
Content-Type: application/json

请求体:

{ "email": "user@example.com", "password": "securepassword123", "username": "myusername" }

响应:

{
  "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 用户登录

POST /auth/login
Content-Type: application/json

请求体:

{ "email": "user@example.com", "password": "securepassword123" }

响应:

{
  "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

POST /auth/refresh
Authorization: Bearer <token>

4.4 登出

POST /auth/logout
Authorization: Bearer <token>

4.5 发送验证邮件

用户注册后自动发送一次;此接口用于重新发送。

POST /auth/send-verification
Authorization: Bearer <token>

限流:同一用户 1 分钟内最多 1 次

响应:

{ "success": true, "data": { "message": "验证邮件已发送,请查收" } }

4.6 验证邮箱

POST /auth/verify-email
Content-Type: application/json

请求体:

{ "token": "verification-token-from-email" }

响应:

{ "success": true, "data": { "message": "邮箱验证成功" } }

4.7 请求密码重置

POST /auth/forgot-password
Content-Type: application/json

请求体:

{ "email": "user@example.com" }

限流:同一 IP 1 分钟内最多 3 次

响应(无论邮箱是否存在都返回成功,防止枚举):

{ "success": true, "data": { "message": "如果该邮箱已注册,您将收到重置邮件" } }

4.8 重置密码

POST /auth/reset-password
Content-Type: application/json

请求体:

{ "token": "reset-token-from-email", "new_password": "new-secure-password" }

响应:

{ "success": true, "data": { "message": "密码重置成功,请重新登录" } }

5. 图片压缩接口

5.1 单图压缩(同步,返回 JSON + 下载链接)

适用于网站与轻量同步调用(服务端可选择是否落盘/落对象存储)。

POST /compress
Content-Type: multipart/form-data
Authorization: Bearer <token>   # 或 X-API-Key网站匿名试用可不带
Idempotency-Key: <key>          # 建议

表单字段:

字段 类型 必填 说明
file File 图片文件
compression_rate Integer 压缩率 1-100JPEG/WebP/AVIF 以该比例为体积上限无损格式为尽力优化同格式且不缩放时100 保留原始编码;优先级高于 level
level String high / medium / low(兼容参数,默认 medium
output_format String 输出格式:png/jpeg/webp/avif/gif/bmp/tiff/ico默认保持原格式ICO 自动等比缩至 256x256 边界)
max_width Integer 大于 0 的最大宽度(等比缩放)
max_height Integer 大于 0 的最大高度(等比缩放)
target_size_bytes Integer 不小于 1024 的目标体积(字节),仅 jpeg/webp/avif 输出支持;不能与 compression_rate 同时指定
preserve_metadata Boolean 是否保留 EXIF/ICC默认 false);元数据输出仅支持 jpeg/png/webp

处理约束:

  • 动画 GIF/APNG/WebP/AVIF 不会静默截取首帧,而是返回 400 UNSUPPORTED_FORMAT
  • EXIF 方向会先应用到像素,再移除或归一化方向标记;透明图片转 JPEG 时以白色合成背景。
  • 目标体积搜索优先保持原分辨率,必要时在清晰度保护范围内缩放;仍无法达到时返回 400 INVALID_REQUEST,不会把超出目标的文件作为成功结果。
  • compression_rate=100 只有在同格式且未指定缩放时属于免计量原样请求;格式转换或缩放后若体积变小,正常计 1 次。

响应:

{
  "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。

POST /compress/direct
Content-Type: multipart/form-data
X-API-Key: <your-api-key>        # 或 Bearer token不建议匿名
Idempotency-Key: <key>           # 建议

表单字段与 /compress 一致(包括 output_formattarget_size_bytes)。

成功响应:

  • HTTP 200
  • Body压缩后的图片二进制
  • Content-Type: image/png / image/jpeg / image/webp / image/avif / image/gif / image/bmp / image/tiff / image/x-icon

建议响应头(示例):

ImageForge-Original-Size: 1024000
ImageForge-Compressed-Size: 256000
ImageForge-Saved-Bytes: 768000
ImageForge-Saved-Percent: 75.0
ImageForge-Units-Charged: 1

5.3 批量压缩(异步任务)

适用于多文件或大文件;由 Worker 处理并持续更新进度。

POST /compress/batch
Content-Type: multipart/form-data
Authorization: Bearer <token>    # 或 X-API-Key
Idempotency-Key: <key>           # 建议

表单字段:

字段 类型 必填 说明
files[] File[] 图片文件数组(上限由套餐决定)
compression_rate Integer 压缩率 1-100JPEG/WebP/AVIF 以该比例为体积上限无损格式为尽力优化同格式且不缩放时100 保留原始编码;优先级高于 level
level String high / medium / low(兼容参数)
output_format String 输出格式:png/jpeg/webp/avif/gif/bmp/tiff/ico(默认保持原格式)
preserve_metadata Boolean 是否保留元数据(默认 false

响应:

{
  "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 查询任务状态

GET /compress/tasks/{task_id}
Authorization: Bearer <token>    # 或 X-API-Key匿名试用需携带 Cookie 会话

响应:

{
  "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 取消任务(可选)

POST /compress/tasks/{task_id}/cancel
Authorization: Bearer <token>

5.6 删除任务与文件(隐私/合规)

DELETE /compress/tasks/{task_id}
Authorization: Bearer <token>

6. 下载接口

6.1 下载单个文件

GET /downloads/{file_id}
Authorization: Bearer <token>    # 或 X-API-Key匿名试用需 Cookie 会话

6.2 下载批量 ZIP

GET /downloads/tasks/{task_id}
Authorization: Bearer <token>    # 或 X-API-Key匿名试用需 Cookie 会话

7. 用户接口

7.1 获取当前用户信息

GET /user/profile
Authorization: Bearer <token>

响应(示例):

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "username": "myusername",
    "role": "user"
  }
}

7.2 更新用户信息

PUT /user/profile
Authorization: Bearer <token>
Content-Type: application/json

7.3 修改密码

PUT /user/password
Authorization: Bearer <token>
Content-Type: application/json

7.4 获取压缩历史

GET /user/history?page=1&limit=20
Authorization: Bearer <token>

8. API Key 管理

8.1 获取 API Key 列表

GET /user/api-keys
Authorization: Bearer <token>

8.2 创建 API Key

POST /user/api-keys
Authorization: Bearer <token>
Content-Type: application/json

请求体:

{ "name": "Production Server", "permissions": ["compress", "batch_compress"] }

响应:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440300",
    "name": "Production Server",
    "key": "if_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "message": "请保存此 Key它只会显示一次"
  }
}

8.3 轮换 API Key可选

POST /user/api-keys/{key_id}/rotate
Authorization: Bearer <token>

8.4 删除/禁用 API Key

DELETE /user/api-keys/{key_id}
Authorization: Bearer <token>

9. 计费与用量Billing

9.1 获取套餐列表(公开)

GET /billing/plans

响应(示例):

{
  "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 获取当前订阅

GET /billing/subscription
Authorization: Bearer <token>

9.3 获取当期用量

GET /billing/usage
Authorization: Bearer <token>

响应:

{
  "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,
    "redeemed_units": 200,
    "total_units": 10700,
    "remaining_units": 10580
  }
}

9.4 创建 Checkout订阅/升级)

POST /billing/checkout
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: <key>

请求体:

{ "plan_id": "550e8400-e29b-41d4-a716-446655440900" }

响应:

{ "success": true, "data": { "checkout_url": "https://pay.example.com/..." } }

9.5 打开客户 Portal管理支付方式/取消订阅)

POST /billing/portal
Authorization: Bearer <token>

9.6 发票列表

GET /billing/invoices?page=1&limit=20
Authorization: Bearer <token>

9.7 兑换套餐卡或次数卡

POST /redemptions/redeem
Authorization: Bearer <token>
Content-Type: application/json

{ "code": "IMG-XXXX-XXXX-XXXX-XXXX" }

9.8 获取自己的兑换记录

GET /redemptions
Authorization: Bearer <token>

次数卡额度拥有独立有效期,扣减时优先使用更早到期的可用额度。套餐卡不会覆盖仍然有效的 Stripe 订阅。


10. Webhooks支付回调

无需登录;必须验签与幂等处理,详见 docs/billing.mddocs/security.md

10.1 Stripe 回调(示例)

POST /webhooks/stripe
Content-Type: application/json
Stripe-Signature: t=...,v1=...

11. 管理员接口

需要管理员权限(role: admin

11.1 获取系统统计

GET /admin/stats
Authorization: Bearer <admin_token>

11.2 用户管理(示例)

GET /admin/users?page=1&limit=20&search=keyword
Authorization: Bearer <admin_token>

11.3 系统配置

GET /admin/config
Authorization: Bearer <admin_token>
PUT /admin/config
Authorization: Bearer <admin_token>
Content-Type: application/json

11.4 任务管理

GET /admin/tasks?status=processing&page=1
Authorization: Bearer <admin_token>
POST /admin/tasks/{task_id}/cancel
Authorization: Bearer <admin_token>

11.5 计费管理(建议)

GET /admin/billing/subscriptions?page=1&limit=20
Authorization: Bearer <admin_token>
POST /admin/billing/credits
Authorization: Bearer <admin_token>
Content-Type: application/json

11.6 S3 存储端点

GET /admin/storage/endpoints
Authorization: Bearer <admin_token>
POST /admin/storage/endpoints
Authorization: Bearer <admin_token>
Content-Type: application/json

{
  "name": "119 高带宽 S3",
  "internal_endpoint": "http://10.70.0.2:3900",
  "public_endpoint": "https://files.example.com",
  "bucket": "imageforge-results",
  "region": "garage",
  "access_key": "<access-key>",
  "secret_key": "<secret-key>",
  "force_path_style": true,
  "presign_ttl_seconds": 300
}
PUT /admin/storage/endpoints/{endpoint_id}
POST /admin/storage/endpoints/{endpoint_id}/test
POST /admin/storage/endpoints/{endpoint_id}/activate
DELETE /admin/storage/endpoints/{endpoint_id}
Authorization: Bearer <admin_token>

凭据加密保存且不通过 API 回传。测试接口执行 Bucket 检查、内部临时对象读写删和公网预签名下载;激活接口会再次测试并原子切换活动端点。编辑端点时,后端先测试候选配置并抽查一个历史对象,全部通过后才以并发版本校验方式保存;活动端点编辑成功后保持活动,测试失败则原配置不变。

删除接口对活动或仍有关联对象的端点执行软删除:端点立即停止接收新对象并从管理列表隐藏;若删除的是活动端点,新写入自动回退本地。历史对象继续使用保留的加密配置下载和清理,关联对象清空且软删除超过 30 天后,后台任务才彻底移除端点凭据。

列表响应中的 local_object_countlocal_stored_bytes 统计当前实际位于应用服务器的成品。存在活动 S3 时,新对象仍优先写入 S3若本次 S3 写入失败,则自动回退本地并按实际后端完成下载和到期清理。

11.7 兑换码管理

GET /admin/redemption-codes?page=1&limit=50
Authorization: Bearer <admin_token>
POST /admin/redemption-codes
Authorization: Bearer <admin_token>
Content-Type: application/json

{
  "benefit_kind": "units",
  "units": 100,
  "duration_days": 30,
  "quantity": 10,
  "redeem_before": "2026-12-31T15:59:59Z",
  "note": "活动批次"
}

套餐卡使用 benefit_kind: "plan" 并传入 plan_id。完整兑换码只在创建响应中返回一次,数据库只保存 HMAC 哈希和脱敏标识。

PUT /admin/redemption-codes/{code_id}
Authorization: Bearer <admin_token>
Content-Type: application/json

{ "is_active": false }

11.8 邮箱验证开关

GET /admin/authPUT /admin/auth 的配置体包含 email_verification_required。该配置独立于 SMTP修改后立即生效不需要重启服务。


12. WebSocket网站任务进度

网站侧可用 WebSocket 或 SSESSE 更易穿透代理)。当前先保留 WebSocket 方案:

ws://your-domain.com/ws/tasks/{task_id}?token=<jwt_token>

消息(示例):

{ "type": "progress", "data": { "task_id": "550e8400-e29b-41d4-a716-446655440200", "progress": 50, "completed_files": 5 } }