# 第三方服务、接口及密钥说明

本文档记录第三方服务实际使用的网址、接口和密钥字段。
完整明文密钥不得提交到版本库；下文的密钥值使用登记占位符，真实值分别保存在生产环境文件或管理后台加密存储中。

## 1. 第三方服务总览

| 第三方服务 | 用途 | 网址或接口入口 | 所需凭证 | 凭证保存位置 |
| --- | --- | --- | --- | --- |
| AP 渠道 | 文本、图片、视频任务及余额查询 | `https://api.apimart.ai/v1` | 渠道 API Key | PostgreSQL `channels.api_key_ciphertext` |
| 腾讯云 COS | 上传、读取和删除图片、音频、视频等媒体文件 | `https://cos.ap-chengdu.myqcloud.com` | SecretId、SecretKey | `/etc/juyou_ai/juyou_ai.env` |
| 腾讯云 COS 公开域名 | 向前端及第三方渠道提供可访问的媒体 URL | `https://cos.youjuhui.xyz` | 无单独运行时密钥 | `/etc/juyou_ai/juyou_ai.env` |
| Let's Encrypt | 申请和续期 HTTPS 证书 | `https://acme-v02.api.letsencrypt.org/directory` | 证书联系邮箱，不使用 API Key | `/opt/juyou_ai/deploy/.deploy.conf` |
| jsDelivr | 按需更新部署目录中的 `acme.sh` | `https://cdn.jsdelivr.net/gh/acmesh-official/acme.sh@master/acme.sh` | 无 | 不保存凭证 |
| Hugging Face 下载源 | 首次在本地准备离线语音识别文件 | 由 `faster-whisper` 下载工具访问 | 当前未配置访问令牌 | 不保存凭证 |

## 2. AI 渠道

### 2.1 当前渠道登记

| 渠道名称 | 渠道 URL | API Key |
| --- | --- | --- |
| AP | `https://api.apimart.ai/v1` | `<在管理后台维护>` |

渠道只需要登记基础网址和 API Key，具体模型不在本文档记录。

管理后台配置位置：

```text
ADMIN → 渠道管理 → 新增或编辑渠道 → 渠道 URL、API Key
```

### 2.2 认证方式

所有渠道请求使用 Bearer Token：

```http
Authorization: Bearer <渠道 API Key>
```

提交异步任务时还会根据任务请求编号发送：

```http
Idempotency-Key: <请求编号>
X-Request-ID: <请求编号>
Content-Type: application/json
```

`Idempotency-Key` 和 `X-Request-ID` 是任务标识，不是密钥。

### 2.3 项目调用的接口路径

项目会在渠道 URL 后拼接下列路径：

| 请求 | 完整接口 | 用途 |
| --- | --- | --- |
| `POST` | `https://api.apimart.ai/v1/chat/completions` | 提交文本类请求，项目强制使用非流式响应 |
| `POST` | `https://api.apimart.ai/v1/images/generations` | 提交图片生成任务 |
| `POST` | `https://api.apimart.ai/v1/videos/generations` | 提交视频生成任务 |
| `GET` | `https://api.apimart.ai/v1/tasks/{task_id}` | 查询异步任务状态和结果地址 |
| `GET` | `https://api.apimart.ai/v1/user/balance` | 同步渠道余额 |

渠道至少需要兼容以上接口、Bearer 认证以及 JSON 请求和响应。图片或视频接口可以直接返回结果网址，也可以返回任务 ID 后由项目轮询。

### 2.4 渠道密钥如何保存

1. 管理员在管理后台输入渠道 API Key。
2. API 使用 `CONFIG_ENCRYPTION_KEY` 对明文密钥进行 AES-256-GCM 加密。
3. 密文写入 PostgreSQL 的 `channels.api_key_ciphertext`。
4. 密钥末四位写入 `channels.api_key_last4`，用于无法解密时识别密钥。
5. 管理端查询渠道时不会返回完整明文，只显示前后部分字符的掩码。
6. Worker 执行任务或同步余额时，才在服务器内存中解密并放入 Authorization 请求头。

因此，数据库备份中的渠道密钥是密文，但同时取得数据库备份和 `CONFIG_ENCRYPTION_KEY` 的人员仍可恢复明文，两者必须分开保管。

### 2.5 新增或更换渠道密钥

新增渠道：

1. 在渠道服务商处创建 API Key。
2. 在管理后台新增渠道。
3. 填写渠道名称、基础网址和 API Key。
4. 保存后确认后台显示的掩码尾号与新密钥一致。
5. 通过渠道余额同步或一项低成本任务验证权限。

更换密钥：

1. 先在渠道服务商处创建新密钥，不要立即删除旧密钥。
2. 在管理后台编辑渠道，选择“更换 API Key”并保存。
3. 确认余额同步和任务提交正常。
4. 再到渠道服务商处撤销旧密钥。
5. 更新本文档中的密钥尾号和轮换记录，不记录明文。

## 3. 腾讯云 COS

### 3.1 当前存储配置

| 配置项 | 当前值或登记值 | 是否敏感 |
| --- | --- | --- |
| 腾讯云控制台 | `https://console.cloud.tencent.com/cos` | 否 |
| `COS_SECRET_ID` | `<腾讯云子账号 SecretId>` | 是 |
| `COS_SECRET_KEY` | `<腾讯云子账号 SecretKey>` | 是，禁止泄露 |
| `COS_BUCKET` | `juyouai-1451538162` | 否 |
| `COS_REGION` | `ap-chengdu` | 否 |
| `COS_ENDPOINT` | `https://cos.ap-chengdu.myqcloud.com` | 否 |
| `COS_PUBLIC_BASE_URL` | `https://cos.youjuhui.xyz` | 否 |

生产环境中的真实凭证保存在：

```text
/etc/juyou_ai/juyou_ai.env
```

本地开发凭证保存在：

```text
API/.env
```

### 3.2 使用的 COS 接口

项目通过 AWS S3 兼容协议访问 COS，区域使用 `ap-chengdu`，并使用虚拟主机寻址。实际执行的操作包括：

| S3 操作 | 用途 |
| --- | --- |
| `PutObject` | 上传用户文件、抽帧、识别结果、任务原始响应和生成结果 |
| `GetObject` | 后端读取已存储对象 |
| `DeleteObject` | 删除业务数据时清理对应对象 |

腾讯云子账号至少需要目标存储桶的对象读取、写入和删除权限。若缺少删除权限，业务记录可以删除，但 COS 对象清理会失败并产生遗留文件。

### 3.3 公开访问域名

`COS_PUBLIC_BASE_URL` 必须是外部可以访问的 HTTPS 地址。项目会按以下形式生成媒体地址：

```text
https://cos.youjuhui.xyz/juyou_ran/<业务对象键>
```

该网址会提供给：

- WEB 和 ADMIN 页面展示媒体；
- 第三方渠道读取参考图片、音频或视频；
- 服务端重新下载上游生成结果。

公开域名不使用 SecretKey，但必须正确绑定 `juyouai-1451538162` 存储桶。第三方渠道无法访问该域名时，带参考媒体的任务会失败。

### 3.4 COS CORS

COS 存储桶需要允许实际 WEB 域名执行 `GET` 和 `HEAD`。建议规则：

```json
[
  {
    "AllowedOrigins": [
      "https://实际WEB域名"
    ],
    "AllowedMethods": [
      "GET",
      "HEAD"
    ],
    "AllowedHeaders": [
      "*"
    ],
    "ExposeHeaders": [
      "Accept-Ranges",
      "Content-Length",
      "Content-Range",
      "Content-Type",
      "ETag"
    ],
    "MaxAgeSeconds": 3600
  }
]
```

缺少 CORS 时，媒体可能仍能直接打开或播放，但浏览器中的抽帧、Blob 下载等功能会被拦截。

### 3.5 更换 COS 密钥

1. 在腾讯云 CAM 中为程序子账号创建新的 API 密钥。
2. 备份 `/etc/juyou_ai/juyou_ai.env`。
3. 更新 `COS_SECRET_ID` 和 `COS_SECRET_KEY`。
4. 重启 `juyou_ai` 服务。
5. 验证上传、读取和删除操作。
6. 确认新密钥正常后撤销旧 API 密钥。

```bash
sudo systemctl restart juyou_ai
sudo journalctl -u juyou_ai -n 100 --no-pager
```

## 4. Let's Encrypt 与 acme.sh

### 4.1 服务信息

| 项目 | 值 |
| --- | --- |
| 正式 ACME 接口 | `https://acme-v02.api.letsencrypt.org/directory` |
| 认证方式 | ACME HTTP-01 |
| API Key | 不需要 |
| 必需登记信息 | 证书联系邮箱、域名 |
| 验证目录 | `/var/www/jcf-acme` |
| 证书目录 | `/etc/nginx/ssl` |

域名和邮箱保存在：

```text
/opt/juyou_ai/deploy/.deploy.conf
```

部署脚本先让 Nginx 提供 `/.well-known/acme-challenge/`，再调用项目内的 `API/acme/acme.sh` 申请证书。证书申请要求域名已解析到服务器，并且公网可以访问 TCP 80。

已有匹配域名的证书和私钥时，部署脚本直接复用，不会重复申请。

## 5. 部署阶段使用的无密钥第三方来源

### 5.1 jsDelivr

只有手动执行以下脚本时才访问 jsDelivr：

```bash
sudo bash /opt/juyou_ai/deploy/update_acme.sh
```

下载地址：

```text
https://cdn.jsdelivr.net/gh/acmesh-official/acme.sh@master/acme.sh
```

该请求不使用 API Key。脚本下载后会检查内容中是否存在 `VER=` 版本标识，再替换项目中的 `acme.sh`。

### 5.2 Hugging Face 下载源

当服务器缺少离线语音识别文件时，`push.bat` 会在 Windows 本机调用 Python 下载工具准备文件，然后上传服务器。当前代码未读取或配置 Hugging Face Access Token，服务器部署和运行阶段不会访问该下载源。

如果以后使用需要授权的资源，应通过受保护的本机环境变量或 Hugging Face 凭证存储配置，不得把令牌写入部署脚本或本文档。

## 6. 不是第三方接口密钥的项目安全配置

下列配置同样敏感，但不属于第三方 API 凭证：

| 配置项 | 用途 | 保存位置 |
| --- | --- | --- |
| `CONFIG_ENCRYPTION_KEY` | 加密和解密渠道 API Key | 生产环境文件 |
| `JWT_SECRET_KEY` | 签发和校验用户、管理员登录令牌 | 生产环境文件 |
| `DATABASE_PASSWORD` | PostgreSQL 应用账号密码 | 生产环境文件 |
| `REDIS_PASSWORD` | Redis 密码，当前为空时不启用认证 | 生产环境文件 |
| `ADMIN_BOOTSTRAP_PASSWORD` | 数据库没有管理员时创建初始管理员 | 生产环境文件 |

其中 `CONFIG_ENCRYPTION_KEY` 必须是 32 字节随机值的标准 Base64 编码。丢失该密钥后，数据库中已有的渠道密钥无法解密，只能逐个重新录入；更换它时也不能只修改环境变量，必须先完成渠道密文重加密方案。

## 7. 密钥存放与权限要求

- 生产第三方密钥只保存在 `/etc/juyou_ai/juyou_ai.env` 或数据库加密字段中。
- `/etc/juyou_ai/juyou_ai.env` 权限应为 `root:root`、`600`。
- `API/.env` 不得提交到 Git，也不得通过普通聊天、邮件或工单正文传递。
- Markdown 文档只记录密钥负责人、用途、尾号和轮换时间，不记录完整明文。
- 渠道密钥、COS 密钥和 `CONFIG_ENCRYPTION_KEY` 不应由同一个公开位置统一保存。
- 日志中不得打印 Authorization 请求头、COS SecretKey 或解密后的渠道密钥。
- 人员离职、权限范围变化或怀疑泄露时，应立即轮换对应密钥。

## 8. 密钥轮换登记

| 服务 | 密钥标识或尾号 | 最近轮换时间 | 负责人 | 下次计划时间 |
| --- | --- | --- | --- | --- |
| AP 渠道 | `7MNC` | `<填写>` | `<填写>` | `<填写>` |
| 腾讯云 COS | `<SecretId 尾号>` | `<填写>` | `<填写>` | `<填写>` |
| Let's Encrypt 邮箱 | `<邮箱>` | `<填写>` | `<填写>` | `<填写>` |

轮换登记只用于识别凭证，不填写完整密钥。
