# 部署文档(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用户名>:@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 " \ -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 ` - 检查环境变量值是否前后有空格 ### 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 路由 ```