Files
sub2api/AGENTS.md
2026-03-04 10:15:42 +08:00

39 KiB
Raw Blame History

Sub2API 开发说明

版本管理策略

版本号规则

我们在官方版本号后面添加自己的小版本号:

  • 官方版本:v0.1.68
  • 我们的版本:v0.1.68.1v0.1.68.2(递增)

分支策略

分支 说明
main 我们的主分支,包含所有定制功能
release/custom-X.Y.Z 基于官方 vX.Y.Z 的发布分支
upstream/main 上游官方仓库

发布流程(基于新官方版本)

当官方发布新版本(如 v0.1.69)时:

1. 同步上游并创建发布分支

# 获取上游最新代码
git fetch upstream --tags

# 基于官方标签创建新的发布分支
git checkout v0.1.69 -b release/custom-0.1.69

# 合并我们的 main 分支(包含所有定制功能)
git merge main --no-edit

# 解决可能的冲突后继续

2. 更新版本号并打标签

# 更新版本号文件
echo "0.1.69.1" > backend/cmd/server/VERSION
git add backend/cmd/server/VERSION
git commit -m "chore: bump version to 0.1.69.1"

# 打上我们自己的标签
git tag v0.1.69.1

# 推送分支和标签
git push origin release/custom-0.1.69
git push origin v0.1.69.1

3. 更新 main 分支

# 将发布分支合并回 main保持 main 包含最新定制功能
git checkout main
git merge release/custom-0.1.69
git push origin main

热修复发布(在现有版本上修复)

当需要在当前版本上发布修复时:

# 在当前发布分支上修复
git checkout release/custom-0.1.68
# ... 进行修复 ...
git commit -m "fix: 修复描述"

# 递增小版本号
echo "0.1.68.2" > backend/cmd/server/VERSION
git add backend/cmd/server/VERSION
git commit -m "chore: bump version to 0.1.68.2"

# 打标签并推送
git tag v0.1.68.2
git push origin release/custom-0.1.68
git push origin v0.1.68.2

# 同步修复到 main
git checkout main
git cherry-pick <fix-commit-hash>
git push origin main

服务器部署流程

前置条件

  • 本地已配置 SSH 别名 clicodeplus 连接到生产服务器(运行服务)
  • 本地已配置 SSH 别名 us-asaki-root 连接到构建服务器(拉取代码、构建镜像)
  • 生产服务器部署目录:/root/sub2api(正式)、/root/sub2api-beta(测试)、/root/sub2api-starStar
  • 生产服务器使用 Docker Compose 部署
  • 镜像统一在构建服务器上构建,避免生产服务器因编译占用 CPU/内存影响线上服务

服务器角色说明

服务器 SSH 别名 职责
构建服务器 us-asaki-root 拉取代码、docker build 构建镜像
生产服务器 clicodeplus 加载镜像、运行服务、部署验证
数据库服务器 db-clicodeplus PostgreSQL 16 + Redis 7所有环境共用

数据库服务器运维手册:db-clicodeplus:/root/README.md

部署环境说明

环境 目录(生产服务器) 端口 数据库 Redis DB 容器名
正式 /root/sub2api 8080 sub2api 0 sub2api
Beta /root/sub2api-beta 8084 beta 2 sub2api-beta
OpenAI /root/sub2api-openai 8083 openai 3 sub2api-openai
Star /root/sub2api-star 8086 star 4 sub2api-star

外部数据库与 Redis

所有环境正式、Beta、OpenAI、Star共用 db.clicodeplus.com 上的 PostgreSQL 16Redis 7,不使用容器内数据库或 Redis。

PostgreSQL(端口 5432TLS 加密scram-sha-256 认证):

环境 用户名 数据库
正式 sub2api sub2api
Beta beta beta
OpenAI openai openai
Star star star

Redis(端口 6379密码认证

环境 DB
正式 0
Beta 2
OpenAI 3
Star 4

配置方式

  • 数据库通过 .env 中的 DATABASE_HOSTDATABASE_SSLMODEPOSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_DB 配置
  • Redis 通过 docker-compose.override.yml 覆盖 REDIS_HOST(因主 compose 文件硬编码为 redis),密码通过 .env 中的 REDIS_PASSWORD 配置
  • 各环境的 docker-compose.override.yml 已通过 depends_on: !reset {}redis: profiles: [disabled] 去掉了对容器 Redis 的依赖

数据库操作命令

通过 SSH 在服务器上执行数据库操作:

# 正式环境 - 查询迁移记录
ssh clicodeplus "source /root/sub2api/deploy/.env && PGPASSWORD=\"\$POSTGRES_PASSWORD\" psql -h \$DATABASE_HOST -U \$POSTGRES_USER -d \$POSTGRES_DB -c 'SELECT * FROM schema_migrations ORDER BY applied_at DESC LIMIT 5;'"

# Beta 环境 - 查询迁移记录
ssh clicodeplus "source /root/sub2api-beta/deploy/.env && PGPASSWORD=\"\$POSTGRES_PASSWORD\" psql -h \$DATABASE_HOST -U \$POSTGRES_USER -d \$POSTGRES_DB -c 'SELECT * FROM schema_migrations ORDER BY applied_at DESC LIMIT 5;'"

# Beta 环境 - 清除指定迁移记录(重新执行迁移)
ssh clicodeplus "source /root/sub2api-beta/deploy/.env && PGPASSWORD=\"\$POSTGRES_PASSWORD\" psql -h \$DATABASE_HOST -U \$POSTGRES_USER -d \$POSTGRES_DB -c \"DELETE FROM schema_migrations WHERE filename LIKE '%049%';\""

# Beta 环境 - 更新账号数据
ssh clicodeplus "source /root/sub2api-beta/deploy/.env && PGPASSWORD=\"\$POSTGRES_PASSWORD\" psql -h \$DATABASE_HOST -U \$POSTGRES_USER -d \$POSTGRES_DB -c \"UPDATE accounts SET credentials = credentials - 'model_mapping' WHERE platform = 'antigravity';\""

注意:使用 source .env 加载环境变量,避免在命令行中暴露密码。

部署步骤

重要:每次部署都必须递增版本号!

0. 递增版本号并推送(本地操作)

每次部署前,先在本地递增小版本号并确保推送成功:

# 查看当前版本号
cat backend/cmd/server/VERSION
# 假设当前是 0.1.69.1

# 递增版本号
echo "0.1.69.2" > backend/cmd/server/VERSION
git add backend/cmd/server/VERSION
git commit -m "chore: bump version to 0.1.69.2"
git push origin release/custom-0.1.69

# ⚠️ 确认推送成功(必须看到分支更新输出,不能有 rejected 错误)

检查点:如果有其他未提交的改动,应先 commit 并 push确保 release 分支上的所有代码都已推送到远程。

1. 构建服务器拉取代码

# 拉取最新代码并切换分支
ssh us-asaki-root "cd /root/sub2api && git fetch origin && git checkout -B release/custom-0.1.69 origin/release/custom-0.1.69"

# ⚠️ 验证版本号与步骤 0 一致
ssh us-asaki-root "cat /root/sub2api/backend/cmd/server/VERSION"

首次使用构建服务器? 需要先初始化仓库,参见下方「构建服务器首次初始化」章节。

2. 构建服务器构建镜像

ssh us-asaki-root "cd /root/sub2api && docker build --no-cache -t sub2api:latest -f Dockerfile ."

# ⚠️ 必须看到构建成功输出,如果失败需要先排查问题

常见构建问题

  • buildx 版本过旧导致 API 版本不兼容 → 更新 buildxcurl -fsSL "https://github.com/docker/buildx/releases/latest/download/buildx-$(curl -fsSL https://api.github.com/repos/docker/buildx/releases/latest | grep tag_name | cut -d'"' -f4).linux-amd64" -o ~/.docker/cli-plugins/docker-buildx && chmod +x ~/.docker/cli-plugins/docker-buildx
  • 磁盘空间不足 → docker system prune -f 清理无用镜像

3. 传输镜像到生产服务器并加载

# 导出镜像 → 通过管道传输 → 生产服务器加载
ssh us-asaki-root "docker save sub2api:latest" | ssh clicodeplus "docker load"

# ⚠️ 必须看到 "Loaded image: sub2api:latest" 输出

4. 生产服务器同步代码、更新标签并重启

# 同步代码(用于版本号确认和 deploy 配置)
ssh clicodeplus "cd /root/sub2api && git fetch fork && git checkout -B release/custom-0.1.69 fork/release/custom-0.1.69"

# 更新镜像标签并重启
ssh clicodeplus "docker tag sub2api:latest weishaw/sub2api:latest"
ssh clicodeplus "cd /root/sub2api/deploy && docker compose up -d --force-recreate sub2api"

5. 验证部署

# 查看启动日志
ssh clicodeplus "docker logs sub2api --tail 20"

# 确认版本号(必须与步骤 0 中设置的版本号一致)
ssh clicodeplus "cat /root/sub2api/backend/cmd/server/VERSION"

# 检查容器状态(必须显示 healthy
ssh clicodeplus "docker ps | grep sub2api"

构建服务器首次初始化

首次使用 us-asaki-root 作为构建服务器时,需要执行以下一次性操作:

ssh us-asaki-root

# 1) 克隆仓库
cd /root
git clone https://github.com/touwaeriol/sub2api.git sub2api
cd sub2api

# 2) 验证 Docker 和 buildx 版本
docker version
docker buildx version
# 如果 buildx 版本过旧(< v0.14),执行更新:
# LATEST=$(curl -fsSL https://api.github.com/repos/docker/buildx/releases/latest | grep tag_name | cut -d'"' -f4)
# curl -fsSL "https://github.com/docker/buildx/releases/download/${LATEST}/buildx-${LATEST}.linux-amd64" -o ~/.docker/cli-plugins/docker-buildx
# chmod +x ~/.docker/cli-plugins/docker-buildx

# 3) 验证构建能力
docker build --no-cache -t sub2api:test -f Dockerfile .
docker rmi sub2api:test

Beta 并行部署(不影响现网)

目标:在同一台服务器上并行启动一个 beta 实例(例如端口 8084严禁改动/重启现网实例(默认目录 /root/sub2api)。

设计原则

  • 新目录beta 使用独立目录,例如 /root/sub2api-beta
  • 敏感信息只放 .envbeta 的数据库密码、JWT_SECRET 等只写入 /root/sub2api-beta/deploy/.env,不要提交到 git。
  • 独立 Compose Project:通过 docker compose -p sub2api-beta ... 启动,确保 network/volume 隔离。
  • 独立端口:通过 .envSERVER_PORT 映射宿主机端口(例如 8084:8080)。

前置检查

# 1) 确保 8084 未被占用
ssh clicodeplus "ss -ltnp | grep :8084 || echo '8084 is free'"

# 2) 确认现网容器还在(只读检查)
ssh clicodeplus "docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}' | sed -n '1,200p'"

首次部署步骤

构建服务器说明:正式和 beta 共用构建服务器上的 /root/sub2api 仓库,通过不同的镜像标签区分(sub2api:latest 用于正式,sub2api:beta 用于测试)。

# 1) 构建服务器构建 beta 镜像(共用 /root/sub2api 仓库,切到目标分支后打 beta 标签)
ssh us-asaki-root "cd /root/sub2api && git fetch origin && git checkout -B release/custom-0.1.71 origin/release/custom-0.1.71"
ssh us-asaki-root "cd /root/sub2api && docker build --no-cache -t sub2api:beta -f Dockerfile ."

# ⚠️ 构建完成后如需恢复正式分支:
# ssh us-asaki-root "cd /root/sub2api && git checkout release/custom-<正式版本>"

# 2) 传输镜像到生产服务器
ssh us-asaki-root "docker save sub2api:beta" | ssh clicodeplus "docker load"
# ⚠️ 必须看到 "Loaded image: sub2api:beta" 输出

# 3) 在生产服务器上准备 beta 环境
ssh clicodeplus

# 克隆代码(仅用于 deploy 配置和版本号确认,不在此构建)
cd /root
git clone https://github.com/touwaeriol/sub2api.git sub2api-beta
cd /root/sub2api-beta
git checkout release/custom-0.1.71

# 4) 准备 beta 的 .env敏感信息只写这里
cd /root/sub2api-beta/deploy

# 推荐:从现网 .env 复制,保证除 DB 名/用户/端口外完全一致
cp -f /root/sub2api/deploy/.env ./.env

# 仅修改以下三项(其他保持不变)
perl -pi -e 's/^SERVER_PORT=.*/SERVER_PORT=8084/' ./.env
perl -pi -e 's/^POSTGRES_USER=.*/POSTGRES_USER=beta/' ./.env
perl -pi -e 's/^POSTGRES_DB=.*/POSTGRES_DB=beta/' ./.env

# 5) 写 compose override避免与现网容器名冲突镜像使用构建服务器传输的 sub2api:betaRedis 使用外部服务)
cat > docker-compose.override.yml <<'YAML'
services:
  sub2api:
    image: sub2api:beta
    container_name: sub2api-beta
    environment:
      - DATABASE_HOST=${DATABASE_HOST:-postgres}
      - DATABASE_SSLMODE=${DATABASE_SSLMODE:-disable}
      - REDIS_HOST=db.clicodeplus.com
    depends_on: !reset {}
  redis:
    profiles:
      - disabled
YAML

# 6) 启动 beta独立 project确保不影响现网
cd /root/sub2api-beta/deploy
docker compose -p sub2api-beta --env-file .env -f docker-compose.yml -f docker-compose.override.yml up -d

# 7) 验证 beta
curl -fsS http://127.0.0.1:8084/health
docker logs sub2api-beta --tail 50

数据库配置约定beta

  • 数据库地址/SSL/密码:与现网一致(从现网 .env 复制即可),均指向 db.clicodeplus.com
  • 仅修改:
    • POSTGRES_USER=beta
    • POSTGRES_DB=beta
    • REDIS_DB=2

注意:需要数据库侧已存在 beta 用户与 beta 数据库,并授予权限;否则容器会启动失败并不断重启。

更新 beta构建服务器构建 + 传输 + 仅重启 beta 容器)

# 1) 构建服务器拉取代码并构建镜像(共用 /root/sub2api 仓库)
ssh us-asaki-root "cd /root/sub2api && git fetch origin && git checkout -B release/custom-0.1.71 origin/release/custom-0.1.71"
ssh us-asaki-root "cd /root/sub2api && docker build --no-cache -t sub2api:beta -f Dockerfile ."
# ⚠️ 必须看到构建成功输出

# 2) 传输镜像到生产服务器
ssh us-asaki-root "docker save sub2api:beta" | ssh clicodeplus "docker load"
# ⚠️ 必须看到 "Loaded image: sub2api:beta" 输出

# 3) 生产服务器同步代码(用于版本号确认和 deploy 配置)
ssh clicodeplus "set -e; cd /root/sub2api-beta && git fetch --all --tags && git checkout -f release/custom-0.1.71 && git reset --hard origin/release/custom-0.1.71"

# 4) 重启 beta 容器并验证
ssh clicodeplus "cd /root/sub2api-beta/deploy && docker compose -p sub2api-beta --env-file .env -f docker-compose.yml -f docker-compose.override.yml up -d --no-deps --force-recreate sub2api"
ssh clicodeplus "sleep 5 && curl -fsS http://127.0.0.1:8084/health"
ssh clicodeplus "cat /root/sub2api-beta/backend/cmd/server/VERSION"

停止/回滚 beta只影响 beta

ssh clicodeplus "cd /root/sub2api-beta/deploy && docker compose -p sub2api-beta -f docker-compose.yml -f docker-compose.override.yml down"

服务器首次部署

1. 构建服务器:克隆代码并配置远程仓库

ssh us-asaki-root
cd /root
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api

# 添加 fork 仓库
git remote add fork https://github.com/touwaeriol/sub2api.git

2. 构建服务器:切换到定制分支并构建镜像

git fetch fork
git checkout -B release/custom-0.1.69 fork/release/custom-0.1.69

cd /root/sub2api
docker build -t sub2api:latest -f Dockerfile .
exit

3. 传输镜像到生产服务器

ssh us-asaki-root "docker save sub2api:latest" | ssh clicodeplus "docker load"

4. 生产服务器:克隆代码并配置环境

ssh clicodeplus
cd /root
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api

# 添加 fork 仓库
git remote add fork https://github.com/touwaeriol/sub2api.git
git fetch fork
git checkout -B release/custom-0.1.69 fork/release/custom-0.1.69

# 配置环境变量
cd deploy
cp .env.example .env
vim .env  # 配置 DATABASE_HOST=db.clicodeplus.com, POSTGRES_PASSWORD, REDIS_PASSWORD, JWT_SECRET 等

# 创建 override 文件Redis 指向外部服务,去掉容器 Redis 依赖)
cat > docker-compose.override.yml <<'YAML'
services:
  sub2api:
    environment:
      - REDIS_HOST=db.clicodeplus.com
    depends_on: !reset {}
  redis:
    profiles:
      - disabled
YAML

5. 生产服务器:更新镜像标签并启动服务

docker tag sub2api:latest weishaw/sub2api:latest
cd /root/sub2api/deploy && docker compose up -d

6. 验证部署

# 查看应用日志
docker logs sub2api --tail 50

# 检查健康状态
curl http://localhost:8080/health

# 确认版本号
cat /root/sub2api/backend/cmd/server/VERSION

7. 常用运维命令

# 查看实时日志
docker logs -f sub2api

# 重启服务
docker compose restart sub2api

# 停止所有服务
docker compose down

# 停止并删除数据卷(慎用!会删除数据库数据)
docker compose down -v

# 查看资源使用情况
docker stats sub2api

定制功能说明

当前定制分支包含以下功能(相对于官方版本):

UI/UX 定制

功能 说明
首页优化 面向用户的价值主张设计
移除 GitHub 链接 用户菜单中不显示 GitHub 导航
微信客服按钮 首页悬浮微信客服入口
限流时间精确显示 账号限流时间显示精确到秒

Antigravity 平台增强

功能 说明
Scope 级别限流 按配额域claude/gemini_text/gemini_image独立限流避免整个账号被锁定
模型级别限流 按具体模型(如 claude-opus-4-5独立限流更精细的限流控制
限流预检查 调度时预检查账号/模型限流状态,避免选中已限流账号
秒级冷却时间 支持 429 响应的秒级精确冷却时间
身份注入优化 模型身份信息注入 + 静默边界防止身份泄露
thoughtSignature 修复 Gemini 3 函数调用 400 错误修复
max_tokens 自动修正 自动修正 max_tokens <= budget_tokens 导致的 400 错误

调度算法优化

功能 说明
分层过滤选择 调度算法从全排序改为分层过滤,提升性能
LRU 随机选择 相同 LRU 时间时随机选择,避免账号集中
限流等待阈值配置化 可配置的限流等待阈值

运维增强

功能 说明
Scope 限流统计 运维界面展示 Antigravity 账号 scope 级别限流统计
账号限流状态显示 账号列表显示 scope 和模型级别限流状态
清除限流按钮增强 有 scope/模型限流时也显示清除限流按钮

其他修复

功能 说明
.gitattributes 确保迁移文件使用 LF 换行符(解决 Windows 下 SQL 摘要不一致)
部署配置优化 DATABASE_HOST 和 DATABASE_SSLMODE 可通过 .env 配置

Admin API 接口文档

⚠️ API 操作流程规范

当收到操作正式环境 Web 界面的新需求,但文档中未记录对应 API 接口时,必须按以下流程执行

  1. 探索接口:通过代码库搜索路由定义(backend/internal/server/routes/、Handlerbackend/internal/handler/admin/)和请求结构体,确定正确的 API 端点、请求方法、请求体格式
  2. 更新文档:将新发现的接口补充到本文档的 Admin API 接口文档章节中,包含端点、参数说明和 curl 示例
  3. 执行操作:根据最新文档中记录的接口完成用户需求

目的:避免每次遇到相同需求都重复探索代码库,确保 API 文档持续完善,后续操作可直接查阅文档执行。


认证方式

所有 Admin API 通过 x-api-key 请求头传递 Admin API Key 认证。

x-api-key: admin-xxx

使用说明Admin API Key 统一存放在项目根目录 .env 文件的 ADMIN_API_KEY 变量中(该文件已被 .gitignore 排除,不会提交到代码库)。操作前先从 .env 读取密钥;若密钥失效(返回 401应提示用户提供新的密钥并更新到 .env 中。Token 格式为 admin- + 64 位十六进制字符,在管理后台 设置 > Admin API Key 中生成。请勿将实际 token 写入文档或代码中。

环境地址

环境 基础地址 说明
正式 https://clicodeplus.com 生产环境
Beta http://<服务器IP>:8084 仅内网访问
OpenAI http://<服务器IP>:8083 仅内网访问
Star https://hyntoken.com 独立环境

以下接口文档中,${BASE} 代表环境基础地址,${KEY} 代表 .env 中的 ADMIN_API_KEY。操作前执行 source .envexport KEY=$ADMIN_API_KEY 加载。


1. 账号管理

1.1 获取账号列表

GET /api/v1/admin/accounts

查询参数

参数 类型 必填 说明
platform string 平台筛选:antigravity / anthropic / openai / gemini
type string 账号类型:oauth / api_key / cookie
status string 状态:active / disabled / error
search string 搜索关键词(名称、备注)
page int 页码,默认 1
page_size int 每页数量,默认 20
curl -s "${BASE}/api/v1/admin/accounts?platform=antigravity&page=1&page_size=100" \
  -H "x-api-key: ${KEY}"

响应

{
  "code": 0,
  "message": "success",
  "data": {
    "items": [{"id": 1, "name": "xxx@gmail.com", "platform": "antigravity", "status": "active", ...}],
    "total": 66
  }
}

1.2 获取账号详情

GET /api/v1/admin/accounts/:id
curl -s "${BASE}/api/v1/admin/accounts/1" -H "x-api-key: ${KEY}"

1.3 测试账号连接

POST /api/v1/admin/accounts/:id/test

请求体JSON可选

字段 类型 必填 说明
model_id string 指定测试模型,如 claude-opus-4-6;不传则使用默认模型

响应格式SSEServer-Sent Events

curl -N -X POST "${BASE}/api/v1/admin/accounts/1/test" \
  -H "x-api-key: ${KEY}" \
  -H "Content-Type: application/json" \
  -d '{"model_id": "claude-opus-4-6"}'

SSE 事件类型

type 字段 说明
test_start model 测试开始,返回测试模型名
content text 模型响应内容(流式文本片段)
test_end success, error 测试结束,success=true 表示成功
error text 错误信息

1.4 清除账号限流

POST /api/v1/admin/accounts/:id/clear-rate-limit
curl -X POST "${BASE}/api/v1/admin/accounts/1/clear-rate-limit" \
  -H "x-api-key: ${KEY}"

1.5 清除账号错误状态

POST /api/v1/admin/accounts/:id/clear-error
curl -X POST "${BASE}/api/v1/admin/accounts/1/clear-error" \
  -H "x-api-key: ${KEY}"

1.6 获取账号可用模型

GET /api/v1/admin/accounts/:id/models
curl -s "${BASE}/api/v1/admin/accounts/1/models" -H "x-api-key: ${KEY}"

1.7 刷新 OAuth Token

POST /api/v1/admin/accounts/:id/refresh
curl -X POST "${BASE}/api/v1/admin/accounts/1/refresh" -H "x-api-key: ${KEY}"

1.8 刷新账号等级

POST /api/v1/admin/accounts/:id/refresh-tier
curl -X POST "${BASE}/api/v1/admin/accounts/1/refresh-tier" -H "x-api-key: ${KEY}"

1.9 获取账号统计

GET /api/v1/admin/accounts/:id/stats
curl -s "${BASE}/api/v1/admin/accounts/1/stats" -H "x-api-key: ${KEY}"

1.10 获取账号用量

GET /api/v1/admin/accounts/:id/usage
curl -s "${BASE}/api/v1/admin/accounts/1/usage" -H "x-api-key: ${KEY}"

1.11 更新单个账号

PUT /api/v1/admin/accounts/:id

请求体JSON所有字段均为可选仅传需要更新的字段

字段 类型 说明
name string 账号名称
notes *string 备注
type string 类型:oauth / setup-token / apikey / upstream
credentials object 凭证信息
extra object 额外配置
proxy_id *int64 代理 ID
concurrency *int 并发数
priority *int 优先级(默认 50
rate_multiplier *float64 速率倍数
status string 状态:active / inactive
group_ids *[]int64 分组 ID 列表
expires_at *int64 过期时间戳
auto_pause_on_expired *bool 过期后自动暂停

使用指针类型(*)的字段可以区分"未提供"和"设置为零值"。

# 示例:更新账号优先级为 100
curl -X PUT "${BASE}/api/v1/admin/accounts/1" \
  -H "x-api-key: ${KEY}" \
  -H "Content-Type: application/json" \
  -d '{"priority": 100}'

1.12 批量更新账号

POST /api/v1/admin/accounts/bulk-update

请求体JSON

字段 类型 必填 说明
account_ids []int64 要更新的账号 ID 列表
priority *int 优先级
concurrency *int 并发数
rate_multiplier *float64 速率倍数
status string 状态:active / inactive / error
schedulable *bool 是否可调度
group_ids *[]int64 分组 ID 列表
proxy_id *int64 代理 ID
credentials object 凭证信息(批量覆盖)
extra object 额外配置(批量覆盖)
# 示例:批量设置多个账号优先级为 100
curl -X POST "${BASE}/api/v1/admin/accounts/bulk-update" \
  -H "x-api-key: ${KEY}" \
  -H "Content-Type: application/json" \
  -d '{"account_ids": [1, 2, 3], "priority": 100}'

1.13 批量测试账号(脚本)

批量测试指定平台所有账号的指定模型连通性:

# 用户需提供BASE环境地址、KEYadmin token、MODEL测试模型
ACCOUNT_IDS=$(curl -s "${BASE}/api/v1/admin/accounts?platform=antigravity&page=1&page_size=100" \
  -H "x-api-key: ${KEY}" | python3 -c "
import json, sys
data = json.load(sys.stdin)
for item in data['data']['items']:
    print(f\"{item['id']}|{item['name']}\")
")

while IFS='|' read -r ID NAME; do
    echo "测试账号 ID=${ID} (${NAME})..."
    RESPONSE=$(curl -s --max-time 60 -N \
      -X POST "${BASE}/api/v1/admin/accounts/${ID}/test" \
      -H "x-api-key: ${KEY}" \
      -H "Content-Type: application/json" \
      -d "{\"model_id\": \"${MODEL}\"}" 2>&1)
    if echo "$RESPONSE" | grep -q '"success":true'; then
        echo "  ✅ 成功"
    elif echo "$RESPONSE" | grep -q '"type":"content"'; then
        echo "  ✅ 成功(有内容响应)"
    else
        ERROR_MSG=$(echo "$RESPONSE" | grep -o '"error":"[^"]*"' | tail -1)
        echo "  ❌ 失败: ${ERROR_MSG}"
    fi
done <<< "$ACCOUNT_IDS"

2. 运维监控

2.1 并发统计

GET /api/v1/admin/ops/concurrency
curl -s "${BASE}/api/v1/admin/ops/concurrency" -H "x-api-key: ${KEY}"

2.2 账号可用性

GET /api/v1/admin/ops/account-availability
curl -s "${BASE}/api/v1/admin/ops/account-availability" -H "x-api-key: ${KEY}"

2.3 实时流量摘要

GET /api/v1/admin/ops/realtime-traffic
curl -s "${BASE}/api/v1/admin/ops/realtime-traffic" -H "x-api-key: ${KEY}"

2.4 请求错误列表

GET /api/v1/admin/ops/request-errors

查询参数pagepage_size

curl -s "${BASE}/api/v1/admin/ops/request-errors?page=1&page_size=50" \
  -H "x-api-key: ${KEY}"

2.5 上游错误列表

GET /api/v1/admin/ops/upstream-errors
curl -s "${BASE}/api/v1/admin/ops/upstream-errors?page=1&page_size=50" \
  -H "x-api-key: ${KEY}"

2.6 仪表板概览

GET /api/v1/admin/ops/dashboard/overview
curl -s "${BASE}/api/v1/admin/ops/dashboard/overview" -H "x-api-key: ${KEY}"

3. 系统设置

3.1 获取系统设置

GET /api/v1/admin/settings
curl -s "${BASE}/api/v1/admin/settings" -H "x-api-key: ${KEY}"

3.2 更新系统设置

PUT /api/v1/admin/settings
curl -X PUT "${BASE}/api/v1/admin/settings" \
  -H "x-api-key: ${KEY}" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

3.3 Admin API Key 状态(脱敏)

GET /api/v1/admin/settings/admin-api-key
curl -s "${BASE}/api/v1/admin/settings/admin-api-key" -H "x-api-key: ${KEY}"

4. 用户管理

4.1 用户列表

GET /api/v1/admin/users
curl -s "${BASE}/api/v1/admin/users?page=1&page_size=20" -H "x-api-key: ${KEY}"

4.2 用户详情

GET /api/v1/admin/users/:id
curl -s "${BASE}/api/v1/admin/users/1" -H "x-api-key: ${KEY}"

4.3 更新用户余额

POST /api/v1/admin/users/:id/balance
curl -X POST "${BASE}/api/v1/admin/users/1/balance" \
  -H "x-api-key: ${KEY}" \
  -H "Content-Type: application/json" \
  -d '{"amount": 100, "reason": "充值"}'

5. 分组管理

5.1 分组列表

GET /api/v1/admin/groups
curl -s "${BASE}/api/v1/admin/groups" -H "x-api-key: ${KEY}"

5.2 所有分组(不分页)

GET /api/v1/admin/groups/all
curl -s "${BASE}/api/v1/admin/groups/all" -H "x-api-key: ${KEY}"

注意事项

  1. 前端必须打包进镜像:使用 docker build 在构建服务器(us-asaki-root上构建Dockerfile 会自动编译前端并 embed 到后端二进制中,构建完成后通过 docker save | docker load 传输到生产服务器(clicodeplus

  2. 镜像标签docker-compose.yml 使用 weishaw/sub2api:latest,本地构建后需要 docker tag 覆盖

  3. Windows 换行符问题:已通过 .gitattributes 解决,确保 *.sql 文件始终使用 LF

  4. 版本号管理:每次发布必须更新 backend/cmd/server/VERSION 并打标签

  5. 合并冲突:合并上游新版本时,重点关注以下文件可能的冲突:

    • backend/internal/service/antigravity_gateway_service.go
    • backend/internal/service/gateway_service.go
    • backend/internal/pkg/antigravity/request_transformer.go

Go 代码规范

1. 函数设计

单一职责原则

  • 函数行数:单个函数常规不应超过 30 行,超过时应拆分为子函数。若某段逻辑确实不可拆分(如复杂的状态机、协议解析等),可以例外,但需添加注释说明原因
  • 嵌套层级:避免超过 3 层嵌套,使用 early return 减少嵌套
// ❌ 不推荐:深层嵌套
func process(data []Item) {
    for _, item := range data {
        if item.Valid {
            if item.Type == "A" {
                if item.Status == "active" {
                    // 业务逻辑...
                }
            }
        }
    }
}

// ✅ 推荐early return
func process(data []Item) {
    for _, item := range data {
        if !item.Valid {
            continue
        }
        if item.Type != "A" {
            continue
        }
        if item.Status != "active" {
            continue
        }
        // 业务逻辑...
    }
}

复杂逻辑提取

将复杂的条件判断或处理逻辑提取为独立函数:

// ❌ 不推荐:内联复杂逻辑
if resp.StatusCode == 429 || resp.StatusCode == 503 {
    // 80+ 行处理逻辑...
}

// ✅ 推荐:提取为独立函数
result := handleRateLimitResponse(resp, params)
switch result.action {
case actionRetry:
    continue
case actionBreak:
    return result.resp, nil
}

2. 重复代码消除

配置获取模式

将重复的配置获取逻辑提取为方法:

// ❌ 不推荐:重复代码
logBody := s.settingService != nil && s.settingService.cfg != nil && s.settingService.cfg.Gateway.LogUpstreamErrorBody
maxBytes := 2048
if s.settingService != nil && s.settingService.cfg != nil && s.settingService.cfg.Gateway.LogUpstreamErrorBodyMaxBytes > 0 {
    maxBytes = s.settingService.cfg.Gateway.LogUpstreamErrorBodyMaxBytes
}

// ✅ 推荐:提取为方法
func (s *Service) getLogConfig() (logBody bool, maxBytes int) {
    maxBytes = 2048
    if s.settingService == nil || s.settingService.cfg == nil {
        return false, maxBytes
    }
    cfg := s.settingService.cfg.Gateway
    if cfg.LogUpstreamErrorBodyMaxBytes > 0 {
        maxBytes = cfg.LogUpstreamErrorBodyMaxBytes
    }
    return cfg.LogUpstreamErrorBody, maxBytes
}

3. 常量管理

避免魔法数字

所有硬编码的数值都应定义为常量:

// ❌ 不推荐
if retryDelay >= 10*time.Second {
    resetAt := time.Now().Add(30 * time.Second)
}

// ✅ 推荐
const (
    rateLimitThreshold       = 10 * time.Second
    defaultRateLimitDuration = 30 * time.Second
)

if retryDelay >= rateLimitThreshold {
    resetAt := time.Now().Add(defaultRateLimitDuration)
}

注释引用常量名

在注释中引用常量名而非硬编码值:

// ❌ 不推荐
// < 10s: 等待后重试

// ✅ 推荐
// < rateLimitThreshold: 等待后重试

4. 错误处理

使用结构化日志

优先使用 slog 进行结构化日志记录:

// ❌ 不推荐
log.Printf("%s status=%d model_rate_limit_failed model=%s error=%v", prefix, statusCode, modelName, err)

// ✅ 推荐
slog.Error("failed to set model rate limit",
    "prefix", prefix,
    "status_code", statusCode,
    "model", modelName,
    "error", err,
)

5. 测试规范

Mock 函数签名同步

修改函数签名时,必须同步更新所有测试中的 mock 函数:

// 如果修改了 handleError 签名
handleError func(..., groupID int64, sessionHash string) *Result

// 必须同步更新测试中的 mock
handleError: func(..., groupID int64, sessionHash string) *Result {
    return nil
},

测试构建标签

统一使用测试构建标签:

//go:build unit

package service

6. 时间格式解析

使用标准库

优先使用 time.ParseDuration,支持所有 Go duration 格式:

// ❌ 不推荐:手动限制格式
if !strings.HasSuffix(delay, "s") || strings.Contains(delay, "m") {
    continue
}

// ✅ 推荐:使用标准库
dur, err := time.ParseDuration(delay) // 支持 "0.5s", "4m50s", "1h30m" 等

7. 接口设计

接口隔离原则

定义最小化接口,只包含必需的方法:

// ❌ 不推荐:使用过于宽泛的接口
type AccountRepository interface {
    // 20+ 个方法...
}

// ✅ 推荐:定义最小化接口
type ModelRateLimiter interface {
    SetModelRateLimit(ctx context.Context, id int64, modelKey string, resetAt time.Time) error
}

8. 并发安全

共享数据保护

访问可能被并发修改的数据时,确保线程安全:

// 如果 Account.Extra 可能被并发修改
// 需要使用互斥锁或原子操作保护读取
func (a *Account) GetRateLimitRemainingTime(model string) time.Duration {
    a.mu.RLock()
    defer a.mu.RUnlock()
    // 读取 Extra 字段...
}

9. 命名规范

一致的命名风格

  • 常量使用 camelCaserateLimitThreshold
  • 类型使用 PascalCaseAntigravityQuotaScope
  • 同一概念使用统一命名:ThresholdLimit,不要混用
// ❌ 不推荐:命名不一致
antigravitySmartRetryMinWait    // 使用 Min
antigravityRateLimitThreshold   // 使用 Threshold

// ✅ 推荐:统一风格
antigravityMinRetryWait
antigravityRateLimitThreshold

10. 代码审查清单

在提交代码前,检查以下项目:

  • 函数是否超过 30 行?(不可拆分的逻辑除外,需注释说明)
  • 嵌套是否超过 3 层?
  • 是否有重复代码可以提取?
  • 是否使用了魔法数字?
  • Mock 函数签名是否与实际函数一致?
  • 测试是否覆盖了新增逻辑?
  • 日志是否包含足够的上下文信息?
  • 是否考虑了并发安全?

CI 检查与发布门禁

GitHub Actions 检查项

本项目有 4 个 CI 任务,任何代码推送或发布前都必须全部通过

Workflow Job 说明 本地验证命令
CI test 单元测试 + 集成测试 cd backend && make test-unit && make test-integration
CI golangci-lint Go 代码静态检查golangci-lint v2.7 cd backend && golangci-lint run --timeout=5m
Security Scan backend-security govulncheck + gosec 安全扫描 cd backend && govulncheck ./... && gosec -severity high -confidence high ./...
Security Scan frontend-security pnpm audit 前端依赖安全检查 cd frontend && pnpm audit --prod --audit-level=high

向上游提交 PR

PR 目标是上游官方仓库,只包含通用功能改动bug fix、新功能、性能优化等

以下文件禁止出现在 PR 中(属于我们 fork 的定制化内容):

  • CLAUDE.mdAGENTS.md — 我们的开发文档
  • backend/cmd/server/VERSION — 我们的版本号文件
  • UI 定制改动GitHub 链接移除、微信客服按钮、首页定制等)
  • 部署配置(deploy/ 目录下的定制修改)

PR 流程

  1. develop 创建功能分支,只包含要提交给上游的改动
  2. 推送分支后,等待 4 个 CI job 全部通过
  3. 确认通过后再创建 PR
  4. 使用 gh run list --repo touwaeriol/sub2api --branch <branch> 检查状态

自有分支推送develop / main

推送到我们自己的 developmain 分支时,包含所有改动(定制化 + 通用功能)。

推送前必须在本地执行全部 CI 检查(不要等 GitHub Actions

# 确保 Go 工具链可用macOS homebrew
export PATH="/opt/homebrew/bin:$HOME/go/bin:$PATH"

# 1. 单元测试(必须)
cd backend && make test-unit

# 2. 集成测试(推荐,需要 Docker
make test-integration

# 3. golangci-lint 静态检查(必须)
golangci-lint run --timeout=5m

# 4. gofmt 格式检查(必须)
gofmt -l ./...
# 如果有输出,运行 gofmt -w <file> 修复

推送后确认

  1. 使用 gh run list --repo touwaeriol/sub2api --branch <branch> 检查 GitHub Actions 状态
  2. 确认 CI 和 Security Scan 两个 workflow 的 4 个 job 全部绿色
  3. 任何 job 失败必须立即修复,禁止在 CI 未通过的状态下继续后续操作

发布版本

  1. 本地执行上述全部 CI 检查通过
  2. 递增 backend/cmd/server/VERSION,提交并推送
  3. 推送后确认 GitHub Actions 的 4 个 CI job 全部通过
  4. CI 未通过时禁止部署 — 必须先修复问题
  5. 使用 gh run list --repo touwaeriol/sub2api --limit 10 确认状态

常见 CI 失败原因及修复

  • gofmtstruct 字段对齐不一致 → 运行 gofmt -w <file> 修复
  • golangci-lint:未使用的变量/导入 → 删除或使用 _ 忽略
  • test 失败mock 函数签名不一致 → 同步更新 mock
  • gosec:安全漏洞 → 根据提示修复或添加例外

PR 描述格式规范

所有 PR 描述使用中英文同步(先中文、后英文),包含以下三个部分:

模板

## 背景 / Background

<一两句说明问题现状或触发原因>

<English version of the background>

---

## 目的 / Purpose

<本次改动要解决的问题或达到的目标>

<English version of the purpose>

---

## 改动内容 / Changes

### 后端 / Backend

- **改动点 1**:说明
- **改动点 2**:说明

---

- **Change 1**: description
- **Change 2**: description

### 前端 / Frontend

- **改动点 1**:说明
- **改动点 2**:说明

---

- **Change 1**: description
- **Change 2**: description

---

## 截图 / Screenshot可选

ASCII 示意图或实际截图

规范要点

  • 标题:使用 conventional commits 格式,如 feat(scope): description
  • 中英文顺序:同一段落先中文后英文,用空行分隔,不用 --- 分割同段内容
  • 改动分类:按 Backend / Frontend / Config 等模块分组,先列中文要点再列英文要点
  • 截图/示意图:有 UI 变动时必须附上,可用 ASCII 示意布局
  • 目标分支:提交到 touwaeriol/sub2apimain 分支