mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-17 21:34:45 +08:00
docs+ui: add bilingual payment integration doc and rename purchase entry to recharge/subscription
This commit is contained in:
@@ -1,41 +1,40 @@
|
|||||||
# Sub2API Admin API: Payment Integration / 支付集成接口文档
|
# ADMIN_PAYMENT_INTEGRATION_API
|
||||||
|
|
||||||
|
> 单文件中英双语文档 / Single-file bilingual documentation (Chinese + English)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 中文
|
## 中文
|
||||||
|
|
||||||
### 概述
|
### 目标
|
||||||
|
本文档用于对接外部支付系统(如 `sub2apipay`)与 Sub2API 的 Admin API,覆盖:
|
||||||
本文档描述外部支付系统(例如 sub2apipay)对接 Sub2API 时的最小 Admin API 集合,用于完成充值发放与对账。
|
- 支付成功后充值
|
||||||
|
- 用户查询
|
||||||
|
- 人工余额修正
|
||||||
|
- 前端购买页参数透传
|
||||||
|
|
||||||
### 基础地址
|
### 基础地址
|
||||||
|
- 生产:`https://<your-domain>`
|
||||||
|
- Beta:`http://<your-server-ip>:8084`
|
||||||
|
|
||||||
- 生产环境:`https://<your-domain>`
|
### 认证
|
||||||
- Beta 环境:`http://<your-server-ip>:8084`
|
推荐使用:
|
||||||
|
- `x-api-key: admin-<64hex>`
|
||||||
|
- `Content-Type: application/json`
|
||||||
|
- 幂等接口额外传:`Idempotency-Key`
|
||||||
|
|
||||||
### 认证方式
|
说明:管理员 JWT 也可访问 admin 路由,但服务间调用建议使用 Admin API Key。
|
||||||
|
|
||||||
以下接口均建议使用:
|
|
||||||
|
|
||||||
- 请求头:`x-api-key: admin-<64hex>`(服务间调用推荐)
|
|
||||||
- 请求头:`Content-Type: application/json`
|
|
||||||
|
|
||||||
说明:管理员 JWT 也可访问 admin 路由,但机器对机器调用建议使用 Admin API Key。
|
|
||||||
|
|
||||||
### 1) 一步完成:创建兑换码并兑换
|
|
||||||
|
|
||||||
|
### 1) 一步完成创建并兑换
|
||||||
`POST /api/v1/admin/redeem-codes/create-and-redeem`
|
`POST /api/v1/admin/redeem-codes/create-and-redeem`
|
||||||
|
|
||||||
用途:
|
用途:原子完成“创建兑换码 + 兑换到指定用户”。
|
||||||
|
|
||||||
- 原子化完成“创建固定兑换码 + 兑换给指定用户”。
|
|
||||||
- 常用于支付回调成功后的自动充值。
|
|
||||||
|
|
||||||
必需请求头:
|
|
||||||
|
|
||||||
|
请求头:
|
||||||
- `x-api-key`
|
- `x-api-key`
|
||||||
- `Idempotency-Key`
|
- `Idempotency-Key`
|
||||||
|
|
||||||
请求体:
|
请求体示例:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"code": "s2p_cm1234567890",
|
"code": "s2p_cm1234567890",
|
||||||
@@ -46,21 +45,12 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
规则:
|
|
||||||
|
|
||||||
- `code`:外部订单映射的确定性兑换码。
|
|
||||||
- `type`:当前推荐使用 `balance`。
|
|
||||||
- `value`:必须大于 0。
|
|
||||||
- `user_id`:目标用户 ID。
|
|
||||||
|
|
||||||
幂等语义:
|
幂等语义:
|
||||||
|
- 同 `code` 且 `used_by` 一致:`200`
|
||||||
|
- 同 `code` 但 `used_by` 不一致:`409`
|
||||||
|
- 缺少 `Idempotency-Key`:`400`(`IDEMPOTENCY_KEY_REQUIRED`)
|
||||||
|
|
||||||
- 同一 `code` 且 `used_by` 一致:返回 `200`(幂等回放)。
|
curl 示例:
|
||||||
- 同一 `code` 但 `used_by` 不一致:返回 `409`(冲突)。
|
|
||||||
- 缺少 `Idempotency-Key`:返回 `400`(`IDEMPOTENCY_KEY_REQUIRED`)。
|
|
||||||
|
|
||||||
示例:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST "${BASE}/api/v1/admin/redeem-codes/create-and-redeem" \
|
curl -X POST "${BASE}/api/v1/admin/redeem-codes/create-and-redeem" \
|
||||||
-H "x-api-key: ${KEY}" \
|
-H "x-api-key: ${KEY}" \
|
||||||
@@ -75,32 +65,20 @@ curl -X POST "${BASE}/api/v1/admin/redeem-codes/create-and-redeem" \
|
|||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2) 查询用户(可选前置检查)
|
### 2) 查询用户(可选前置校验)
|
||||||
|
|
||||||
`GET /api/v1/admin/users/:id`
|
`GET /api/v1/admin/users/:id`
|
||||||
|
|
||||||
用途:
|
|
||||||
|
|
||||||
- 支付成功后充值前,确认目标用户是否存在。
|
|
||||||
|
|
||||||
示例:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -s "${BASE}/api/v1/admin/users/123" \
|
curl -s "${BASE}/api/v1/admin/users/123" \
|
||||||
-H "x-api-key: ${KEY}"
|
-H "x-api-key: ${KEY}"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3) 余额调整(已存在接口)
|
### 3) 余额调整(已有接口)
|
||||||
|
|
||||||
`POST /api/v1/admin/users/:id/balance`
|
`POST /api/v1/admin/users/:id/balance`
|
||||||
|
|
||||||
用途:
|
用途:人工补偿 / 扣减,支持 `set` / `add` / `subtract`。
|
||||||
|
|
||||||
- 复用现有管理员接口做人工纠偏。
|
|
||||||
- 支持 `set`、`add`、`subtract`。
|
|
||||||
|
|
||||||
示例(扣减):
|
|
||||||
|
|
||||||
|
请求体示例(扣减):
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"balance": 100.0,
|
"balance": 100.0,
|
||||||
@@ -121,36 +99,25 @@ curl -X POST "${BASE}/api/v1/admin/users/123/balance" \
|
|||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4) 购买页跳转 URL Query 参数(iframe 与新窗口统一)
|
### 4) 购买页 URL Query 透传(iframe / 新窗口一致)
|
||||||
|
当 Sub2API 打开 `purchase_subscription_url` 时,会统一追加:
|
||||||
Sub2API 前端在打开 `purchase_subscription_url` 时,会给 iframe 和“新窗口打开”统一追加 query 参数,确保外部支付页拿到一致上下文。
|
- `user_id`
|
||||||
|
- `token`
|
||||||
追加参数:
|
- `theme`(`light` / `dark`)
|
||||||
|
- `ui_mode`(固定 `embedded`)
|
||||||
- `user_id`:当前登录用户 ID
|
|
||||||
- `token`:当前登录 JWT token
|
|
||||||
- `theme`:当前主题(`light` / `dark`)
|
|
||||||
- `ui_mode`:当前 UI 模式(固定 `embedded`)
|
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
https://pay.example.com/pay?user_id=123&token=<jwt>&theme=light&ui_mode=embedded
|
https://pay.example.com/pay?user_id=123&token=<jwt>&theme=light&ui_mode=embedded
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5) 失败处理建议
|
### 5) 失败处理建议
|
||||||
|
- 支付成功与充值成功分状态落库
|
||||||
- 支付状态与充值状态分开落库。
|
- 回调验签成功后立即标记“支付成功”
|
||||||
- 收到并验证支付回调后,立即标记“支付成功”。
|
- 支付成功但充值失败的订单允许后续重试
|
||||||
- 支付成功但充值失败的订单应允许后续重试。
|
- 重试保持相同 `code`,并使用新的 `Idempotency-Key`
|
||||||
- 重试时继续使用同一 `code`,并使用新的 `Idempotency-Key`。
|
|
||||||
|
|
||||||
### 6) `doc_url` 配置建议
|
### 6) `doc_url` 配置建议
|
||||||
|
|
||||||
Sub2API 已支持系统设置中的 `doc_url` 字段。
|
|
||||||
|
|
||||||
推荐配置:
|
|
||||||
|
|
||||||
- 查看链接:`https://github.com/Wei-Shaw/sub2api/blob/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
- 查看链接:`https://github.com/Wei-Shaw/sub2api/blob/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
||||||
- 下载链接:`https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
- 下载链接:`https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
||||||
|
|
||||||
@@ -158,40 +125,35 @@ Sub2API 已支持系统设置中的 `doc_url` 字段。
|
|||||||
|
|
||||||
## English
|
## English
|
||||||
|
|
||||||
### Overview
|
### Purpose
|
||||||
|
This document describes the minimal Sub2API Admin API surface for external payment integrations (for example, `sub2apipay`), including:
|
||||||
This document defines the minimum Admin API surface for integrating external payment systems (for example, sub2apipay) with Sub2API for recharge fulfillment and reconciliation.
|
- Recharge after payment success
|
||||||
|
- User lookup
|
||||||
|
- Manual balance correction
|
||||||
|
- Purchase page query parameter forwarding
|
||||||
|
|
||||||
### Base URL
|
### Base URL
|
||||||
|
|
||||||
- Production: `https://<your-domain>`
|
- Production: `https://<your-domain>`
|
||||||
- Beta: `http://<your-server-ip>:8084`
|
- Beta: `http://<your-server-ip>:8084`
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
Recommended headers:
|
Recommended headers:
|
||||||
|
- `x-api-key: admin-<64hex>`
|
||||||
- `x-api-key: admin-<64hex>` (recommended for server-to-server calls)
|
|
||||||
- `Content-Type: application/json`
|
- `Content-Type: application/json`
|
||||||
|
- `Idempotency-Key` for idempotent endpoints
|
||||||
|
|
||||||
Note: Admin JWT is also accepted by admin routes, but Admin API key is recommended for machine integrations.
|
Note: Admin JWT can also access admin routes, but Admin API Key is recommended for server-to-server integration.
|
||||||
|
|
||||||
### 1) One-step Create + Redeem
|
|
||||||
|
|
||||||
|
### 1) Create and Redeem in one step
|
||||||
`POST /api/v1/admin/redeem-codes/create-and-redeem`
|
`POST /api/v1/admin/redeem-codes/create-and-redeem`
|
||||||
|
|
||||||
Purpose:
|
Use case: atomically create a redeem code and redeem it to a target user.
|
||||||
|
|
||||||
- Atomically create a deterministic redeem code and redeem it to the target user.
|
|
||||||
- Typical usage: called right after payment callback success.
|
|
||||||
|
|
||||||
Required headers:
|
|
||||||
|
|
||||||
|
Headers:
|
||||||
- `x-api-key`
|
- `x-api-key`
|
||||||
- `Idempotency-Key`
|
- `Idempotency-Key`
|
||||||
|
|
||||||
Request body:
|
Request body:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"code": "s2p_cm1234567890",
|
"code": "s2p_cm1234567890",
|
||||||
@@ -202,21 +164,12 @@ Request body:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
- `code`: deterministic code mapped from external order id.
|
|
||||||
- `type`: `balance` is the recommended type.
|
|
||||||
- `value`: must be greater than 0.
|
|
||||||
- `user_id`: target user id.
|
|
||||||
|
|
||||||
Idempotency behavior:
|
Idempotency behavior:
|
||||||
|
- Same `code` and same `used_by`: `200`
|
||||||
|
- Same `code` but different `used_by`: `409`
|
||||||
|
- Missing `Idempotency-Key`: `400` (`IDEMPOTENCY_KEY_REQUIRED`)
|
||||||
|
|
||||||
- Same `code` and same `used_by`: `200` (idempotent replay).
|
curl example:
|
||||||
- Same `code` and different `used_by`: `409` (conflict).
|
|
||||||
- Missing `Idempotency-Key`: `400` (`IDEMPOTENCY_KEY_REQUIRED`).
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST "${BASE}/api/v1/admin/redeem-codes/create-and-redeem" \
|
curl -X POST "${BASE}/api/v1/admin/redeem-codes/create-and-redeem" \
|
||||||
-H "x-api-key: ${KEY}" \
|
-H "x-api-key: ${KEY}" \
|
||||||
@@ -231,32 +184,20 @@ curl -X POST "${BASE}/api/v1/admin/redeem-codes/create-and-redeem" \
|
|||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2) Query User (Optional Pre-check)
|
### 2) Query User (optional pre-check)
|
||||||
|
|
||||||
`GET /api/v1/admin/users/:id`
|
`GET /api/v1/admin/users/:id`
|
||||||
|
|
||||||
Purpose:
|
|
||||||
|
|
||||||
- Verify target user existence before final recharge/retry.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -s "${BASE}/api/v1/admin/users/123" \
|
curl -s "${BASE}/api/v1/admin/users/123" \
|
||||||
-H "x-api-key: ${KEY}"
|
-H "x-api-key: ${KEY}"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3) Balance Adjustment (Existing API)
|
### 3) Balance Adjustment (existing API)
|
||||||
|
|
||||||
`POST /api/v1/admin/users/:id/balance`
|
`POST /api/v1/admin/users/:id/balance`
|
||||||
|
|
||||||
Purpose:
|
Use case: manual correction with `set` / `add` / `subtract`.
|
||||||
|
|
||||||
- Reuse existing admin endpoint for manual reconciliation.
|
|
||||||
- Supports `set`, `add`, `subtract`.
|
|
||||||
|
|
||||||
Request body example (`subtract`):
|
Request body example (`subtract`):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"balance": 100.0,
|
"balance": 100.0,
|
||||||
@@ -265,8 +206,6 @@ Request body example (`subtract`):
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST "${BASE}/api/v1/admin/users/123/balance" \
|
curl -X POST "${BASE}/api/v1/admin/users/123/balance" \
|
||||||
-H "x-api-key: ${KEY}" \
|
-H "x-api-key: ${KEY}" \
|
||||||
@@ -279,35 +218,24 @@ curl -X POST "${BASE}/api/v1/admin/users/123/balance" \
|
|||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4) Purchase URL Query Parameters (Iframe + New Tab)
|
### 4) Purchase URL query forwarding (iframe and new tab)
|
||||||
|
When Sub2API opens `purchase_subscription_url`, it appends:
|
||||||
When Sub2API frontend opens `purchase_subscription_url`, it appends the same query parameters for both iframe and “open in new tab” to keep context consistent.
|
- `user_id`
|
||||||
|
- `token`
|
||||||
Appended parameters:
|
- `theme` (`light` / `dark`)
|
||||||
|
- `ui_mode` (fixed: `embedded`)
|
||||||
- `user_id`: current logged-in user id
|
|
||||||
- `token`: current logged-in JWT token
|
|
||||||
- `theme`: current theme (`light` / `dark`)
|
|
||||||
- `ui_mode`: UI mode (fixed `embedded`)
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
https://pay.example.com/pay?user_id=123&token=<jwt>&theme=light&ui_mode=embedded
|
https://pay.example.com/pay?user_id=123&token=<jwt>&theme=light&ui_mode=embedded
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5) Failure Handling Recommendations
|
### 5) Failure handling recommendations
|
||||||
|
- Persist payment success and recharge success as separate states
|
||||||
- Store payment state and recharge state separately.
|
- Mark payment as successful immediately after verified callback
|
||||||
- Mark payment success immediately after callback verification.
|
- Allow retry for orders with payment success but recharge failure
|
||||||
- Keep orders retryable when payment succeeded but recharge failed.
|
- Keep the same `code` for retry, and use a new `Idempotency-Key`
|
||||||
- Reuse the same deterministic `code` and a new `Idempotency-Key` when retrying.
|
|
||||||
|
|
||||||
### 6) Suggested `doc_url` Value
|
|
||||||
|
|
||||||
Sub2API already supports `doc_url` in system settings.
|
|
||||||
|
|
||||||
Recommended values:
|
|
||||||
|
|
||||||
|
### 6) Recommended `doc_url`
|
||||||
- View URL: `https://github.com/Wei-Shaw/sub2api/blob/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
- View URL: `https://github.com/Wei-Shaw/sub2api/blob/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
||||||
- Download URL: `https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
- Download URL: `https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/ADMIN_PAYMENT_INTEGRATION_API.md`
|
||||||
|
|||||||
@@ -290,6 +290,26 @@ const CreditCardIcon = {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RechargeSubscriptionIcon = {
|
||||||
|
render: () =>
|
||||||
|
h(
|
||||||
|
'svg',
|
||||||
|
{ fill: 'none', viewBox: '0 0 24 24', stroke: 'currentColor', 'stroke-width': '1.5' },
|
||||||
|
[
|
||||||
|
h('path', {
|
||||||
|
'stroke-linecap': 'round',
|
||||||
|
'stroke-linejoin': 'round',
|
||||||
|
d: 'M2.25 7.5A2.25 2.25 0 014.5 5.25h15A2.25 2.25 0 0121.75 7.5v9A2.25 2.25 0 0119.5 18.75h-15A2.25 2.25 0 012.25 16.5v-9z'
|
||||||
|
}),
|
||||||
|
h('path', {
|
||||||
|
'stroke-linecap': 'round',
|
||||||
|
'stroke-linejoin': 'round',
|
||||||
|
d: 'M6.75 12h3m4.5 0h3m-3-3v6'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const GlobeIcon = {
|
const GlobeIcon = {
|
||||||
render: () =>
|
render: () =>
|
||||||
h(
|
h(
|
||||||
@@ -442,7 +462,7 @@ const userNavItems = computed(() => {
|
|||||||
{
|
{
|
||||||
path: '/purchase',
|
path: '/purchase',
|
||||||
label: t('nav.buySubscription'),
|
label: t('nav.buySubscription'),
|
||||||
icon: CreditCardIcon,
|
icon: RechargeSubscriptionIcon,
|
||||||
hideInSimpleMode: true
|
hideInSimpleMode: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -464,7 +484,7 @@ const personalNavItems = computed(() => {
|
|||||||
{
|
{
|
||||||
path: '/purchase',
|
path: '/purchase',
|
||||||
label: t('nav.buySubscription'),
|
label: t('nav.buySubscription'),
|
||||||
icon: CreditCardIcon,
|
icon: RechargeSubscriptionIcon,
|
||||||
hideInSimpleMode: true
|
hideInSimpleMode: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ export default {
|
|||||||
logout: 'Logout',
|
logout: 'Logout',
|
||||||
github: 'GitHub',
|
github: 'GitHub',
|
||||||
mySubscriptions: 'My Subscriptions',
|
mySubscriptions: 'My Subscriptions',
|
||||||
buySubscription: 'Purchase Subscription',
|
buySubscription: 'Recharge / Subscription',
|
||||||
docs: 'Docs'
|
docs: 'Docs'
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -3343,11 +3343,11 @@ export default {
|
|||||||
hideCcsImportButtonHint: 'When enabled, the "Import to CCS" button will be hidden on the API Keys page'
|
hideCcsImportButtonHint: 'When enabled, the "Import to CCS" button will be hidden on the API Keys page'
|
||||||
},
|
},
|
||||||
purchase: {
|
purchase: {
|
||||||
title: 'Purchase Page',
|
title: 'Recharge / Subscription Page',
|
||||||
description: 'Show a "Purchase Subscription" entry in the sidebar and open the configured URL in an iframe',
|
description: 'Show a "Recharge / Subscription" entry in the sidebar and open the configured URL in an iframe',
|
||||||
enabled: 'Show Purchase Entry',
|
enabled: 'Show Recharge / Subscription Entry',
|
||||||
enabledHint: 'Only shown in standard mode (not simple mode)',
|
enabledHint: 'Only shown in standard mode (not simple mode)',
|
||||||
url: 'Purchase URL',
|
url: 'Recharge / Subscription URL',
|
||||||
urlPlaceholder: 'https://example.com/purchase',
|
urlPlaceholder: 'https://example.com/purchase',
|
||||||
urlHint: 'Must be an absolute http(s) URL',
|
urlHint: 'Must be an absolute http(s) URL',
|
||||||
iframeWarning:
|
iframeWarning:
|
||||||
@@ -3575,16 +3575,16 @@ export default {
|
|||||||
retry: 'Retry'
|
retry: 'Retry'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Purchase Subscription Page
|
// Recharge / Subscription Page
|
||||||
purchase: {
|
purchase: {
|
||||||
title: 'Purchase Subscription',
|
title: 'Recharge / Subscription',
|
||||||
description: 'Purchase a subscription via the embedded page',
|
description: 'Recharge balance or purchase subscription via the embedded page',
|
||||||
openInNewTab: 'Open in new tab',
|
openInNewTab: 'Open in new tab',
|
||||||
notEnabledTitle: 'Feature not enabled',
|
notEnabledTitle: 'Feature not enabled',
|
||||||
notEnabledDesc: 'The administrator has not enabled the purchase page. Please contact admin.',
|
notEnabledDesc: 'The administrator has not enabled the recharge/subscription entry. Please contact admin.',
|
||||||
notConfiguredTitle: 'Purchase URL not configured',
|
notConfiguredTitle: 'Recharge / Subscription URL not configured',
|
||||||
notConfiguredDesc:
|
notConfiguredDesc:
|
||||||
'The administrator enabled the entry but has not configured a purchase URL. Please contact admin.'
|
'The administrator enabled the entry but has not configured a recharge/subscription URL. Please contact admin.'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Announcements Page
|
// Announcements Page
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export default {
|
|||||||
redeemCodes: '兑换码',
|
redeemCodes: '兑换码',
|
||||||
ops: '运维监控',
|
ops: '运维监控',
|
||||||
promoCodes: '优惠码',
|
promoCodes: '优惠码',
|
||||||
|
dataManagement: '数据管理',
|
||||||
settings: '系统设置',
|
settings: '系统设置',
|
||||||
myAccount: '我的账户',
|
myAccount: '我的账户',
|
||||||
lightMode: '浅色模式',
|
lightMode: '浅色模式',
|
||||||
@@ -279,8 +280,9 @@ export default {
|
|||||||
logout: '退出登录',
|
logout: '退出登录',
|
||||||
github: 'GitHub',
|
github: 'GitHub',
|
||||||
mySubscriptions: '我的订阅',
|
mySubscriptions: '我的订阅',
|
||||||
buySubscription: '购买订阅',
|
buySubscription: '充值/订阅',
|
||||||
docs: '文档'
|
docs: '文档',
|
||||||
|
sora: 'Sora 创作'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Auth
|
// Auth
|
||||||
@@ -501,6 +503,7 @@ export default {
|
|||||||
claudeCode: 'Claude Code',
|
claudeCode: 'Claude Code',
|
||||||
geminiCli: 'Gemini CLI',
|
geminiCli: 'Gemini CLI',
|
||||||
codexCli: 'Codex CLI',
|
codexCli: 'Codex CLI',
|
||||||
|
codexCliWs: 'Codex CLI (WebSocket)',
|
||||||
opencode: 'OpenCode'
|
opencode: 'OpenCode'
|
||||||
},
|
},
|
||||||
antigravity: {
|
antigravity: {
|
||||||
@@ -618,8 +621,10 @@ export default {
|
|||||||
firstToken: '首 Token',
|
firstToken: '首 Token',
|
||||||
duration: '耗时',
|
duration: '耗时',
|
||||||
time: '时间',
|
time: '时间',
|
||||||
|
ws: 'WS',
|
||||||
stream: '流式',
|
stream: '流式',
|
||||||
sync: '同步',
|
sync: '同步',
|
||||||
|
unknown: '未知',
|
||||||
in: '输入',
|
in: '输入',
|
||||||
out: '输出',
|
out: '输出',
|
||||||
cacheRead: '读取',
|
cacheRead: '读取',
|
||||||
@@ -862,6 +867,181 @@ export default {
|
|||||||
failedToLoad: '加载仪表盘数据失败'
|
failedToLoad: '加载仪表盘数据失败'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dataManagement: {
|
||||||
|
title: '数据管理',
|
||||||
|
description: '统一管理数据管理代理状态、对象存储配置和备份任务',
|
||||||
|
agent: {
|
||||||
|
title: '数据管理代理状态',
|
||||||
|
description: '系统会自动探测固定 Unix Socket,仅在可连通时启用数据管理功能。',
|
||||||
|
enabled: '数据管理代理已就绪,可继续进行数据管理操作。',
|
||||||
|
disabled: '数据管理代理不可用,当前仅可查看诊断信息。',
|
||||||
|
socketPath: 'Socket 路径',
|
||||||
|
version: '版本',
|
||||||
|
status: '状态',
|
||||||
|
uptime: '运行时长',
|
||||||
|
reasonLabel: '不可用原因',
|
||||||
|
reason: {
|
||||||
|
DATA_MANAGEMENT_AGENT_SOCKET_MISSING: '未检测到数据管理 Socket 文件',
|
||||||
|
DATA_MANAGEMENT_AGENT_UNAVAILABLE: '数据管理代理不可连通',
|
||||||
|
BACKUP_AGENT_SOCKET_MISSING: '未检测到备份 Socket 文件',
|
||||||
|
BACKUP_AGENT_UNAVAILABLE: '备份代理不可连通',
|
||||||
|
UNKNOWN: '未知原因'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sections: {
|
||||||
|
config: {
|
||||||
|
title: '备份配置',
|
||||||
|
description: '配置备份源、保留策略与 S3 存储参数。'
|
||||||
|
},
|
||||||
|
s3: {
|
||||||
|
title: 'S3 对象存储',
|
||||||
|
description: '配置并测试备份产物上传到标准 S3 对象存储。'
|
||||||
|
},
|
||||||
|
backup: {
|
||||||
|
title: '备份操作',
|
||||||
|
description: '触发 PostgreSQL、Redis 与全量备份任务。'
|
||||||
|
},
|
||||||
|
history: {
|
||||||
|
title: '备份历史',
|
||||||
|
description: '查看备份任务执行状态、错误与产物信息。'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
sourceMode: '源模式',
|
||||||
|
backupRoot: '备份根目录',
|
||||||
|
activePostgresProfile: '当前激活 PostgreSQL 配置',
|
||||||
|
activeRedisProfile: '当前激活 Redis 配置',
|
||||||
|
activeS3Profile: '当前激活 S3 账号',
|
||||||
|
retentionDays: '保留天数',
|
||||||
|
keepLast: '至少保留最近任务数',
|
||||||
|
uploadToS3: '上传到 S3',
|
||||||
|
useActivePostgresProfile: '使用当前激活 PostgreSQL 配置',
|
||||||
|
useActiveRedisProfile: '使用当前激活 Redis 配置',
|
||||||
|
useActiveS3Profile: '使用当前激活账号',
|
||||||
|
idempotencyKey: '幂等键(可选)',
|
||||||
|
secretConfigured: '已配置,留空不变',
|
||||||
|
source: {
|
||||||
|
profileID: '配置 ID(唯一)',
|
||||||
|
profileName: '配置名称',
|
||||||
|
setActive: '创建后立即设为激活配置'
|
||||||
|
},
|
||||||
|
postgres: {
|
||||||
|
title: 'PostgreSQL',
|
||||||
|
host: '主机',
|
||||||
|
port: '端口',
|
||||||
|
user: '用户名',
|
||||||
|
password: '密码',
|
||||||
|
database: '数据库',
|
||||||
|
sslMode: 'SSL 模式',
|
||||||
|
containerName: '容器名(docker_exec 模式)'
|
||||||
|
},
|
||||||
|
redis: {
|
||||||
|
title: 'Redis',
|
||||||
|
addr: '地址(host:port)',
|
||||||
|
username: '用户名',
|
||||||
|
password: '密码',
|
||||||
|
db: '数据库编号',
|
||||||
|
containerName: '容器名(docker_exec 模式)'
|
||||||
|
},
|
||||||
|
s3: {
|
||||||
|
enabled: '启用 S3 上传',
|
||||||
|
profileID: '账号 ID(唯一)',
|
||||||
|
profileName: '账号名称',
|
||||||
|
endpoint: 'Endpoint(可选)',
|
||||||
|
region: 'Region',
|
||||||
|
bucket: 'Bucket',
|
||||||
|
accessKeyID: 'Access Key ID',
|
||||||
|
secretAccessKey: 'Secret Access Key',
|
||||||
|
prefix: '对象前缀',
|
||||||
|
forcePathStyle: '强制 path-style',
|
||||||
|
useSSL: '使用 SSL',
|
||||||
|
setActive: '创建后立即设为激活账号'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sourceProfiles: {
|
||||||
|
createTitle: '创建数据源配置',
|
||||||
|
editTitle: '编辑数据源配置',
|
||||||
|
empty: '暂无配置,请先创建',
|
||||||
|
deleteConfirm: '确定删除配置 {profileID} 吗?',
|
||||||
|
columns: {
|
||||||
|
profile: '配置',
|
||||||
|
active: '激活状态',
|
||||||
|
connection: '连接信息',
|
||||||
|
database: '数据库',
|
||||||
|
updatedAt: '更新时间',
|
||||||
|
actions: '操作'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
s3Profiles: {
|
||||||
|
createTitle: '创建 S3 账号',
|
||||||
|
editTitle: '编辑 S3 账号',
|
||||||
|
empty: '暂无 S3 账号,请先创建',
|
||||||
|
editHint: '点击“编辑”将在右侧抽屉中修改账号信息。',
|
||||||
|
deleteConfirm: '确定删除 S3 账号 {profileID} 吗?',
|
||||||
|
columns: {
|
||||||
|
profile: '账号',
|
||||||
|
active: '激活状态',
|
||||||
|
storage: '存储配置',
|
||||||
|
updatedAt: '更新时间',
|
||||||
|
actions: '操作'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
history: {
|
||||||
|
total: '共 {count} 条',
|
||||||
|
empty: '暂无备份任务',
|
||||||
|
columns: {
|
||||||
|
jobID: '任务 ID',
|
||||||
|
type: '类型',
|
||||||
|
status: '状态',
|
||||||
|
triggeredBy: '触发人',
|
||||||
|
pgProfile: 'PostgreSQL 配置',
|
||||||
|
redisProfile: 'Redis 配置',
|
||||||
|
s3Profile: 'S3 账号',
|
||||||
|
finishedAt: '完成时间',
|
||||||
|
artifact: '产物',
|
||||||
|
error: '错误'
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
queued: '排队中',
|
||||||
|
running: '执行中',
|
||||||
|
succeeded: '成功',
|
||||||
|
failed: '失败',
|
||||||
|
partial_succeeded: '部分成功'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
refresh: '刷新状态',
|
||||||
|
disabledHint: '请先启动 datamanagementd 并确认 Socket 可连通。',
|
||||||
|
reloadConfig: '加载配置',
|
||||||
|
reloadSourceProfiles: '刷新数据源配置',
|
||||||
|
reloadProfiles: '刷新账号列表',
|
||||||
|
newSourceProfile: '新建数据源配置',
|
||||||
|
saveConfig: '保存配置',
|
||||||
|
configSaved: '配置保存成功',
|
||||||
|
testS3: '测试 S3 连接',
|
||||||
|
s3TestOK: 'S3 连接测试成功',
|
||||||
|
s3TestFailed: 'S3 连接测试失败',
|
||||||
|
newProfile: '新建账号',
|
||||||
|
saveProfile: '保存账号',
|
||||||
|
activateProfile: '设为激活',
|
||||||
|
profileIDRequired: '请输入账号 ID',
|
||||||
|
profileNameRequired: '请输入账号名称',
|
||||||
|
profileSelectRequired: '请先选择要编辑的账号',
|
||||||
|
profileCreated: 'S3 账号创建成功',
|
||||||
|
profileSaved: 'S3 账号保存成功',
|
||||||
|
profileActivated: 'S3 账号已切换为激活',
|
||||||
|
profileDeleted: 'S3 账号删除成功',
|
||||||
|
sourceProfileCreated: '数据源配置创建成功',
|
||||||
|
sourceProfileSaved: '数据源配置保存成功',
|
||||||
|
sourceProfileActivated: '数据源配置已切换为激活',
|
||||||
|
sourceProfileDeleted: '数据源配置删除成功',
|
||||||
|
createBackup: '创建备份任务',
|
||||||
|
jobCreated: '备份任务已创建:{jobID}({status})',
|
||||||
|
refreshJobs: '刷新任务',
|
||||||
|
loadMore: '加载更多'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Users Management
|
// Users Management
|
||||||
users: {
|
users: {
|
||||||
title: '用户管理',
|
title: '用户管理',
|
||||||
@@ -925,6 +1105,9 @@ export default {
|
|||||||
noApiKeys: '此用户暂无 API 密钥',
|
noApiKeys: '此用户暂无 API 密钥',
|
||||||
group: '分组',
|
group: '分组',
|
||||||
none: '无',
|
none: '无',
|
||||||
|
groupChangedSuccess: '分组修改成功',
|
||||||
|
groupChangedWithGrant: '分组修改成功,已自动为用户添加「{group}」分组权限',
|
||||||
|
groupChangeFailed: '分组修改失败',
|
||||||
noUsersYet: '暂无用户',
|
noUsersYet: '暂无用户',
|
||||||
createFirstUser: '创建您的第一个用户以开始使用系统',
|
createFirstUser: '创建您的第一个用户以开始使用系统',
|
||||||
userCreated: '用户创建成功',
|
userCreated: '用户创建成功',
|
||||||
@@ -978,6 +1161,8 @@ export default {
|
|||||||
failedToAdjust: '调整失败',
|
failedToAdjust: '调整失败',
|
||||||
emailRequired: '请输入邮箱',
|
emailRequired: '请输入邮箱',
|
||||||
concurrencyMin: '并发数不能小于1',
|
concurrencyMin: '并发数不能小于1',
|
||||||
|
soraStorageQuota: 'Sora 存储配额',
|
||||||
|
soraStorageQuotaHint: '单位 GB,0 表示使用分组或系统默认配额',
|
||||||
amountRequired: '请输入有效金额',
|
amountRequired: '请输入有效金额',
|
||||||
insufficientBalance: '余额不足',
|
insufficientBalance: '余额不足',
|
||||||
setAllowedGroups: '设置允许分组',
|
setAllowedGroups: '设置允许分组',
|
||||||
@@ -1228,7 +1413,9 @@ export default {
|
|||||||
image360: '图片 360px ($)',
|
image360: '图片 360px ($)',
|
||||||
image540: '图片 540px ($)',
|
image540: '图片 540px ($)',
|
||||||
video: '视频(标准)($)',
|
video: '视频(标准)($)',
|
||||||
videoHd: '视频(Pro-HD)($)'
|
videoHd: '视频(Pro-HD)($)',
|
||||||
|
storageQuota: '存储配额',
|
||||||
|
storageQuotaHint: '单位 GB,设置该分组用户的 Sora 存储配额上限,0 表示使用系统默认'
|
||||||
},
|
},
|
||||||
claudeCode: {
|
claudeCode: {
|
||||||
title: 'Claude Code 客户端限制',
|
title: 'Claude Code 客户端限制',
|
||||||
@@ -1280,14 +1467,6 @@ export default {
|
|||||||
enabled: '已启用',
|
enabled: '已启用',
|
||||||
disabled: '已禁用'
|
disabled: '已禁用'
|
||||||
},
|
},
|
||||||
claudeMaxSimulation: {
|
|
||||||
title: 'Claude Max 用量模拟',
|
|
||||||
tooltip:
|
|
||||||
'启用后,针对 Claude 模型且上游未返回写缓存时,系统会按确定性算法把输入 token 映射为少量 input,并将其余归入 1h cache creation,保持总 token 不变。',
|
|
||||||
enabled: '已启用(模拟 1h 缓存)',
|
|
||||||
disabled: '已禁用',
|
|
||||||
hint: '仅影响 usage 计费记录中的 token 分类,不保存请求级映射状态。'
|
|
||||||
},
|
|
||||||
supportedScopes: {
|
supportedScopes: {
|
||||||
title: '支持的模型系列',
|
title: '支持的模型系列',
|
||||||
tooltip: '选择此分组支持的模型系列。未勾选的系列将不会被路由到此分组。',
|
tooltip: '选择此分组支持的模型系列。未勾选的系列将不会被路由到此分组。',
|
||||||
@@ -1489,7 +1668,19 @@ export default {
|
|||||||
sessions: {
|
sessions: {
|
||||||
full: '活跃会话已满,新会话需等待(空闲超时:{idle}分钟)',
|
full: '活跃会话已满,新会话需等待(空闲超时:{idle}分钟)',
|
||||||
normal: '活跃会话正常(空闲超时:{idle}分钟)'
|
normal: '活跃会话正常(空闲超时:{idle}分钟)'
|
||||||
}
|
},
|
||||||
|
rpm: {
|
||||||
|
full: '已达 RPM 上限',
|
||||||
|
warning: 'RPM 接近上限',
|
||||||
|
normal: 'RPM 正常',
|
||||||
|
tieredNormal: 'RPM 限制 (三区模型) - 正常',
|
||||||
|
tieredWarning: 'RPM 限制 (三区模型) - 接近阈值',
|
||||||
|
tieredStickyOnly: 'RPM 限制 (三区模型) - 仅粘性会话 | 缓冲区: {buffer}',
|
||||||
|
tieredBlocked: 'RPM 限制 (三区模型) - 已阻塞 | 缓冲区: {buffer}',
|
||||||
|
stickyExemptNormal: 'RPM 限制 (粘性豁免) - 正常',
|
||||||
|
stickyExemptWarning: 'RPM 限制 (粘性豁免) - 接近阈值',
|
||||||
|
stickyExemptOver: 'RPM 限制 (粘性豁免) - 超限,仅粘性会话'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
clearRateLimit: '清除速率限制',
|
clearRateLimit: '清除速率限制',
|
||||||
testConnection: '测试连接',
|
testConnection: '测试连接',
|
||||||
@@ -1520,6 +1711,10 @@ export default {
|
|||||||
codeAssist: 'Code Assist',
|
codeAssist: 'Code Assist',
|
||||||
antigravityOauth: 'Antigravity OAuth',
|
antigravityOauth: 'Antigravity OAuth',
|
||||||
antigravityApikey: '通过 Base URL + API Key 连接',
|
antigravityApikey: '通过 Base URL + API Key 连接',
|
||||||
|
soraApiKey: 'API Key / 上游透传',
|
||||||
|
soraApiKeyHint: '连接另一个 Sub2API 或兼容 API',
|
||||||
|
soraBaseUrlRequired: 'Sora apikey 账号必须设置上游地址(Base URL)',
|
||||||
|
soraBaseUrlInvalidScheme: 'Base URL 必须以 http:// 或 https:// 开头',
|
||||||
upstream: '对接上游',
|
upstream: '对接上游',
|
||||||
upstreamDesc: '通过 Base URL + API Key 连接上游',
|
upstreamDesc: '通过 Base URL + API Key 连接上游',
|
||||||
api_key: 'API Key',
|
api_key: 'API Key',
|
||||||
@@ -1700,6 +1895,22 @@ export default {
|
|||||||
oauthPassthrough: '自动透传(仅替换认证)',
|
oauthPassthrough: '自动透传(仅替换认证)',
|
||||||
oauthPassthroughDesc:
|
oauthPassthroughDesc:
|
||||||
'开启后,该 OpenAI 账号将自动透传请求与响应,仅替换认证并保留计费/并发/审计及必要安全过滤;如遇兼容性问题可随时关闭回滚。',
|
'开启后,该 OpenAI 账号将自动透传请求与响应,仅替换认证并保留计费/并发/审计及必要安全过滤;如遇兼容性问题可随时关闭回滚。',
|
||||||
|
responsesWebsocketsV2: 'Responses WebSocket v2',
|
||||||
|
responsesWebsocketsV2Desc:
|
||||||
|
'默认关闭。开启后可启用 responses_websockets_v2 协议能力(受网关全局开关与账号类型开关约束)。',
|
||||||
|
wsMode: 'WS mode',
|
||||||
|
wsModeDesc: '仅对当前 OpenAI 账号类型生效。',
|
||||||
|
wsModeOff: '关闭(off)',
|
||||||
|
wsModeShared: '共享(shared)',
|
||||||
|
wsModeDedicated: '独享(dedicated)',
|
||||||
|
wsModeConcurrencyHint: '启用 WS mode 后,该账号并发数将作为该账号 WS 连接池上限。',
|
||||||
|
oauthResponsesWebsocketsV2: 'OAuth WebSocket Mode',
|
||||||
|
oauthResponsesWebsocketsV2Desc:
|
||||||
|
'仅对 OpenAI OAuth 生效。开启后该账号才允许使用 OpenAI WebSocket Mode 协议。',
|
||||||
|
apiKeyResponsesWebsocketsV2: 'API Key WebSocket Mode',
|
||||||
|
apiKeyResponsesWebsocketsV2Desc:
|
||||||
|
'仅对 OpenAI API Key 生效。开启后该账号才允许使用 OpenAI WebSocket Mode 协议。',
|
||||||
|
responsesWebsocketsV2PassthroughHint: '当前已开启自动透传:仅影响 HTTP 透传链路,不影响 WS mode。',
|
||||||
codexCLIOnly: '仅允许 Codex 官方客户端',
|
codexCLIOnly: '仅允许 Codex 官方客户端',
|
||||||
codexCLIOnlyDesc: '仅对 OpenAI OAuth 生效。开启后仅允许 Codex 官方客户端家族访问;关闭后完全绕过并保持原逻辑。',
|
codexCLIOnlyDesc: '仅对 OpenAI OAuth 生效。开启后仅允许 Codex 官方客户端家族访问;关闭后完全绕过并保持原逻辑。',
|
||||||
modelRestrictionDisabledByPassthrough: '已开启自动透传:模型白名单/映射不会生效。',
|
modelRestrictionDisabledByPassthrough: '已开启自动透传:模型白名单/映射不会生效。',
|
||||||
@@ -1776,6 +1987,22 @@ export default {
|
|||||||
idleTimeoutPlaceholder: '5',
|
idleTimeoutPlaceholder: '5',
|
||||||
idleTimeoutHint: '会话空闲超时后自动释放'
|
idleTimeoutHint: '会话空闲超时后自动释放'
|
||||||
},
|
},
|
||||||
|
rpmLimit: {
|
||||||
|
label: 'RPM 限制',
|
||||||
|
hint: '限制每分钟请求数量,保护上游账号',
|
||||||
|
baseRpm: '基础 RPM',
|
||||||
|
baseRpmPlaceholder: '15',
|
||||||
|
baseRpmHint: '每分钟最大请求数,0 或留空表示不限制',
|
||||||
|
strategy: 'RPM 策略',
|
||||||
|
strategyTiered: '三区模型',
|
||||||
|
strategyStickyExempt: '粘性豁免',
|
||||||
|
strategyTieredHint: '绿区→黄区→仅粘性→阻塞,逐步限流',
|
||||||
|
strategyStickyExemptHint: '超限后仅允许粘性会话',
|
||||||
|
strategyHint: '三区模型: 超限后逐步限制; 粘性豁免: 已有会话不受限',
|
||||||
|
stickyBuffer: '粘性缓冲区',
|
||||||
|
stickyBufferPlaceholder: '默认: base RPM 的 20%',
|
||||||
|
stickyBufferHint: '超过 base RPM 后,粘性会话额外允许的请求数。为空则使用默认值(base RPM 的 20%,最小为 1)'
|
||||||
|
},
|
||||||
tlsFingerprint: {
|
tlsFingerprint: {
|
||||||
label: 'TLS 指纹模拟',
|
label: 'TLS 指纹模拟',
|
||||||
hint: '模拟 Node.js/Claude Code 客户端的 TLS 指纹'
|
hint: '模拟 Node.js/Claude Code 客户端的 TLS 指纹'
|
||||||
@@ -1899,6 +2126,15 @@ export default {
|
|||||||
sessionTokenAuth: '手动输入 ST',
|
sessionTokenAuth: '手动输入 ST',
|
||||||
sessionTokenDesc: '输入您已有的 Sora Session Token,支持批量输入(每行一个),系统将自动验证并创建账号。',
|
sessionTokenDesc: '输入您已有的 Sora Session Token,支持批量输入(每行一个),系统将自动验证并创建账号。',
|
||||||
sessionTokenPlaceholder: '粘贴您的 Sora Session Token...\n支持多个,每行一个',
|
sessionTokenPlaceholder: '粘贴您的 Sora Session Token...\n支持多个,每行一个',
|
||||||
|
sessionTokenRawLabel: '原始字符串',
|
||||||
|
sessionTokenRawPlaceholder: '粘贴 /api/auth/session 原始数据或 Session Token...',
|
||||||
|
sessionTokenRawHint: '支持粘贴完整 JSON,系统会自动解析 ST 和 AT。',
|
||||||
|
openSessionUrl: '打开获取链接',
|
||||||
|
copySessionUrl: '复制链接',
|
||||||
|
sessionUrlHint: '该链接通常可获取 AT。若返回中无 sessionToken,请从浏览器 Cookie 复制 __Secure-next-auth.session-token 作为 ST。',
|
||||||
|
parsedSessionTokensLabel: '解析出的 ST',
|
||||||
|
parsedSessionTokensEmpty: '未解析到 ST,请检查输入内容',
|
||||||
|
parsedAccessTokensLabel: '解析出的 AT',
|
||||||
validating: '验证中...',
|
validating: '验证中...',
|
||||||
validateAndCreate: '验证并创建账号',
|
validateAndCreate: '验证并创建账号',
|
||||||
pleaseEnterRefreshToken: '请输入 Refresh Token',
|
pleaseEnterRefreshToken: '请输入 Refresh Token',
|
||||||
@@ -2142,6 +2378,7 @@ export default {
|
|||||||
selectTestModel: '选择测试模型',
|
selectTestModel: '选择测试模型',
|
||||||
testModel: '测试模型',
|
testModel: '测试模型',
|
||||||
testPrompt: '提示词:"hi"',
|
testPrompt: '提示词:"hi"',
|
||||||
|
soraUpstreamBaseUrlHint: '上游 Sora 服务地址(另一个 Sub2API 实例或兼容 API)',
|
||||||
soraTestHint: 'Sora 测试将执行连通性与能力检测(/backend/me、订阅信息、Sora2 邀请码与剩余额度)。',
|
soraTestHint: 'Sora 测试将执行连通性与能力检测(/backend/me、订阅信息、Sora2 邀请码与剩余额度)。',
|
||||||
soraTestTarget: '检测目标:Sora 账号能力',
|
soraTestTarget: '检测目标:Sora 账号能力',
|
||||||
soraTestMode: '模式:连通性 + 能力探测',
|
soraTestMode: '模式:连通性 + 能力探测',
|
||||||
@@ -3517,15 +3754,21 @@ export default {
|
|||||||
hideCcsImportButtonHint: '启用后将在 API Keys 页面隐藏"导入 CCS"按钮'
|
hideCcsImportButtonHint: '启用后将在 API Keys 页面隐藏"导入 CCS"按钮'
|
||||||
},
|
},
|
||||||
purchase: {
|
purchase: {
|
||||||
title: '购买订阅页面',
|
title: '充值/订阅页面',
|
||||||
description: '在侧边栏展示“购买订阅”入口,并在页面内通过 iframe 打开指定链接',
|
description: '在侧边栏展示“充值/订阅”入口,并在页面内通过 iframe 打开指定链接',
|
||||||
enabled: '显示购买订阅入口',
|
enabled: '显示充值/订阅入口',
|
||||||
enabledHint: '仅在标准模式(非简单模式)下展示',
|
enabledHint: '仅在标准模式(非简单模式)下展示',
|
||||||
url: '购买页面 URL',
|
url: '充值/订阅页面 URL',
|
||||||
urlPlaceholder: 'https://example.com/purchase',
|
urlPlaceholder: 'https://example.com/purchase',
|
||||||
urlHint: '必须是完整的 http(s) 链接',
|
urlHint: '必须是完整的 http(s) 链接',
|
||||||
iframeWarning:
|
iframeWarning:
|
||||||
'⚠️ iframe 提示:部分网站会通过 X-Frame-Options 或 CSP(frame-ancestors)禁止被 iframe 嵌入,出现空白时可引导用户使用“新窗口打开”。'
|
'⚠️ iframe 提示:部分网站会通过 X-Frame-Options 或 CSP(frame-ancestors)禁止被 iframe 嵌入,出现空白时可引导用户使用”新窗口打开”。'
|
||||||
|
},
|
||||||
|
soraClient: {
|
||||||
|
title: 'Sora 客户端',
|
||||||
|
description: '控制是否在侧边栏展示 Sora 客户端入口',
|
||||||
|
enabled: '启用 Sora 客户端',
|
||||||
|
enabledHint: '开启后,侧边栏将显示 Sora 入口,用户可访问 Sora 功能'
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
title: 'SMTP 设置',
|
title: 'SMTP 设置',
|
||||||
@@ -3597,6 +3840,60 @@ export default {
|
|||||||
securityWarning: '警告:此密钥拥有完整的管理员权限,请妥善保管。',
|
securityWarning: '警告:此密钥拥有完整的管理员权限,请妥善保管。',
|
||||||
usage: '使用方法:在请求头中添加 x-api-key: <your-admin-api-key>'
|
usage: '使用方法:在请求头中添加 x-api-key: <your-admin-api-key>'
|
||||||
},
|
},
|
||||||
|
soraS3: {
|
||||||
|
title: 'Sora S3 存储配置',
|
||||||
|
description: '以多配置列表方式管理 Sora S3 端点,并可切换生效配置',
|
||||||
|
newProfile: '新建配置',
|
||||||
|
reloadProfiles: '刷新列表',
|
||||||
|
empty: '暂无 Sora S3 配置,请先创建',
|
||||||
|
createTitle: '新建 Sora S3 配置',
|
||||||
|
editTitle: '编辑 Sora S3 配置',
|
||||||
|
profileID: '配置 ID',
|
||||||
|
profileName: '配置名称',
|
||||||
|
setActive: '创建后设为生效',
|
||||||
|
saveProfile: '保存配置',
|
||||||
|
activateProfile: '设为生效',
|
||||||
|
profileCreated: 'Sora S3 配置创建成功',
|
||||||
|
profileSaved: 'Sora S3 配置保存成功',
|
||||||
|
profileDeleted: 'Sora S3 配置删除成功',
|
||||||
|
profileActivated: 'Sora S3 生效配置已切换',
|
||||||
|
profileIDRequired: '请填写配置 ID',
|
||||||
|
profileNameRequired: '请填写配置名称',
|
||||||
|
profileSelectRequired: '请先选择配置',
|
||||||
|
endpointRequired: '启用时必须填写 S3 端点',
|
||||||
|
bucketRequired: '启用时必须填写存储桶',
|
||||||
|
accessKeyRequired: '启用时必须填写 Access Key ID',
|
||||||
|
deleteConfirm: '确定删除 Sora S3 配置 {profileID} 吗?',
|
||||||
|
columns: {
|
||||||
|
profile: '配置',
|
||||||
|
active: '生效状态',
|
||||||
|
endpoint: '端点',
|
||||||
|
bucket: '存储桶',
|
||||||
|
quota: '默认配额',
|
||||||
|
updatedAt: '更新时间',
|
||||||
|
actions: '操作'
|
||||||
|
},
|
||||||
|
enabled: '启用 S3 存储',
|
||||||
|
enabledHint: '启用后,Sora 生成的媒体文件将自动上传到 S3 存储',
|
||||||
|
endpoint: 'S3 端点',
|
||||||
|
region: '区域',
|
||||||
|
bucket: '存储桶',
|
||||||
|
prefix: '对象前缀',
|
||||||
|
accessKeyId: 'Access Key ID',
|
||||||
|
secretAccessKey: 'Secret Access Key',
|
||||||
|
secretConfigured: '(已配置,留空保持不变)',
|
||||||
|
cdnUrl: 'CDN URL',
|
||||||
|
cdnUrlHint: '可选,配置后使用 CDN URL 访问文件,否则使用预签名 URL',
|
||||||
|
forcePathStyle: '强制路径风格(Path Style)',
|
||||||
|
defaultQuota: '默认存储配额',
|
||||||
|
defaultQuotaHint: '未在用户或分组级别指定配额时的默认值,0 表示无限制',
|
||||||
|
testConnection: '测试连接',
|
||||||
|
testing: '测试中...',
|
||||||
|
testSuccess: 'S3 连接测试成功',
|
||||||
|
testFailed: 'S3 连接测试失败',
|
||||||
|
saved: 'Sora S3 设置保存成功',
|
||||||
|
saveFailed: '保存 Sora S3 设置失败'
|
||||||
|
},
|
||||||
streamTimeout: {
|
streamTimeout: {
|
||||||
title: '流超时处理',
|
title: '流超时处理',
|
||||||
description: '配置上游响应超时时的账户处理策略,避免问题账户持续被选中',
|
description: '配置上游响应超时时的账户处理策略,避免问题账户持续被选中',
|
||||||
@@ -3748,15 +4045,15 @@ export default {
|
|||||||
retry: '重试'
|
retry: '重试'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Purchase Subscription Page
|
// Recharge / Subscription Page
|
||||||
purchase: {
|
purchase: {
|
||||||
title: '购买订阅',
|
title: '充值/订阅',
|
||||||
description: '通过内嵌页面完成订阅购买',
|
description: '通过内嵌页面完成充值/订阅',
|
||||||
openInNewTab: '新窗口打开',
|
openInNewTab: '新窗口打开',
|
||||||
notEnabledTitle: '该功能未开启',
|
notEnabledTitle: '该功能未开启',
|
||||||
notEnabledDesc: '管理员暂未开启购买订阅入口,请联系管理员。',
|
notEnabledDesc: '管理员暂未开启充值/订阅入口,请联系管理员。',
|
||||||
notConfiguredTitle: '购买链接未配置',
|
notConfiguredTitle: '充值/订阅链接未配置',
|
||||||
notConfiguredDesc: '管理员已开启入口,但尚未配置购买订阅链接,请联系管理员。'
|
notConfiguredDesc: '管理员已开启入口,但尚未配置充值/订阅链接,请联系管理员。'
|
||||||
},
|
},
|
||||||
|
|
||||||
// Announcements Page
|
// Announcements Page
|
||||||
@@ -3980,5 +4277,93 @@ export default {
|
|||||||
'<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">点击确认创建您的 API 密钥。</p><div style="padding: 8px 12px; background: #fee2e2; border-left: 3px solid #ef4444; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>⚠️ 重要:</b><ul style="margin: 8px 0 0 16px;"><li>创建后请立即复制密钥(sk-xxx)</li><li>密钥只显示一次,丢失需重新生成</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>🚀 如何使用:</b><br/>将密钥配置到支持 OpenAI 接口的任何客户端(如 ChatBox、OpenCat 等),即可开始使用!</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 点击"创建"按钮</p></div>'
|
'<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">点击确认创建您的 API 密钥。</p><div style="padding: 8px 12px; background: #fee2e2; border-left: 3px solid #ef4444; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>⚠️ 重要:</b><ul style="margin: 8px 0 0 16px;"><li>创建后请立即复制密钥(sk-xxx)</li><li>密钥只显示一次,丢失需重新生成</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>🚀 如何使用:</b><br/>将密钥配置到支持 OpenAI 接口的任何客户端(如 ChatBox、OpenCat 等),即可开始使用!</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 点击"创建"按钮</p></div>'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Sora 创作
|
||||||
|
sora: {
|
||||||
|
title: 'Sora 创作',
|
||||||
|
description: '使用 Sora AI 生成视频与图片',
|
||||||
|
notEnabled: '功能未开放',
|
||||||
|
notEnabledDesc: '管理员尚未启用 Sora 创作功能,请联系管理员开通。',
|
||||||
|
tabGenerate: '生成',
|
||||||
|
tabLibrary: '作品库',
|
||||||
|
noActiveGenerations: '暂无生成任务',
|
||||||
|
startGenerating: '在下方输入提示词,开始创作',
|
||||||
|
storage: '存储',
|
||||||
|
promptPlaceholder: '描述你想创作的内容...',
|
||||||
|
generate: '生成',
|
||||||
|
generating: '生成中...',
|
||||||
|
selectModel: '选择模型',
|
||||||
|
statusPending: '等待中',
|
||||||
|
statusGenerating: '生成中',
|
||||||
|
statusCompleted: '已完成',
|
||||||
|
statusFailed: '失败',
|
||||||
|
statusCancelled: '已取消',
|
||||||
|
cancel: '取消',
|
||||||
|
delete: '删除',
|
||||||
|
save: '保存到云端',
|
||||||
|
saved: '已保存',
|
||||||
|
retry: '重试',
|
||||||
|
download: '下载',
|
||||||
|
justNow: '刚刚',
|
||||||
|
minutesAgo: '{n} 分钟前',
|
||||||
|
hoursAgo: '{n} 小时前',
|
||||||
|
noSavedWorks: '暂无保存的作品',
|
||||||
|
saveWorksHint: '生成完成后,将作品保存到作品库',
|
||||||
|
filterAll: '全部',
|
||||||
|
filterVideo: '视频',
|
||||||
|
filterImage: '图片',
|
||||||
|
confirmDelete: '确定删除此作品?',
|
||||||
|
loading: '加载中...',
|
||||||
|
loadMore: '加载更多',
|
||||||
|
noStorageWarningTitle: '未配置存储',
|
||||||
|
noStorageWarningDesc: '生成的内容仅通过上游临时链接提供,约 15 分钟后过期。建议管理员配置 S3 存储。',
|
||||||
|
mediaTypeVideo: '视频',
|
||||||
|
mediaTypeImage: '图片',
|
||||||
|
notificationCompleted: '生成完成',
|
||||||
|
notificationFailed: '生成失败',
|
||||||
|
notificationCompletedBody: '您的 {model} 任务已完成',
|
||||||
|
notificationFailedBody: '您的 {model} 任务失败了',
|
||||||
|
upstreamExpiresSoon: '即将过期',
|
||||||
|
upstreamExpired: '链接已过期',
|
||||||
|
upstreamCountdown: '剩余 {time}',
|
||||||
|
previewTitle: '作品预览',
|
||||||
|
closePreview: '关闭',
|
||||||
|
beforeUnloadWarning: '您有未保存的生成内容,确定要离开吗?',
|
||||||
|
downloadTitle: '下载生成内容',
|
||||||
|
downloadExpirationWarning: '此链接约 15 分钟后过期,请尽快下载保存。',
|
||||||
|
downloadNow: '立即下载',
|
||||||
|
referenceImage: '参考图',
|
||||||
|
removeImage: '移除',
|
||||||
|
imageTooLarge: '图片大小不能超过 20MB',
|
||||||
|
// Sora 暗色主题新增
|
||||||
|
welcomeTitle: '将你的想象力变成视频',
|
||||||
|
welcomeSubtitle: '输入一段描述,Sora 将为你创作逼真的视频或图片。尝试以下示例开始创作。',
|
||||||
|
queueTasks: '个任务',
|
||||||
|
queueWaiting: '队列中等待',
|
||||||
|
waiting: '等待中',
|
||||||
|
waited: '已等待',
|
||||||
|
errorCategory: '内容策略限制',
|
||||||
|
savedToCloud: '已保存到云端',
|
||||||
|
downloadLocal: '本地下载',
|
||||||
|
canDownload: '可下载',
|
||||||
|
regenrate: '重新生成',
|
||||||
|
creatorPlaceholder: '描述你想要生成的视频或图片...',
|
||||||
|
videoModels: '视频模型',
|
||||||
|
imageModels: '图片模型',
|
||||||
|
noStorageConfigured: '存储未配置',
|
||||||
|
selectCredential: '选择凭证',
|
||||||
|
apiKeys: 'API 密钥',
|
||||||
|
subscriptions: '订阅',
|
||||||
|
subscription: '订阅',
|
||||||
|
noCredentialHint: '请先创建 API Key 或联系管理员分配订阅',
|
||||||
|
uploadReference: '上传参考图片',
|
||||||
|
generatingCount: '正在生成 {current}/{max}',
|
||||||
|
noStorageToastMessage: '管理员未开通云存储,生成完成后请使用"本地下载"保存文件,否则将会丢失。',
|
||||||
|
galleryCount: '共 {count} 个作品',
|
||||||
|
galleryEmptyTitle: '还没有任何作品',
|
||||||
|
galleryEmptyDesc: '你的创作成果将会展示在这里。前往生成页,开始你的第一次创作吧。',
|
||||||
|
startCreating: '开始创作',
|
||||||
|
yesterday: '昨天'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user