xtc-backend / DEPLOY.md
a3216's picture
deploy: initial release to Hugging Face Spaces
36fae79
|
Raw
History Blame Contribute Delete
15.6 kB
# 部署文档(Hugging Face Spaces)
本文档说明如何把 XTC 后端部署到 Hugging Face Spaces,采用**混合存储方案**:本地 SQLite 持久卷(热存储)+ HF Hub dataset 仓库(配置备份)。
> **存储方案说明**:本项目采用混合方案——
> - **主体数据**(令牌、会话、请求历史、用量、审计、速率限制、Webhook)存在 HF Spaces 本地的 `/data/xtc.db`(SQLite)
> - **配置文件**(providers / 默认厂商)额外同步到 HF Hub dataset 仓库做备份
>
> **为什么配置要走 Hub?** HF Spaces **免费档磁盘是 ephemeral(临时)**,restart/stop 会清空 `/data`,导致配置丢失。把配置 JSON 备份到 HF Hub dataset 仓库(随账号持久保存),重建后能自动拉取恢复。其余数据(会话/日志等)反正不重启也无所谓,纯本地即可。
>
> **付费档用户**($5/月 Small 持久存储):`/data` 永久保留,可不配置 Hub 备份(但配了也无妨,多一层保险)。
---
## 一、整体架构
```
手表端 / 前端
│ HTTPS
Cloudflare Workers(反向代理,可选)
│ 解决:免费域名 / 国内访问 / 48h 休眠保活
Hugging Face Spaces(Docker 类型)
│ 运行 FastAPI + uvicorn
/data/xtc.db(SQLite 持久卷)
├─ config 配置(providers / 默认厂商)──┐ 备份
├─ access_tokens 临时令牌 │
├─ sessions 会话历史 │
├─ session_messages 会话消息 │
├─ request_log 请求历史(最近 N 条) │ 仅配置
├─ usage_log 用量统计 │ 走 Hub
├─ audit_log 审计日志 │
├─ rate_limit 速率限制 │
└─ webhook_config Webhook 配置 │
HF Hub dataset 仓库(config/app_config.json)
解决免费档 ephemeral 问题,重建后自动恢复配置
```
**为什么仅配置走 Hub,其余纯本地?**
- 单实例部署,Hub 的"多实例同步"优势用不上,避免无谓的网络开销
- 本地 SQLite 读写比网络 API 快几个数量级
- 配置是唯一"丢了很麻烦"的数据(要重新填所有 API Key),其余数据(日志/会话)重建丢失可接受
- 图片/文件前端会重传,不需要后端长期存储
- 复杂度可控:只在 `config_store` 一处接入 Hub,其余模块无感知
**持久性保证**
- HF Spaces 免费档:restart/stop 清空 `/data`,但配置已备份到 Hub,重建后自动恢复
- HF Spaces 付费档($5/月):`/data` 永久保留,所有数据都不丢
- 删除整个 Space:`/data` 丢失,但 Hub 仓库里的配置备份仍在
---
## 二、前置准备
### 1. 注册 Hugging Face 账号
访问 https://huggingface.co/join 注册(免费)。
### 2. 准备代码仓库
本项目代码在 GitHub 仓库 `luckfun233/AI``xtc-backend-hf/` 目录。HF Spaces 支持两种方式同步代码:
- **方式 A(推荐)**:HF Space 直接从 GitHub 同步(每次 push 自动重建)
- **方式 B**:手动把代码推到 HF Space 的 Git 仓库
---
## 三、创建 HF Space
### 1. 新建 Space
1. 访问 https://huggingface.co/new-space
2. 填写:
- **Space name**`xtc-backend`(或任意名)
- **License**:随意
- **SDK**:选择 **Docker**
- **Space hardware****CPU basic (Free)**(免费档够用)
- **Visibility****Private**(强烈建议私有,避免后台暴露)
3. 点击 **Create Space**
### 2. 绑定持久化存储(可选,付费档)
**免费档磁盘是 ephemeral(临时)**,restart/stop 会清空 `/data`。本项目通过 HF Hub 仓库备份配置来规避此问题(见下一步),所以**免费档也能用**
如需所有数据(会话/日志等)都持久保留,可开通付费持久存储:
1. 进入刚创建的 Space
2. 点击顶部 **Settings** 标签
3. 找到 **Persistent storage** 区域
4. 选择容量(**20 GB Small - $5/month**,最小档够用)
5. 点击 **Attach** 并完成支付
> **免费档 vs 付费档**
> - **免费档**:配置靠 Hub 备份(不丢),其余数据 restart/stop 后清空(可接受)
> - **付费档**:所有数据永久保留,Hub 备份作为额外保险
### 3. 创建 HF Hub 配置备份仓库(免费档强烈建议)
用于备份配置文件,解决免费档磁盘 ephemeral 问题:
1. 访问 https://huggingface.co/new-dataset
2. 填写:
- **Dataset name**`xtc-config`(或任意名)
- **License**:随意
- **Visibility****Private**(强烈建议私有,配置含 API Key)
3. 点击 **Create dataset**
记录仓库名称,格式为 `用户名/xtc-config`(如 `luckfun233/xtc-config`),后面配置 `HF_CONFIG_REPO` 要用。
### 4. 创建 HF Access Token
用于后端读写配置备份仓库:
1. 访问 https://huggingface.co/settings/tokens
2. 点击 **New token**
3. 填写 Name(如 `xtc-backend`),Type 选 **Write**(需要写权限推送配置)
4. 复制 token(以 `hf_` 开头),后面配置 `HF_TOKEN` 要用
> **权限说明**:token 需要对上一步创建的 dataset 仓库有 write 权限。Space 和 dataset 在同一账号下时,Write token 自动有权限。
### 5. 同步代码
**方式 A:从 GitHub 同步(推荐)**
1. Space Settings → **Repositories****Connect a GitHub repository**
2. 选择 `luckfun233/AI` 仓库
3. **Root directory** 填写:`xtc-backend-hf`
4. 保存后,每次 GitHub push 自动触发 HF 重建
**方式 B:手动推送**
```bash
# 在 HF 账号设置里创建 Access Token:https://huggingface.co/settings/tokens
# 权限选 Write,复制 token
cd xtc-backend-hf
git init
git remote add hf https://<你的HF用户名>:<HF_TOKEN>@huggingface.co/spaces/<你的HF用户名>/xtc-backend
git add .
git commit -m "init"
git push hf main
```
---
## 四、配置环境变量
**这是最关键的一步**。在 Space Settings → **Variables and secrets** 中添加以下环境变量:
### 必填(不填无法正常运行)
| 变量名 | 说明 | 示例值 |
|---|---|---|
| `XTC_ADMIN_KEY` | 后台管理密钥,访问 `/admin` 登录用 | `my-super-admin-key-2026` |
| `XTC_ACCESS_KEY` | 前端/手表端长期访问密钥 | `my-access-key-for-watch` |
| `XTC_JWT_SECRET` | JWT 签名密钥(临时令牌),**必须随机长字符串** | `a1b2c3d4e5...(32位以上随机)` |
> **生成随机密钥**:在本地终端运行 `openssl rand -hex 32` 生成 `XTC_JWT_SECRET`。
### 选填(有默认值,通常不用改)
| 变量名 | 默认值 | 说明 |
|---|---|---|
| `PORT` | `7860` | 服务端口(HF Spaces 固定 7860,不要改) |
| `XTC_DB_PATH` | `/data/xtc.db` | SQLite 数据库路径(持久卷路径,不要改) |
| `XTC_DISABLE_ADMIN` | `false` | 紧急关闭后台(设 `true``/admin` 不可用) |
| `UPSTREAM_TIMEOUT_MS` | `120000` | 上游 API 超时(毫秒) |
| `XTC_ACCESS_TOKEN_TTL_SEC` | `1800` | 临时令牌有效期(秒,60-86400) |
| `XTC_ACCESS_TOKEN_LENGTH` | `8` | 临时令牌长度(6-24) |
### 选填(种子厂商,也可启动后在后台配)
| 变量名 | 说明 |
|---|---|
| `GEMINI_API_KEY` | Gemini API 密钥(可填多个,逗号分隔) |
| `OPENAI_API_KEY` | OpenAI 兼容厂商的 API 密钥 |
| `OPENAI_BASE_URL` | OpenAI 兼容厂商的 base URL(默认 `https://api.openai.com`) |
> **建议**:`GEMINI_API_KEY` / `OPENAI_API_KEY` 留空,启动后用 `/admin` 后台添加厂商,这样密钥存在 SQLite 而非环境变量里,方便随时增删轮询。
### 选填(HF Hub 配置备份,免费档强烈建议)
| 变量名 | 说明 | 示例值 |
|---|---|---|
| `HF_TOKEN` | HF Hub 访问令牌(write 权限,见第三节第 4 步) | `hf_xxxxxxxxxxxxxxxxxxxx` |
| `HF_CONFIG_REPO` | 配置备份 dataset 仓库名(见第三节第 3 步) | `luckfun233/xtc-config` |
> **配置后才生效**:两个变量都填了才会启用 Hub 配置备份。只填一个不生效。
> **降级行为**:未配置时,后端纯本地运行(功能正常),但 restart/stop 后配置会丢失,需重新在后台添加厂商。
> **付费档用户**:`/data` 已持久,可不配(但配了多一层保险,删 Space 重建也能恢复配置)。
### 配置方式
在 Space Settings → **Variables and secrets**
- **Variables**:普通环境变量(明文存储,如 `PORT``XTC_DB_PATH``HF_CONFIG_REPO`
- **Secrets**:敏感信息(加密存储,推荐用于 `XTC_ADMIN_KEY``XTC_ACCESS_KEY``XTC_JWT_SECRET``HF_TOKEN``GEMINI_API_KEY`
---
## 五、首次启动验证
### 1. 等待构建
Space 创建后会自动拉取代码、构建 Docker 镜像。在 Space 主页能看到构建日志,看到 `Running on http://0.0.0.0:7860` 即构建成功。
### 2. 健康检查
访问 `https://<你的用户名>-xtc-backend.hf.space/health`,应返回:
```json
{"ok": true, "service": "xtc-backend-hf", "version": "..."}
```
### 3. 登录后台
访问 `https://<你的用户名>-xtc-backend.hf.space/admin`,输入 `XTC_ADMIN_KEY` 登录。
### 4. 添加厂商
在后台 **厂商** Tab:
1. 点击"添加厂商"
2. 填写:
- **ID**`gemini`(或任意唯一标识)
- **名称**`Gemini`
- **类型**`gemini`
- **API Keys**:填入你的 Gemini API Key(每行一个,支持多密钥轮询)
3. 保存
4. 设为默认厂商
### 5. 测试对话
在后台 **测试** Tab,输入消息测试是否正常返回。
### 6. 测试 API
```bash
curl -X POST https://<你的用户名>-xtc-backend.hf.space/v1/chat/completions \
-H "Authorization: Bearer <XTC_ACCESS_KEY>" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-flash-latest",
"messages": [{"role": "user", "content": "你好"}]
}'
```
---
## 六、Cloudflare Workers 反向代理(可选但推荐)
解决三个问题:
1. **免费域名**:HF Spaces 默认域名 `*.hf.space` 在国内可能无法访问
2. **国内访问**:CF Workers 节点在国内可达
3. **48h 休眠保活**:HF 免费档 48h 无请求会休眠,CF Workers 定时器保活
### 1. 创建 Worker
在 Cloudflare Dashboard → Workers & Pages → Create Worker:
```javascript
export default {
async fetch(request) {
const url = new URL(request.url);
url.hostname = '<你的用户名>-xtc-backend.hf.space';
return fetch(url, request);
},
// 每 24 小时保活一次
async scheduled(event, env, ctx) {
ctx.waitUntil(fetch('https://<你的用户名>-xtc-backend.hf.space/health'));
}
};
```
### 2. 配置定时器
Worker Settings → **Triggers****Cron Triggers**
```
0 */12 * * *
```
(每 12 小时触发一次,保活)
### 3. 绑定自定义域名(可选)
Worker → Settings → **Triggers****Custom Domains**,添加你自己的域名。
---
## 七、数据备份与恢复
### 自动备份(配置文件)
配置了 `HF_TOKEN` + `HF_CONFIG_REPO` 后,每次在后台修改厂商配置都会**自动**把配置 JSON 推送到 HF Hub dataset 仓库的 `config/app_config.json`。无需手动操作。
### 手动备份
后台 → **概览** Tab 有"导出配置"按钮,可下载 `app_config.json`(含所有厂商配置)到本地。
如需备份完整数据库(含会话/日志),需通过 SSH/终端方式(HF Spaces 付费档支持)复制 `/data/xtc.db`
### 恢复
- **免费档 restart/stop 后**`/data` 被清空,但启动时后端会自动从 Hub 拉取 `config/app_config.json` 恢复配置(需已配置 `HF_TOKEN` + `HF_CONFIG_REPO`)。其余数据(会话/日志)丢失。
- **付费档重建后**`/data` 保留,所有数据自动恢复,无需操作。
- **删除 Space 重建**:先配置环境变量(含 `HF_TOKEN` + `HF_CONFIG_REPO`),启动后配置自动从 Hub 恢复;或登录后台 → 厂商 → 导入手动导出的配置 JSON。
---
## 八、常见问题
### Q1: 重建 Space 后配置丢失?
**原因**:免费档磁盘是 ephemeral,restart/stop 会清空 `/data`;且未配置 HF Hub 配置备份。
**解决**
1. 配置 `HF_TOKEN` + `HF_CONFIG_REPO` 环境变量(见第三节第 3-4 步),启用配置自动备份
2. 启动时后端会自动从 Hub 拉取配置恢复
3. 或开通付费持久存储($5/月),`/data` 永久保留
### Q2: 后台登录提示 "invalid admin key"?
**原因**`XTC_ADMIN_KEY` 环境变量未设置或拼写错误。
**解决**:Space Settings → Variables and secrets 检查 `XTC_ADMIN_KEY`,注意 Secrets 修改后需要重启 Space。
### Q3: 手表端请求返回 401?
**原因**`XTC_ACCESS_KEY` 不匹配,或请求头格式错误。
**解决**
- 请求头格式:`Authorization: Bearer <XTC_ACCESS_KEY>`
- 检查环境变量值是否前后有空格
### Q4: Space 休眠了怎么办?
**原因**:HF 免费档 48h 无请求自动休眠。
**解决**
- 配置 Cloudflare Workers 定时器(见第六节)
- 或用外部监控服务(如 UptimeRobot)每 5 分钟 ping `/health`
### Q5: 怎么查看请求日志?
后台 → **请求历史** Tab,默认保留最近 500 条完整请求(含标头、body、响应、耗时、错误码)。可在该 Tab 调整保留条数(0-2000)。
### Q6: 怎么添加多个 API Key 做轮询?
后台 → **厂商** → 编辑厂商 → **API Keys** 字段,每行填一个 key,自动轮询。
### Q7: 国内访问慢/连不上?
1. 用 Cloudflare Workers 反向代理(见第六节)
2. 或绑定自定义域名走 CF CDN
---
## 九、环境变量完整清单
```bash
# ===== 必填 =====
XTC_ADMIN_KEY=<你的后台管理密钥>
XTC_ACCESS_KEY=<前端访问密钥>
XTC_JWT_SECRET=<32位以上随机字符串>
# ===== 默认值(通常不用改)=====
PORT=7860
XTC_DB_PATH=/data/xtc.db
XTC_DISABLE_ADMIN=false
UPSTREAM_TIMEOUT_MS=120000
XTC_ACCESS_TOKEN_TTL_SEC=1800
XTC_ACCESS_TOKEN_LENGTH=8
# ===== 种子厂商(可选,留空用后台配)=====
GEMINI_API_KEY=
OPENAI_API_KEY=
OPENAI_BASE_URL=https://api.openai.com
# ===== HF Hub 配置备份(免费档强烈建议)=====
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxx
HF_CONFIG_REPO=你的用户名/xtc-config
```
---
## 十、文件结构说明
部署后 Space 内的关键文件:
```
/data/ # 本地存储卷(免费档 ephemeral,付费档持久)
└── xtc.db # SQLite 数据库(所有数据,配置额外备份到 Hub)
/app/ # 应用代码(每次重建从 Git 拉取)
├── Dockerfile
├── requirements.txt
└── app/
├── main.py # FastAPI 入口
├── config.py # 环境变量配置
├── database.py # SQLite 初始化
├── hf_storage.py # HF Hub 配置备份抽象层
├── auth.py # 鉴权(admin key + access key + JWT)
├── middleware.py # 速率限制中间件
├── request_log_middleware.py # 请求日志中间件
├── errors.py # 统一错误模型
├── admin_html.py # 后台 HTML
├── adapters/ # Gemini/OpenAI 适配器
├── services/ # 业务逻辑层(config_store 接入 Hub 备份)
└── api/ # API 路由
```