docs: add maintainer guidance and API contract
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# 认证与安全
|
||||
|
||||
## 初始化与管理员
|
||||
|
||||
新数据库始终创建账号表中的默认 Codex 账号,但只有 `settings.initialized` 存在才视为完成安装。`POST /api/v1/setup` 在事务中创建唯一 `admin(id=1)`、通用设置和安装标记;用户名至少 3 位、密码至少 10 位。首次初始化没有额外安装令牌,因此初始化完成前不得把实例直接暴露到不可信网络。
|
||||
|
||||
管理员密码使用 argon2id(3 次、64 MiB、2 lanes、32 字节结果和随机 salt)保存。当前没有改密或找回接口;不要通过新增旁路直接写入明文或弱摘要。
|
||||
|
||||
## Session 与请求来源
|
||||
|
||||
登录成功生成 32 字节随机 token,客户端得到七天 `HttpOnly`、`SameSite=Strict` cookie,SQLite 只保存 SHA-256 摘要。每次受保护请求回查未过期 session。登出删除当前摘要并清 cookie。
|
||||
|
||||
所有非 `GET`/`HEAD` 受保护请求还必须携带 `X-Requested-With: codex-helper`。这是当前同源部署下的额外 CSRF 门禁,不替代 session 校验,也不意味着可以放宽 CSP 或 cookie 策略。`Secure` 当前为 false,以支持 README 中直接 HTTP 部署;公网必须由 HTTPS 反向代理保护,调整此兼容行为时同步评估代理终止 TLS 的方式。
|
||||
|
||||
登录失败限流仅存在于单进程内,以 `RemoteAddr` 为 key,每 15 分钟最多 10 次。修改反向代理或客户端 IP 处理时,不能未经可信代理白名单就相信任意转发头。
|
||||
|
||||
## 密钥与外部凭据
|
||||
|
||||
`security.OpenVault` 首次启动创建权限 `0600` 的 `/data/secret.key`。SMTP 密码和 Telegram Bot Token 使用该 32 字节密钥经 AES-GCM 加密,密文写入 SQLite;GET 和保存响应不得返回秘密明文,空密码/Token 表示保留旧值。
|
||||
|
||||
Codex OAuth 凭据由 app-server 写入各账号隔离的 `CODEX_HOME`,不经过浏览器或 SQLite。`secret.key`、数据库、Codex 目录和日志都可能包含敏感运行信息,不得提交 Git、加入镜像层或复制到前端。
|
||||
|
||||
## HTTP 边界
|
||||
|
||||
JSON 解码限制为 1 MiB 并拒绝未知字段。统一安全头包括限制性 CSP、`nosniff`、禁止 iframe 和 same-origin referrer。前端路由、按钮禁用和邮箱掩码均不是服务端授权边界;所有新敏感端点必须在后端经过 `require`,改变 API 方法时还要核对来源头逻辑。
|
||||
@@ -0,0 +1,35 @@
|
||||
# Codex 集成与用量同步
|
||||
|
||||
## 账号隔离与进程生命周期
|
||||
|
||||
每个 `accounts` 记录对应一个 `accountRuntime` 和独立 `codex app-server` 子进程。账号 1 为升级兼容固定使用 `/data/codex`;其他账号使用 `/data/accounts/<id>/codex`。该目录通过子进程 `CODEX_HOME` 注入,保存并刷新 ChatGPT 登录凭据。
|
||||
|
||||
`ensureReady` 用 lifecycle mutex 串行冷启动:创建目录、启动子进程、在 20 秒内调用 `initialize`,成功后才标记 ready。仅子进程存活不代表协议已初始化;失败进程必须关闭后重试。`syncing` mutex 串行同账号同步并保护 Dashboard,`stateMu` 保护 ready/stopped。删除账号先从应用 map 移除并停止进程,再删除数据库和精确凭据目录。
|
||||
|
||||
## JSONL 协议与登录
|
||||
|
||||
客户端以 stdio 启动 `codex app-server`,每行一个 JSON 消息。递增 ID 将响应关联到 buffered channel;请求受调用 context 或 20 秒 timeout 控制。旧进程晚退出时不能把替代进程标记为断开。
|
||||
|
||||
设备码登录调用:
|
||||
|
||||
```text
|
||||
account/login/start {type:"chatgptDeviceCode"}
|
||||
```
|
||||
|
||||
前端展示 `verificationUrl` 与 `userCode`。收到 `account/login/completed`、`account/updated` 或 `account/rateLimits/updated` 后异步重拉数据;登录完成最多以 0、1、3 秒退避等待套餐分类可用。官方流程和字段见 [Codex App Server 文档](https://learn.chatgpt.com/docs/app-server)。Dockerfile 固定的 CLI 版本及项目测试仍是实际兼容基线。
|
||||
|
||||
## 同步数据流
|
||||
|
||||
一次同步依次读取 `account/read`、`account/rateLimits/read` 和 `account/usage/read`:
|
||||
|
||||
- `account/read` 决定连接、邮箱、认证模式和套餐;读取失败会使整次同步失败。
|
||||
- 限额读取兼容 `rateLimitsByLimitId` 多 bucket 和旧 `rateLimits` 单 bucket,再把 primary/secondary 展平。失败时保留空限额而不令整次同步失败。
|
||||
- 套餐未知时,可从所有可分类且一致的限额 bucket 回填;冲突或未知时必须保持 unknown。
|
||||
- 用量读取保存 summary 和每日 Token bucket;接口失败时使用空摘要和空历史,不令整次同步失败。
|
||||
- 每次限额同步写入快照、检测用量百分比显著回落,并更新账号元数据及内存 Dashboard。
|
||||
|
||||
`account/usage/read` 的 optional 指标和 daily buckets 可能暂未提供。仅 API Key 或 Bedrock 登录不能保证读取 ChatGPT 用量;不得在缺失数据时合成调用次数、输入/输出 Token、价格或账单日期。
|
||||
|
||||
## 套餐类型
|
||||
|
||||
用户期望类型仅为连接后的校验提示:`any`、`personal`、`team`。当前 personal 包括 free/go/plus/pro/prolite;team 包括 team/business 及两种 self-serve business 标识。未知新套餐必须显示 unknown,不能静默当作某一类型。相同邮箱和相同已知实际类型的多个账号标记 `possibleDuplicate`,但不会自动合并或删除。
|
||||
@@ -0,0 +1,32 @@
|
||||
# 数据、通知与备份
|
||||
|
||||
## SQLite 与迁移
|
||||
|
||||
数据库位于 `${DATA_DIR:-/data}/codex-helper.db`,启用 WAL、5 秒 busy timeout 和 foreign keys。`backend/internal/store/store.go` 是 schema 与启动迁移的事实来源:
|
||||
|
||||
- `settings` 保存通用、SMTP、Telegram、绑定码及安装标记;秘密单独以密文 key 保存。
|
||||
- `admin` 与 `sessions` 保存唯一管理员和登录会话。
|
||||
- `accounts` 保存 Codex 连接元数据与期望套餐类型。
|
||||
- `daily_usage` 和 `limit_snapshots` 按 `account_id` 保存历史,删除账号时级联删除。
|
||||
- `notifications` 保存稳定去重键、调度时间、结构化消息、状态、次数和错误。
|
||||
- `telegram_updates` 保存 Bot API offset。
|
||||
|
||||
启动迁移必须幂等并兼容早期单账号库:创建默认账号 1,把旧用量和限额数据迁入该账号,补 `expected_kind` 和通知 body。schema 变化应增加覆盖旧结构且保留已有行的测试。
|
||||
|
||||
## 清理与备份
|
||||
|
||||
每分钟调度器根据 `retentionDays` 清理旧限额、通知和每日用量;允许范围为 30–365 天。`maintenance/backup` 使用 SQLite `VACUUM INTO` 创建独立一致性快照,包含已提交 WAL 数据且不中断写入。
|
||||
|
||||
数据库快照不包含 `/data/secret.key`、`/data/codex` 或 `/data/accounts/*/codex`,因此不能单独恢复通知凭据和 Codex 登录。完整灾难恢复必须停止容器并备份、恢复整个 `/data`,同时保持 UID `10001` 可读。不得把 `docker compose down -v` 写成普通升级步骤。
|
||||
|
||||
## 提醒生成与重试
|
||||
|
||||
限额快照按 `(account_id, limit_id, window_type)` 比较。计划提醒的 key 还包含 `resets_at` 和 `before|after`;异常提前重置以旧快照 ID 生成 `detected_after` key。百分比回落必须超过 `0.01`,旧快照不得超过六小时。
|
||||
|
||||
处理器每分钟为当前 Dashboard 生成到期提醒,并只发送 `scheduled_at` 后六小时内的未发送记录。Telegram 与 SMTP 中任何启用渠道失败都会把记录标记为 failed,后续周期在窗口内重试;全部启用渠道成功才标记 sent。稳定 key 和 `INSERT OR IGNORE` 是防重复边界。
|
||||
|
||||
## Telegram 与 SMTP
|
||||
|
||||
Telegram 保存加密 Token、Chat ID、启用开关和菜单开关。保存 Token 前调用 `getMe`;long polling timeout 为 25 秒,HTTP client timeout 为 35 秒。六位绑定码十分钟有效且一次成功后清除。只有绑定 Chat ID 且启用菜单时才处理查询命令。
|
||||
|
||||
SMTP 支持 `starttls`、隐式 `tls` 和 `none`,TLS 最低 1.2;支持可选 PLAIN AUTH,发送 multipart text/html。TCP 连接和后续 SMTP/TLS 读写共享 35 秒 deadline。修改外部调用时必须保留这一超时边界、TLS server name、HTML 转义和不记录秘密的错误处理。
|
||||
@@ -0,0 +1,26 @@
|
||||
# 后端运行与 API
|
||||
|
||||
## 启动与关闭
|
||||
|
||||
入口为 `backend/cmd/server/main.go`。普通启动调用 `app.New`:打开 `/data/codex-helper.db` 并执行兼容迁移、打开或创建 `/data/secret.key`、为数据库中的每个账号创建运行时对象,再组装 HTTP server。`Run` 启动 app-server 保活、每分钟调度器和 Telegram long polling;SIGINT/SIGTERM 触发五秒 HTTP 优雅关闭、停止所有账号进程并关闭数据库。
|
||||
|
||||
`codex-helper healthcheck` 将 `${LISTEN_ADDR:-:8080}` 的通配地址转换为 `127.0.0.1` 并请求 `/health/live`,供 Docker `HEALTHCHECK` 使用。
|
||||
|
||||
## 请求链路
|
||||
|
||||
- `internal/app/app.go`:应用生命周期、账号运行时、路由、静态前端和通用 HTTP 辅助函数。
|
||||
- `internal/app/api.go`:`/api/v1/` 分派、初始化、会话、账号和用量同步。
|
||||
- `internal/app/notify.go`:SMTP、Telegram、提醒生成和发送。
|
||||
- `internal/store/store.go`:SQLite schema、兼容迁移和数据方法。
|
||||
- `internal/codex/client.go`:与 `codex app-server` 的 JSONL 请求/响应关联。
|
||||
|
||||
所有路由由 `http.ServeMux` 承载。API 先处理三个匿名入口,再统一调用 `require`;前端资源从 Go `embed.FS` 提供,未知浏览器路径回退到 `index.html`。完整端点以 [`backend/CONTRACT.md`](../../backend/CONTRACT.md) 为准。
|
||||
|
||||
## 后台任务
|
||||
|
||||
- `keepCodex` 每秒检查未就绪的账号,串行完成进程启动与协议初始化,成功后立即同步。
|
||||
- `scheduler` 每分钟按 `syncMinutes` 的 Unix 时间取模触发全账号同步,清理过期历史,并异步处理提醒。
|
||||
- `telegramLoop` 使用 Bot API long polling;仅已配置 Token 才请求更新。
|
||||
- app-server 的登录、账号和限额通知会触发带短退避的同步;多次失败将内存 Dashboard 标为 stale 并记录 `lastError`。
|
||||
|
||||
这些 goroutine 都以应用 context 为退出边界。修改调度时必须避免无限阻塞、重复启动、停止后重启和同一账号并发同步。
|
||||
Reference in New Issue
Block a user