Skip to content

本地运行时统一入口

这篇文档定义 ChatTea 在只有一个对外域名或一个对外端口时,如何用内部入口层把 Git、Pages、备份控制面和后续 Bot 管理面整合到同一个 URI 空间。它记录的是可复用的运行时设计和当前机器的脱敏实践,不包含令牌、密码、证书私钥或真实凭据内容。

目标

生产环境有时只能暴露一个公网端口,或者只希望用户记住一个入口。在这种约束下,ChatTea 仍然把内部服务拆清楚,但在入口层统一路由:

https://<public-host>/git/      -> Git service
https://<public-host>/pages/    -> Pages service
https://<public-host>/control/  -> backup/control service
https://<public-host>/-/status  -> runtime aggregate status

内部可以有多个 loopback 端口,外部只需要一个 host/port。系统 nginx 或公网网关只做一层很薄的转发,真正的服务整合由 ChatTea 内部 entry 完成。

组件边界

system nginx / public gateway
  listen: 80/443 或生产环境允许暴露的唯一端口
  role: TLS、公网入口、转发到 ChatTea entry

chattea-entry.service
  listen: 127.0.0.1:<entry-port>
  role: 内部 nginx,统一 /git /pages /control /-/status

chattea-gitea.service
  listen: 127.0.0.1:3000
  role: Gitea Git service、API、Actions 调度、runner registry

chattea-pages.service
  listen: 127.0.0.1:3001
  role: 静态 Pages service,serve 已发布站点

chattea-control.service
  listen: 127.0.0.1:3002
  role: backup/control/status,本地机器侧管理能力

chattea-runner@<name>.service
  role: Actions worker,不是对外 Web 服务

Runner 不需要独立公网端口。它通过 Gitea 获取 job、在本机执行 build/publish,再把日志和状态回写到 Gitea。Runner 状态应在 /control//-/status 里展示,而不是单独暴露成一个网站。

ChatUp、ChatTea、ChatData 的关系

这套实践至少涉及两个用户可见包:

  • ChatUp:适合做安装、bootstrap、system/user service 编排和机器初始化入口。
  • ChatTea:负责 Gitea、Pages、runner、backup/control 和 entry 的运行时语义。

如果 MySQL 由 ChatUp 安装并托管,用户不一定需要直接感知 ChatData。但实现上可以继续复用 ChatData 的 MySQL runtime 能力:

user command: ChatUp bootstrap/install
  -> prepares MySQL runtime, if requested
  -> installs or starts ChatTea runtime stack
  -> ChatTea consumes the resulting DB/socket/config

因此文档和 CLI 可以把 MySQL 描述为 ChatTea stack 的一个 backend,而不是要求用户单独操作 ChatData。ChatData 是否参与是实现细节;用户视角是“ChatUp 起环境,ChatTea 管 Git/Pages/control runtime”。

URI 路由表

第一版 path-based 路由:

URI Upstream 说明
/ entry landing 显示 Git、Pages、Control 入口
/-/health entry local entry 自身健康检查
/-/status control 汇总 Git、Pages、Control、Runner 状态
/git/ 127.0.0.1:3000 Gitea Web/API/Git HTTP
/pages/ 127.0.0.1:3001 Pages service root
/pages/<owner>/<repo>/ 127.0.0.1:3001/<owner>/<repo>/ 已发布静态站点
/control/ 127.0.0.1:3002 backup/control API
/control/backups 127.0.0.1:3002/control/backups 备份列表和受控创建入口
/bot/ future upstream 后续 Bot webhook/control surface

如果后续需要非可信多租户 Pages,仍建议使用不同 host 隔离 origin,例如 git.<domain>pages.<domain>。单 host path mode 更适合内网可信环境或生产入口受限场景。

内部 entry nginx

内部 entry 是 user-level nginx,不直接监听 80/443。示例监听端口为 <entry-port>,实际部署应选择一个空闲 loopback 端口:

server {
    listen 127.0.0.1:<entry-port>;
    server_name _;

    location = /-/health {
        default_type text/plain;
        return 200 "ok\n";
    }

    location = /-/status {
        proxy_pass http://127.0.0.1:3002/control/status;
    }

    location = /git {
        return 308 /git/;
    }

    location /git/ {
        proxy_pass http://127.0.0.1:3000/;
        proxy_set_header X-Forwarded-Prefix /git;
        proxy_set_header X-Forwarded-Host $host;
        proxy_redirect / /git/;
        proxy_cookie_path / /git/;
    }

    location = /pages {
        return 308 /pages/;
    }

    location /pages/ {
        proxy_pass http://127.0.0.1:3001/;
    }

    location = /control {
        return 308 /control/;
    }

    location /control/ {
        proxy_pass http://127.0.0.1:3002/control/;
    }
}

Gitea 原生子路径部署最好让 Gitea 自己的 ROOT_URL 带上 /git/。如果当前机器还要保留已有 direct Gitea vhost,可以先用 entry nginx 的 proxy_redirectproxy_cookie_path 和 HTML asset rewrite 作为实践兼容层;正式产品化时再决定是否切换 Gitea 的 canonical root。

外部 nginx

系统 nginx 或公网网关只需要把一个 host 转到 entry:

server {
    listen 443 ssl;
    server_name <public-host>;

    location / {
        proxy_pass http://127.0.0.1:<entry-port>;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
    }
}

这样外部仍然只暴露一个域名和一个端口,内部可以继续演进 Git、Pages、Control、Bot 等服务。

Control service 最小能力

chattea-control.service 第一版只做安全的本地管理入口:

GET  /control/health   -> control health
GET  /control/status   -> service + HTTP aggregate status
GET  /control/backups  -> backup manifest list
POST /control/backups  -> dry-run backup manifest, guarded by confirmation header

完整备份仍然需要机器侧能力,不能只靠 Gitea Web API 完成。Control service 只是把本地 backend 包装成可管理任务:

control API
  -> local backup job
  -> gitea dump / database dump / file archive / checksums
  -> <chattea-home>/backups/<backup-id>/manifest.json

第一版不要开放无确认的 restore API。恢复流程应保留 CLI 或本地显式确认门槛。

Backup CLI 分层

备份相关命令要区分“本机 backend”和“服务化 control API”,避免把 Gitea 官方 dump、ChatTea 本地备份和远程管理入口混在一起:

chattea backup local create
  -> 在当前机器执行
  -> 可调用 gitea dump、数据库 dump、文件归档、checksum
  -> 适合 cron、SSH、维护窗口、restore 前置检查

chattea backup service create --base-url <entry-url>
  -> 通过 /control/backups 发起任务
  -> 适合从外部控制台、Bot 或 Web UI 管理
  -> 实际重活仍由服务器本地 job 执行

chattea backup gitea dump
  -> 显式暴露 Gitea 官方 dump 能力
  -> 作为底层原语或诊断命令,不代表完整 ChatTea runtime 备份

服务化备份走网络,因此第一版必须有确认头、身份校验和只读查询默认值。本机 backup 可以执行更强的机器侧操作,但需要本地权限和维护窗口。两者共享同一份 manifest schema,方便 UI、Bot 和 CLI 展示一致的备份状态。

当前机器实践结果

当前机器已经完成最小实践:

chattea-gitea.service    active, 127.0.0.1:3000
chattea-pages.service    active, 127.0.0.1:3001
chattea-control.service  active, 127.0.0.1:3002
chattea-entry.service    active, 127.0.0.1:<entry-port>

当前机器的 3080 已被其他服务占用,因此 entry 选择了另一个空闲 loopback 端口。系统 nginx 把集成入口 host 转发到 entry 后,以下路径均已验证:

/                  -> ChatTea Runtime Entry landing
/-/health          -> 200 ok
/-/status          -> Git / Pages / Control / Runner aggregate JSON
/control/          -> control service routes
/control/backups   -> backup manifest list
/pages/<owner>/<repo>/ -> 已发布 ChatTea 文档站点
/git/              -> Gitea Web UI through entry
/git/<owner>/<repo>.git/info/refs?service=git-upload-pack -> Git smart HTTP

并创建过一个 dry-run backup manifest,用来证明 control plane 可以通过统一入口创建受控备份记录。它不是完整实例备份,只是后续 real backup job 的接口占位和权限检查。

验证清单

systemctl --user is-active \
  chattea-gitea.service \
  chattea-pages.service \
  chattea-control.service \
  chattea-entry.service

curl -I http://127.0.0.1:<entry-port>/-/health
curl http://127.0.0.1:<entry-port>/-/status
curl http://127.0.0.1:<entry-port>/control/backups
curl -I http://127.0.0.1:<entry-port>/pages/<owner>/<repo>/
curl -I 'http://127.0.0.1:<entry-port>/git/<owner>/<repo>.git/info/refs?service=git-upload-pack'

sudo nginx -t

如果外部 host 已经转发到 entry,再验证:

curl -I https://<public-host>/-/health
curl https://<public-host>/-/status
curl -I https://<public-host>/pages/<owner>/<repo>/
curl -I 'https://<public-host>/git/<owner>/<repo>.git/info/refs?service=git-upload-pack'

后续产品化

  • 将 control service 和 entry nginx 配置生成纳入 chattea CLI。
  • 由 ChatUp 提供更高层的 bootstrap 命令,按需安装 MySQL backend 并拉起 ChatTea stack。
  • 把 backup dry-run 替换为真实 gitea dump、DB dump 和文件归档 job。
  • 为 restore 增加 maintenance mode、完整性校验和本地确认门槛。
  • 决定 Gitea 是否正式切到 /git/ canonical root,或继续保留 direct host 兼容入口。
  • 规划 /bot/ 的 webhook/control surface,但不要让它影响 Git/Pages/Control 的第一版验收。