原始 Markdown:/docs/raw
# Shortpress Visitor API 接口参考
## 1. 基本信息
Shortpress Visitor API 是 Visitor App 的 Cloudflare Worker BFF。
生产地址:
- `https://app-api.higoon.xyz`
- `https://app-api.higoon.art`
本地开发地址:
```text
http://127.0.0.1:8790
```
除代理上游返回其他 Content-Type 的情况外,JSON 响应通常包含:
```http
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
```
## 2. 接口总览
| 方法 | 路径 | 类型 | 鉴权 |
|------|------|------|------|
| GET, HEAD | `/docs` | 本地文档 | 否 |
| GET, HEAD | `/docs/raw` | 本地文档 | 否 |
| GET, HEAD | `/api/create-page/config` | 分流配置 | 否 |
| GET, HEAD | `/api/home/tabs` | 本地数据 | 否 |
| GET, HEAD | `/api/home/trending` | 分流 Feed | 否 |
| GET, HEAD | `/api/home/new` | 分流 Feed | 否 |
| GET, HEAD | `/api/home/top_rated` | 分流 Feed | 否 |
| GET, HEAD | `/api/home/most_favorited` | 分流 Feed | 否 |
| GET, HEAD | `/api/home/short` | 分流 Feed | 否 |
| POST | `/api/home/template/generate` | 本地组装后代理 | Token |
| GET | `/api/app/update/check` | D1 | 设备上下文 |
| POST | `/api/ads/facebook/events` | 本地 CAPI | Token |
| GET, HEAD | `/api/payment/coins/package/list` | 代理 | 建议 Token |
| GET, HEAD | `/api/payment/subscription/package/list` | 代理 | 建议 Token |
| GET, HEAD | `/api/payment/coins/balance` | 代理 | Token |
| POST | `/api/payment/iap/verify` | 代理 | Token |
| POST | `/api/payment/iap/verify-subscription` | 代理 | Token |
| POST | `/api/user/login` | 代理 | 否 |
| POST | `/api/user/login/app-code` | 代理 | 否 |
| POST | `/api/user/app-login-code` | 代理 | Token |
| POST | `/api/user/register` | 代理 | 否 |
| GET, HEAD | `/api/user/profile` | 代理 | Token |
| POST | `/api/user/change-password` | 代理 | Token |
| POST | `/api/user/delete-account` | 代理 | Token |
| GET, HEAD | `/api/plugins/video/query` | 代理 | Token |
| POST | `/api/plugins/video/generate` | 代理 | Token |
| GET, HEAD | `/api/client/creations` | 代理 | Token |
| DELETE | `/api/client/creations/{taskId}` | 代理 | Token |
| POST | `/api/client/upload` | 代理 | 否(Token 可选透传) |
鉴权最终由上游服务执行,Worker 本身不验证 Bearer Token。
Higoon 代理接口通常使用统一响应信封:
```json
{
"code": 0,
"info": "ok",
"data": {}
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | integer | 业务状态码,`0` 通常表示成功 |
| `info` | string | 状态或错误说明 |
| `data` | any | 接口业务数据 |
## 3. 公共请求头
| Header | 格式 | 用途 |
|--------|------|------|
| `X-Device-Context` | base64url JSON | 分流与 App 更新检查 |
| `Authorization` | `Bearer <accessToken>` | 用户、支付、IAP、视频和上传 |
| `X-Site-Id` | UUID/字符串 | 代理接口站点标识 |
| `X-Locale` | 如 `en`、`zh-CN` | 透传给上游 |
| `X-App-Version` | 如 `1.0.4` | 透传给上游 |
| `CF-Connecting-IP` | IPv4 | Cloudflare 注入的客户端 IP |
| `X-Debug-IP` | IPv4 | 本地调试 IP 覆盖 |
若未提供 `X-Site-Id`,Worker 会使用 `HIGOON_SITE_ID`。
## 4. X-Device-Context
Header 是紧凑 JSON 的 base64url 编码结果。
编码前示例:
```json
{
"v": 1,
"ts": 1787760000000,
"ap": {
"v": "1.0.3",
"b": "42",
"p": "ios",
"id": "com.example.visitor"
},
"dev": {
"em": 0,
"jb": 0,
"vpn": 0
},
"adj": {
"net": "Facebook Ads",
"camp": "campaign-1"
}
}
```
字段说明:
| 字段 | 类型 | 说明 |
|------|------|------|
| `v` | number | 上下文协议版本,必填 |
| `ts` | number | 时间戳,必填;当前服务端不校验有效期 |
| `ap.v` | string | App 版本 |
| `ap.b` | string | build number |
| `ap.p` | string | `ios` 或 `android` |
| `ap.id` | string | Bundle ID 或 Android 包名 |
| `dev.em` | `0|1` | `1` 表示模拟器 |
| `dev.jb` | `0|1` | `1` 表示越狱或 Root |
| `dev.vpn` | `0|1|null` | VPN 状态,当前不参与分流 |
| `adj.net` | string | Adjust network |
| `adj.camp` | string | Adjust campaign |
| `adj.grp` | string | Adjust ad group |
| `adj.cre` | string | Adjust creative |
| `dbg.ip` | string | 调试 IP,当前优先于真实客户端 IP |
无效的设备上下文在普通分流接口中按“无设备信息”处理;在更新检查接口中返回 HTTP 400。
## 5. Organic / Ad 分流
以下接口使用流量分流:
- `/api/create-page/config`
- `/api/home/trending`
- `/api/home/new`
- `/api/home/top_rated`
- `/api/home/most_favorited`
- `/api/home/short`
`/api/create-page/config` 和全部 `/api/home/*` Feed 都先应用 `TRAFFIC_MODE`。固定模式 `0`、`1` 直接返回,不查询用户资料;只有动态模式 `2` 才在请求携带 `Authorization: Bearer <accessToken>` 时查询 `/api/user/profile`。动态模式下 `premiumType >= 1` 返回 ad,并设置 `X-Traffic-Reason: vip_user`。无 Token、非 VIP、Token 无效或资料服务暂时不可用时,继续执行基础规则。
`TRAFFIC_MODE`:
| 值 | 结果 |
|----|------|
| `0` | ad |
| `1` | organic |
| `2` | 动态分类 |
Create Page 与 Home Feed 分类优先级:
1. `TRAFFIC_MODE=0`:`ad`,`TRAFFIC_MODE=1`:`organic`;
2. 动态模式下,已登录且 `premiumType >= 1`:`ad / vip_user`;
3. 动态模式下 IP 命中 organic CIDR:`organic / ip_organic`;
4. App 版本高于 D1 最新版本:`organic / app_version_newer`;
5. 模拟器或越狱/Root:`organic / emulator_or_rooted`;
6. Adjust 广告归因:`ad / adjust_ad`;
7. 其他情况:`organic / adjust_organic`。
动态模式响应头示例:
```http
X-Traffic-Variant: ad
X-Traffic-Reason: adjust_ad
```
> 注意:设备上下文目前没有签名,调用方可以构造相关字段。分流结果不应作为鉴权或付费权限判断依据。
## 6. 通用错误
### 404 未找到
```json
{
"error": "not_found"
}
```
### 405 方法不允许
```json
{
"error": "method_not_allowed"
}
```
### 502 上游不可用
```json
{
"error": "upstream_unavailable"
}
```
代理接口通常保留上游 HTTP 状态码和响应体。只有 fetch 抛出异常时,通用代理才生成 `upstream_unavailable`。
## 7. 文档接口
### GET /docs
返回内嵌的 HTML 文档页。
```bash
curl https://app-api.higoon.xyz/docs
```
### GET /docs/raw
返回内嵌的 Markdown 文档。
```bash
curl https://app-api.higoon.xyz/docs/raw
```
两个接口都支持 HEAD,HEAD 固定返回 HTTP 200 且无响应体。
## 8. Create Page
### GET /api/create-page/config
根据分流结果返回对应创作页 JSON 原文。
请求示例:
```bash
curl -H "X-Device-Context: <base64url-context>" \
https://app-api.higoon.xyz/api/create-page/config
```
响应结构:
```json
{
"defaults": {
"creationParams": {},
"defaultTab": "imageToVideo",
"defaultModelByTab": {}
},
"modes": {}
}
```
可通过以下字段验证分流:
| 字段 | organic | ad |
|------|---------|----|
| `defaults.defaultModelByTab.imageToVideo` | `ViduQ2` | `Spicy3Ultimate` |
| `defaults.defaultModelByTab.textToVideo` | 无 | `A4` |
HEAD 会执行分流并返回流量 Header,但不返回配置响应体。
## 9. Home
### GET /api/home/tabs
返回固定 Tab 列表。
响应示例:
```json
{
"tabs": [
{
"name": "Trending",
"endpoint": "/api/home/trending",
"description": "热门列表"
},
{
"name": "New",
"endpoint": "/api/home/new",
"description": "最新列表"
},
{
"name": "Top Rated",
"endpoint": "/api/home/top_rated",
"description": "高评分列表"
},
{
"name": "Most Favorited",
"endpoint": "/api/home/most_favorited",
"description": "最多收藏列表"
},
{
"name": "Short",
"endpoint": "/api/home/short",
"description": "短视频列表(duration ≤ 5 秒)"
}
]
}
```
### GET /api/home/{feed}
`feed` 可取:
- `trending`
- `new`
- `top_rated`
- `most_favorited`
- `short`
Query 参数:
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `page` | 正整数 | `1` | ad 上游分页;organic 仅用于响应元数据 |
| `perPage` | 正整数 | `30` | ad 上游分页;organic 实际返回 12~16 条 |
请求示例:
```bash
curl "https://app-api.higoon.xyz/api/home/trending?page=1&perPage=30"
```
organic 响应示例:
```json
{
"feed": "trending",
"page": 1,
"perPage": 14,
"total": 80,
"maxReached": true,
"data": [
{
"vid": "video-id",
"title": "Example",
"username": "Shortpress",
"modelName": "Example",
"isPublic": true,
"isVip": false,
"price": 0,
"keywords": [],
"cover": "https://example.com/cover.jpg",
"local_path": "https://example.com/video.mp4",
"input_img": "",
"modelRating": 5,
"status": 2,
"duration": 5,
"config": {
"coin": 0,
"effectId": "",
"templateType": ""
}
}
]
}
```
ad 流量响应体由 Playbox Explore API 决定。
实际 HEAD 行为:Home Feed 的 HEAD 请求直接返回本地 HTTP 200,不访问上游、不执行流量分类,也不包含流量 Header。它不保证与 GET 状态一致。
### POST /api/home/template/generate
使用 Home Feed 返回的完整 `item` 和用户上传后的图片 URL 创建视频。请求必须带 `Authorization: Bearer <accessToken>` 与 `X-Site-Id`。
```json
{
"item": {
"vid": "video-id",
"local_path": "https://cdn.example.com/template.mp4",
"duration": 8,
"config": {
"effectId": "effect-id",
"templateType": "vidu"
}
},
"imageUrl": "https://cdn.example.com/upload/person.jpg"
}
```
Worker 根据 `item` 生成受控的上游请求,客户端不需要也不能通过此接口指定 `model` 或价格:
| `item` 类型 | 判定字段 | 上游模型 | 必需字段 |
|-------------|----------|----------|----------|
| 普通 dance 模板 | `config.templateType=dance` | `aitubo_dance` | `vid`、`config.effectId` |
| 普通 vidu 模板 | `config.templateType=vidu` | `aitubo_template` | `vid`、`config.effectId` |
| 普通 spicy 模板 | `config.templateType=spicy` | `sora_template` | `vid`、`config.effectId` |
| 广告 Home 模板 | 未提供 `config.templateType` | `mulerouter/w3.0-video` | `vid`、`local_path`;`duration` 缺失或不大于 0 时取 `8` |
成功响应为现有 `/api/plugins/video/generate` 的原始响应,保存其中的 `data.task_id` 和 `data.model`,再调用查询接口。请求 JSON 无效、字段缺失或模板类型不支持时返回 HTTP 400:
```json
{
"error": "invalid_request",
"message": "item.vid 不能为空"
}
```
## 10. App 更新检查
### GET /api/app/update/check
从 `X-Device-Context.ap` 读取平台、Bundle ID、版本号和 build number,然后查询 D1 最新 published 发版。
请求示例:
```bash
curl -H "X-Device-Context: <base64url-context>" \
https://app-api.higoon.xyz/api/app/update/check
```
无效上下文:
```http
HTTP/1.1 400 Bad Request
```
```json
{
"error": "invalid_device_context"
}
```
没有对应发版:
```http
HTTP/1.1 404 Not Found
```
```json
{
"error": "release_not_found"
}
```
存在更新:
```json
{
"updateAvailable": true,
"forceUpdate": true,
"release": {
"version": "1.0.4",
"buildNumber": 44,
"forceUpdate": true,
"storeUrl": "https://apps.apple.com/app/id0000000000",
"downloadUrl": null,
"updateUrl": "https://apps.apple.com/app/id0000000000",
"updateKind": "store",
"releaseNotes": "Bug fixes"
}
}
```
无需更新:
```json
{
"updateAvailable": false,
"forceUpdate": false,
"release": null
}
```
当前判定规则:
- 只比较版本号前三段;
- 客户端 build number 不参与是否更新判断;
- `min_version`、`min_build_number` 不参与强制更新判断;
- Android 优先使用 `downloadUrl`;
- iOS 只使用 `storeUrl`;
- 该接口不支持 HEAD。
## 11. Payment
支付接口默认代理到 `HIGOON_PAYMENT_BASE_URL`。
### GET /api/payment/coins/package/list
金币套餐列表。
Query:
| 参数 | 必填 | 说明 |
|------|------|------|
| `siteId` | 否 | 缺失时使用 Worker 默认 site ID |
请求体:无。鉴权:不需要。
Worker 会同时向上游发送 `siteId` 和 `site_id`,并设置:
```http
X-Client-Platform: ios
```
响应 `data[]` 字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `packageId` | string | 套餐 ID |
| `name` | string | 套餐名称 |
| `description` | string | 套餐说明 |
| `features` | JSON | 功能卖点列表或结构化配置,可能省略 |
| `coinAmount` | integer | 金币数量 |
| `price` | number/string | 当前价格;具体 JSON 表示由服务端 Money 类型决定 |
| `originalPrice` | number/string | 原价,可能省略 |
| `currency` | string | 货币代码 |
| `discountPercentage` | integer | 折扣百分比,可能省略 |
| `status` | integer | `1` 为启用;客户端接口只返回启用套餐 |
| `iosProductId` | string | App Store 商品 ID |
### GET /api/payment/subscription/package/list
订阅套餐列表,Query、站点 ID 补齐和平台 Header 行为与金币套餐相同。请求体和鉴权均不需要。
响应 `data[]` 字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `packageId` | string | 套餐 ID |
| `siteId` | string | 所属站点 ID |
| `name` | string | 套餐名称 |
| `description` | string | 套餐说明 |
| `interval` | string | 计费周期,如 `weekly`、`monthly`、`yearly` |
| `price` | number/string | 当前价格;具体 JSON 表示由服务端 Money 类型决定 |
| `originalPrice` | number/string | 原价,可能省略 |
| `currency` | string | 货币代码 |
| `discountPercentage` | integer | 折扣百分比,可能省略 |
| `coins` | integer | 套餐赠送金币数量 |
| `rights` | JSON | 订阅权益配置 |
| `status` | integer | `1` 为启用;客户端接口只返回启用套餐 |
| `iosProductId` | string | App Store 商品 ID |
| `createdAt` | integer | 创建时间戳 |
### GET /api/payment/coins/balance
查询金币余额。
| 参数位置 | 参数 | 必填 | 说明 |
|----------|------|------|------|
| Header | `Authorization` | 是 | `Bearer <accessToken>` |
| Header | `X-Site-Id` | 否 | Worker 未收到时自动补默认站点 ID |
| Query | `siteId` | 否 | Worker 会转换并同时向上游补 `siteId`、`site_id` |
请求体:无。
```bash
curl -H "Authorization: Bearer <accessToken>" \
"https://app-api.higoon.xyz/api/payment/coins/balance?siteId=<siteId>"
```
成功响应 `data` 字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `balance` | integer | 当前金币余额 |
| `totalEarned` | integer | 累计获得金币 |
| `totalSpent` | integer | 累计消费金币 |
| `totalRealMoneySpent` | number/string | 累计真实货币消费金额 |
### POST /api/payment/iap/verify
iOS 金币购买验证。
请求头:
```http
Authorization: Bearer <accessToken>
X-Site-Id: <siteId>
Content-Type: application/json
```
请求体:
```json
{
"transactionId": "2000000123456789",
"account": "apple",
"packageName": "com.example.visitor",
"packageId": "server-package-id"
}
```
请求体字段:
| 字段 | 类型 | iOS 必填 | 说明 |
|------|------|----------|------|
| `transactionId` | string | 是 | Apple StoreKit 交易 ID |
| `account` | string | 是 | 支付渠道标识,iOS 使用 `apple` |
| `packageName` | string | 客户端约定传入 | App Bundle ID;Google 校验时是包名 |
| `packageId` | string | 是 | 服务端数据库套餐 ID,不是 App Store Product ID |
| `purchaseToken` | string | 否 | Google Play purchase token;iOS 不使用 |
服务端 Handler 没有为这些字段声明 Gin `binding:"required"`,因此“iOS 必填”表示业务成功所需字段,而不是 JSON 绑定阶段的强校验。
成功响应:
```json
{
"code": 0,
"info": "ok",
"data": {
"productId": "app-store-product-id"
}
}
```
上游 `code === 0` 后,Worker 会在后台(`waitUntil`)用 Conversions API 上报 `Purchase`,`eventId` 为 `transactionId`。验单响应形状不变;CAPI 失败不影响发货。
### POST /api/payment/iap/verify-subscription
iOS 订阅购买验证,请求头、请求体及字段含义与金币验证相同。
成功响应 `data`:
| 字段 | 类型 | 说明 |
|------|------|------|
| `isActive` | boolean | 订阅当前是否有效 |
| `sandbox` | boolean | 是否为沙盒交易 |
| `isInFreeTrial` | boolean | 是否处于免费试用期 |
| `autoRenewStatus` | boolean | 是否开启自动续订 |
订阅验单成功后,Worker 同样会后台上报 CAPI `Purchase`(`contentCategory=subscription`)。
### Payment 上游映射
| Worker 路径 | 上游路径 |
|-------------|----------|
| `/api/payment/coins/package/list` | `/api/client/payment/coins/package/list` |
| `/api/payment/subscription/package/list` | `/api/client/payment/subscription/package/list` |
| `/api/payment/coins/balance` | `/api/client/payment/coins/balance` |
| `/api/payment/iap/verify` | `/api/iap/verify` |
| `/api/payment/iap/verify-subscription` | `/api/iap/verifysub` |
Payment GET 接口的 HEAD 请求直接返回本地 HTTP 200,不访问上游。
## 12. User
### App 登录码
`POST /api/user/app-login-code` 生成登录码,需要当前网页用户的 `Authorization: Bearer <token>`、所属站点的 `X-Site-Id`,请求体为 `{"appId":"com.veloxreel.app"}`。
`POST /api/user/login/app-code` 使用登录码登录,无需用户 Token,请求体为 `{"loginCode":"<六位登录码>"}`。`X-Site-Id` 必须与生成登录码时的站点一致。
两个接口均转发到同名上游路径,保留请求体及 `Authorization`、`X-Site-Id`、`X-App-Id`、`X-IOS-Proof` 等请求头。是否要求 App 身份证明由上游配置决定。上游响应状态和正文原样返回,并禁止缓存。
### POST /api/user/login
请求 Header:
| 参数 | 必填 | 说明 |
|------|------|------|
| `X-Site-Id` | 否 | 上游要求站点 ID;Worker 会在缺失时补默认值 |
请求体字段:
| 字段 | 类型 | 必填 | 校验 |
|------|------|------|------|
| `email` | string | 是 | 必须符合邮箱格式 |
| `password` | string | 是 | 非空;登录接口不额外检查最短长度 |
```json
{
"email": "user@example.com",
"password": "123456"
}
```
成功响应:
```json
{
"code": 0,
"info": "success",
"data": {
"accessToken": "<token>",
"ver": "1.0.3"
}
}
```
`ver` 是服务端保存或返回的 App 版本信息。
### POST /api/user/register
请求 Header 与登录相同。请求体字段:
| 字段 | 类型 | 必填 | 校验 |
|------|------|------|------|
| `email` | string | 是 | 必须符合邮箱格式 |
| `password` | string | 是 | 至少 6 个字符 |
| `nickname` | string | 否 | 没有显式长度约束 |
```json
{
"nickname": "user",
"email": "user@example.com",
"password": "123456"
}
```
成功响应 `data`:
| 字段 | 类型 | 说明 |
|------|------|------|
| `accessToken` | string | 注册后签发的访问令牌 |
| `siteId` | string | 当前站点 ID |
| `ver` | string | 请求 `X-App-Version`;缺失时为空字符串 |
### GET /api/user/profile
| 参数位置 | 参数 | 必填 | 说明 |
|----------|------|------|------|
| Header | `Authorization` | 是 | `Bearer <accessToken>` |
| Header | `X-Site-Id` | 否 | Worker 会在缺失时补默认值 |
Query 和请求体:无。
```bash
curl -H "Authorization: Bearer <accessToken>" \
https://app-api.higoon.xyz/api/user/profile
```
成功响应 `data` 字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | string | 用户 ID |
| `email` | string | 邮箱 |
| `nickname` | string | 昵称 |
| `avatarUrl` | string | 头像 URL |
| `premiumType` | integer | `0` 免费,`1` premium;其他值由服务端扩展 |
| `onetimeSub` | integer | `1` 表示一次性订阅 |
| `premiumExpiresAt` | integer | Premium 到期 Unix 时间戳 |
| `autoUnlock` | boolean | 是否自动解锁内容 |
| `status` | integer | 用户状态 |
| `referer` | string | 来源标识 |
| `pixelId` | string | Facebook Pixel ID |
| `loginType` | integer | `0` 邮箱、`1` Google、`2` Facebook、`3` Twitter、`4` TikTok |
| `hasPurchased` | boolean | 是否发生过购买 |
| `ver` | string | App 版本信息 |
HEAD 直接返回本地 HTTP 200,不访问上游。
### POST /api/user/change-password
请求 Header:`Authorization: Bearer <accessToken>`;`X-Site-Id` 缺失时由 Worker 补齐。
请求体字段:
| 字段 | 类型 | 必填 | 校验 |
|------|------|------|------|
| `newPassword` | string | 是 | 6~64 个字符 |
```json
{
"newPassword": "new-password"
}
```
成功时 `data` 为空对象。
### POST /api/user/delete-account
请求 Header:`Authorization: Bearer <accessToken>`;`X-Site-Id` 缺失时由 Worker 补齐。
Query:无。请求体:无。上游仅根据认证用户执行软删除;即使 Worker 收到请求体,上游 Handler 也不会读取。
成功时 `data` 为空对象。
## 13. Video
视频接口默认代理到 `HIGOON_VIDEO_BASE_URL`。
### GET /api/plugins/video/query
Query:
| 参数 | 必填 | 说明 |
|------|------|------|
| `task_id` | 是 | 生成任务 ID |
| `model` | 否 | 与生成请求一致的模型标识;用于预置缓存和响应模型名解析 |
```bash
curl -H "Authorization: Bearer <accessToken>" \
"https://app-api.higoon.xyz/api/plugins/video/query?model=a2eGenerate&task_id=<taskId>"
```
HEAD 直接返回本地 HTTP 200,不访问视频上游。
成功响应 `data` 的统一字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `task_id` | string | 客户端任务 ID |
| `external_task_id` | string | 第三方任务 ID,可能省略 |
| `status` | integer | `1` 处理中、`2` 成功、`3` 失败 |
| `model` | string | 生成模型标识,可能省略 |
| `video_id` | string | 关联业务视频 ID,可能省略 |
| `prompt` | string | 提示词,可能省略 |
| `reference_images` | string[] | 参考图片,可能省略 |
| `videos` | object[] | 视频结果,每项包含 `url`,可能包含 `cover_url` |
| `images` | string[] | 图片结果 URL |
| `error_msg` | string | 失败原因 |
| `retry_count` | integer | 重试次数 |
| `provider_status` | string | 第三方任务状态 |
| `transfer_status` | string | 媒体转存状态 |
| `transfer_retries` | integer | 转存重试次数 |
| `transfer_error` | string | 转存错误 |
| `source_videos` | object[] | 第三方原始视频列表 |
| `delivery_mode` | string | 交付模式 |
| `transfer_retry_at` | string | 下次转存重试时间 |
| `created_at` | string | 创建时间 |
| `updated_at` | string | 更新时间 |
### POST /api/plugins/video/generate
提交视频或图片生成任务。Worker 不解析请求体,生成微服务会校验通用结构。
请求 Header:
| 参数 | 必填 | 说明 |
|------|------|------|
| `Authorization` | 是 | `Bearer <accessToken>`;生成服务只检查 Header 非空,实际身份由调用链处理 |
| `X-Site-Id` | 否 | 生成服务要求非空;Worker 缺失时补默认站点 ID |
| `Content-Type` | 是 | `application/json`;Worker 可在缺失时自动补齐 |
通用请求体字段:
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `model` | string | 是 | 生成器标识,必须是服务端已注册模型 |
| `video_id` | string | 否 | 业务视频/模板 ID;用于计价、扣币和关联记录 |
| `number` | integer | 否 | 生成数量;缺失或 `≤ 0` 按 `1` 计算,总点数为单价乘数量 |
| `args` | object | 是 | 模型参数对象 |
完整请求示例:
```bash
curl -X POST \
-H "Authorization: Bearer <accessToken>" \
-H "X-Site-Id: <siteId>" \
-H "Content-Type: application/json" \
-d '{"model":"viduq2_image","video_id":"template-1","number":1,"args":{"image":"https://example.com/input.jpg","prompt":"gentle movement","duration":"5","resolution":"720p"}}' \
https://app-api.higoon.xyz/api/plugins/video/generate
```
成功响应:
```json
{
"code": 0,
"data": {
"task_id": "client-task-id",
"model": "viduq2_image"
}
}
```
当前已注册的 `model`:
```text
a4
a2eTemplate
a2eGenerate
a2eImage
flux1.0
viduq2
viduq2_image
aitubo_template
aitubo_dance
sora_template
a2eWan2.7
a2eHeadSwap
a2eTalkingPhoto
a2eFaceSwapTask
a2eUpscale
a2eGrokVideo
a2eImageEditor
atlascloudImage
atlascloud/text-to-video
atlascloud/image-to-video
atlascloud/wan-2.7-image-to-video
atlascloud/image-then-video
atlascloud/video-edit
a2eUltra
```
服务端还注册了一个由常量提供名称的增强参考视频模型;该常量的运行时值可能随配置变化,因此不在静态列表中写死。
`args` 是模型相关对象。源码明确读取的常见字段如下;并非每个模型都支持所有字段:
| 字段 | 类型 | 适用示例 | 说明 |
|------|------|----------|------|
| `prompt` | string | 多数生成模型 | 正向提示词 |
| `negative_prompt` | string | 部分视频模型 | 负向提示词 |
| `image`、`image_url`、`sourceImage` | string | 图生视频、舞蹈 | 输入图片 URL;字段名由模型决定 |
| `images`、`input_images` | string[] | 模板、多图/编辑模型 | 输入图片列表 |
| `duration` | string/number | Vidu、视频模型 | 视频时长;具体类型由模型决定 |
| `video_time` | integer | Ultra 类模型 | 视频时长 |
| `resolution` | string | 多数图片/视频模型 | 分辨率,也会影响部分模型计价 |
| `aspect_ratio`、`ratio` | string | Flux、Vidu、模板模型 | 宽高比 |
| `safety_checker` | boolean/string | Flux、Vidu | 是否启用安全检查;部分实现接受布尔字符串 |
| `num_images` | integer | `flux1.0` | 图片生成数量 |
| `performance` | string | `flux1.0` | 性能档位 |
| `mode` | string | `viduq2_image` | 生成模式 |
| `movement_amplitude` | string | `viduq2_image` | 运动幅度 |
| `generate_audio` | boolean | `viduq2_image` | 是否生成音频 |
| `voice_id` | string | `viduq2_image` | 声音 ID |
| `effectId` | string | `aitubo_template` | 特效/模板 ID |
| `scene` | string | `aitubo_template` | 场景参数 |
| `danceId` | string | `aitubo_dance` | 舞蹈模板 ID |
| `faceEmotion` | boolean | `aitubo_dance` | 是否启用面部表情 |
| `background` | boolean | `aitubo_dance` | 是否保留/生成背景 |
| `template_id` | string | `sora_template` | 必填的 Sora 模板 ID |
| `input` | object | AtlasCloud 模型 | 原样传给 AtlasCloud 的嵌套输入对象 |
除 `sora_template.args.template_id` 外,多数生成器没有统一的字段级必填校验,而是将缺失字段交给对应第三方模型处理。调用方应以创作页配置为准构造具体模型的 `args`。
## 14. Creations
### GET /api/client/creations
当前登录用户在本站点最近 24 小时的生成记录。上游:`api.higoon.art` 的 `/api/client/creations`(再转 Generate Redis)。`userId` 只取 JWT,忽略 query。
请求头:
| 参数 | 必填 | 说明 |
|------|------|------|
| `Authorization` | 是 | `Bearer <accessToken>` |
| `X-Site-Id` | 否 | Worker 缺失时补默认站点 ID |
Query:
| 参数 | 必填 | 说明 |
|------|------|------|
| `page` | 否 | 默认 `1` |
| `pageSize` | 否 | 默认 `20`,最大 `100` |
```bash
curl -H "Authorization: Bearer <accessToken>" \
"https://app-api.higoon.xyz/api/client/creations?page=1&pageSize=20"
```
HEAD 直接返回本地 HTTP 200,不访问上游。
成功响应 `data`:
| 字段 | 类型 | 说明 |
|------|------|------|
| `items` | object[] | 记录列表 |
| `total` | integer | 总数 |
| `page` | integer | 当前页 |
| `pageSize` | integer | 每页条数 |
| `hasMore` | boolean | 是否还有下一页 |
`items[]` 字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `taskId` | string | 生成任务 ID |
| `status` | integer | `1` 处理中、`2` 成功、`3` 失败 |
| `model` | string | 生成模型标识 |
| `prompt` | string | 提示词,可能省略 |
| `videos` | object[] | `{ url, coverUrl }`,可能省略 |
| `images` | string[] | 图片结果 URL,可能省略 |
| `errorMsg` | string | 失败原因,可能省略 |
| `createdAt` | integer | Unix 秒 |
| `updatedAt` | integer | Unix 秒 |
### DELETE /api/client/creations/{taskId}
从当前登录用户的创作列表中移除一条记录。只允许删除已结束任务:`status=2`(成功)或 `status=3`(失败);`status=1`(处理中)返回 HTTP 409。删除为软删除,不会删除任务快照、扣费、退款或运营审计记录。
请求头:`Authorization: Bearer <accessToken>`,`X-Site-Id` 可选(Worker 缺失时补默认站点 ID)。路径参数 `taskId` 必填,且必须属于 Token 用户和当前站点。
```bash
curl -X DELETE \
-H "Authorization: Bearer <accessToken>" \
-H "X-Site-Id: 80de3f91-5295-4731-ac33-8893ad3f400c" \
-H "Accept: application/json" \
"https://app-api.higoon.xyz/api/client/creations/<taskId>"
```
Swift 调用示例:
```swift
func deleteCreation(taskId: String, accessToken: String, siteId: String) async throws {
let encodedTaskId = taskId.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? taskId
let url = URL(string: "https://app-api.higoon.art/api/client/creations/\(encodedTaskId)")!
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
request.setValue(siteId, forHTTPHeaderField: "X-Site-Id")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
guard (200...299).contains(http.statusCode) else {
let message = String(data: data, encoding: .utf8) ?? "删除失败"
throw NSError(
domain: "CreationAPI",
code: http.statusCode,
userInfo: [NSLocalizedDescriptionKey: message]
)
}
}
```
成功:
```json
{ "code": 0, "info": "ok", "data": {} }
```
响应状态:
| HTTP | `code` | 说明 |
|------|--------|------|
| `200` | `0` | 删除成功,记录已从当前用户创作列表隐藏 |
| `401` | `401` | Token 缺失、无效或已过期 |
| `404` | `404` | 任务不存在,或不属于当前 Token 用户/站点 |
| `409` | `409` | 任务仍在处理中,只能删除已结束任务 |
处理中任务响应:
```json
{ "code": 409, "info": "只能删除已结束任务", "data": {} }
```
任务不存在或所有权不匹配:
```json
{ "code": 404, "info": "创作记录不存在", "data": {} }
```
删除成功后,App 应立即从本地列表移除该 `taskId`,或重新请求 `GET /api/client/creations`。不要对 `status=1` 的任务显示删除入口。
## 15. Upload
### POST /api/client/upload
上传文件,使用 multipart/form-data。Worker 保留 multipart Content-Type 和 boundary,并流式转发请求体。上游路由不要求登录,Authorization 和 X-Site-Id 均不是必填参数。
Form Data:
| 字段 | 类型 | 必填 | 约束 |
|------|------|------|------|
| `file` | file | 是 | 恰好一个文件;零个返回 400,多个也返回 400 |
代码和 Swagger 将该接口描述为图片上传,但 Handler 不校验 MIME、扩展名或文件大小,只保留原始扩展名并写入存储目录。实际允许范围还可能受到 Gin、反向代理和部署环境的请求体大小限制。
```bash
curl -X POST \
-F "file=@example.jpg" \
https://app-api.higoon.xyz/api/client/upload
```
成功响应:
```json
{
"code": 0,
"info": "ok",
"data": "res/user_upload/<directory>/<filename>.jpg"
}
```
## 16. 完整代理映射
| Worker 路径 | 默认上游 | 上游路径 |
|-------------|----------|----------|
| `/api/payment/coins/package/list` | `api.higoon.art` | `/api/client/payment/coins/package/list` |
| `/api/payment/subscription/package/list` | `api.higoon.art` | `/api/client/payment/subscription/package/list` |
| `/api/payment/coins/balance` | `api.higoon.art` | `/api/client/payment/coins/balance` |
| `/api/payment/iap/verify` | `api.higoon.art` | `/api/iap/verify` |
| `/api/payment/iap/verify-subscription` | `api.higoon.art` | `/api/iap/verifysub` |
| `/api/user/login` | `api.higoon.art` | `/api/user/login` |
| `/api/user/login/app-code` | `api.higoon.art` | `/api/user/login/app-code` |
| `/api/user/app-login-code` | `api.higoon.art` | `/api/user/app-login-code` |
| `/api/user/register` | `api.higoon.art` | `/api/user/register` |
| `/api/user/profile` | `api.higoon.art` | `/api/user/profile` |
| `/api/user/change-password` | `api.higoon.art` | `/api/user/change-password` |
| `/api/user/delete-account` | `api.higoon.art` | `/api/user/delete-account` |
| `/api/plugins/video/query` | `higoon.art` | `/api/plugins/video/query` |
| `/api/plugins/video/generate` | `higoon.art` | `/api/plugins/video/generate` |
| `/api/home/template/generate` | `higoon.art` | `/api/plugins/video/generate`(Worker 先转换请求体) |
| `/api/client/creations` | `api.higoon.art` | `/api/client/creations` |
| `/api/client/creations/{taskId}` | `api.higoon.art` | `/api/client/creations/{taskId}` |
| `/api/client/upload` | `api.higoon.art` | `/api/client/upload` |
## 17. HEAD 请求说明
HEAD 行为并不完全等同于对应 GET:
| 接口类型 | 实际行为 |
|----------|----------|
| `/docs`、`/docs/raw`、Home Tabs | 本地返回 200,无 body |
| Create Page | 执行分流,返回分流 Header,无 body |
| Home Feed | 本地返回 200,不分流、不访问上游 |
| 支付 GET、用户资料、视频查询、创作列表 | 本地返回 200,不访问上游 |
| App 更新检查 | 不支持 HEAD,返回 405 |
因此不能使用代理接口的 HEAD 结果判断上游资源是否存在或上游是否健康。
## 18. 调试示例
### 强制 organic IP
以下 Header 主要适用于本地 Wrangler 调试:
```bash
curl -H "CF-Connecting-IP: 17.1.2.3" \
http://127.0.0.1:8790/api/create-page/config
```
### Adjust 广告归因
构造包含非 organic network 的设备上下文,然后请求:
```bash
curl -H "X-Device-Context: <encoded-context>" \
http://127.0.0.1:8790/api/home/trending
```
预期响应头:
```http
X-Traffic-Variant: ad
X-Traffic-Reason: adjust_ad
```
### 查看在线文档
```bash
curl http://127.0.0.1:8790/docs/raw
```
## 19. Facebook Conversions API
Worker 本地处理,不把 Facebook Access Token 返回给客户端。`pixelId` 来自上游 `GET /api/user/profile`。`action_source` 固定为 `website`。
密钥(不要写入 `wrangler.jsonc` vars):
```bash
wrangler secret put FACEBOOK_CAPI_ACCESS_TOKEN
# staging 测试事件页打开时再设,生产必须为空:
wrangler secret put FACEBOOK_CAPI_TEST_EVENT_CODE
```
本地将 token 放在 `.dev.vars`(见 `.dev.vars.example`)。可选非密配置 `FACEBOOK_EVENT_SOURCE_URL`。
### POST /api/ads/facebook/events
鉴权:`Authorization: Bearer <accessToken>`。Worker 用该 token 拉 profile;无 Bearer 或 profile 失败返回 401。
请求体字段:`eventName`(`PageView` / `Lead` / `InitiateCheckout` / `Purchase`)、`eventId`(Lead / Purchase 必填)、可选 `eventTime`、`eventSourceUrl`、`customData`、`userData`(`clientUserAgent` / `madid` / `anonId`)。
禁止上传:`pixelId`、`accessToken`、`actionSource`、`testEventCode`、明文 `email`。
```bash
curl -sS -X POST http://127.0.0.1:8790/api/ads/facebook/events \
-H "Authorization: Bearer <accessToken>" \
-H "X-Site-Id: <siteId>" \
-H "Content-Type: application/json" \
-d '{"eventName":"Lead","eventId":"task-123"}'
```
成功:`{ "code": 0, "info": "ok", "data": { "accepted": true, "eventName": "Lead", "eventId": "task-123", "fbtraceId": "..." } }`。
站点未配置 profile `pixelId` 或未配置 CAPI token 时返回 HTTP 503。IAP 验单成功后的服务端 Purchase 不改变验单响应。
测试:Events Manager → 测试事件页保持打开,staging 注入 `FACEBOOK_CAPI_TEST_EVENT_CODE`,确认来源为 Server。生产不要设置该 secret。
## 20. 相关文档
- [项目说明](./project-summary.md)
- [原始接口文档](./api.md)
- [项目 README](../README.md)