feat: ship blog platform admin and deploy stack

This commit is contained in:
2026-03-31 21:48:39 +08:00
parent a9a05aa105
commit 313f174fbc
210 changed files with 25476 additions and 5803 deletions

View File

@@ -0,0 +1,224 @@
# Docker / 反代架构说明
本文记录当前项目在 `tohka` 这类宿主机上的推荐部署结构,以及为什么会同时出现:
- 宿主机层的 **Caddy**
- `admin` 容器内的 **Nginx**
## 1. 总体分层
推荐生产结构:
```text
Internet
-> Host Caddy (:80 / :443)
-> frontend container (Astro SSR / Node :4321)
-> admin container (Nginx :80, 静态 SPA)
-> backend container (Loco.rs API :5150)
-> backend-worker container (Loco.rs worker / Redis queue)
```
职责划分:
- **Host Caddy**
- 统一接收公网流量
- 处理域名、HTTPS、证书续签
- 反向代理到各个内部容器
- **frontend**
- Astro SSR(Node) 应用
- 不是纯静态站,所以容器内直接运行 Node 服务即可
- **admin**
- React/Vite 打包后的纯静态 SPA
- 容器内使用 Nginx 提供静态文件
- 生产推荐前面接 TinyAuth / Pocket ID 做 SSO
- **backend**
- API、后台鉴权、审计、版本历史、订阅投递
- **backend-worker**
- 消费 Redis 队列
- 负责通知异步投递、失败重试、digest 任务触发后的实际发送
## 2. 为什么上层已经有 Caddyadmin 容器里还要 Nginx
这两层并不冲突,职责不同:
- **Caddy** 是入口网关
- **Nginx** 是 admin 容器内部的静态文件服务器
也就是说:
```text
Browser
-> Caddy
-> admin nginx
-> /usr/share/nginx/html
```
这样做的好处:
- admin 镜像本身自带可用的静态文件服务能力
- 宿主机层仍然保留统一的域名 / HTTPS / 路由管理
- admin 作为独立前端,可以单独构建、单独发布
## 2.2 为什么现在多了 `backend-worker`
因为当前通知系统已经改成:
```text
backend (web)
-> 写入 notification_deliveries
-> enqueue 到 Redis
backend-worker
-> 消费队列
-> 发送 email / webhook / discord / telegram / ntfy
```
如果只启动 `backend` 而没有 `backend-worker`,通知会入队但没人消费。
## 2.1 推荐的后台认证链路
当前最推荐:
```text
Browser
-> Caddy (import tinyauth)
-> TinyAuth
-> Pocket ID (OIDC)
-> admin nginx / backend API
```
关键点:
- admin 页面和它调用的 `/api/*` 建议走 **同一个受保护后台域名**
- Caddy 使用 `forward_auth`
- backend 开启 `TERMI_ADMIN_TRUST_PROXY_AUTH=true`
- 生产推荐再配 `TERMI_ADMIN_PROXY_SHARED_SECRET=<随机长字符串>`
- Caddy 在 `/api/*` 反代到 backend 时补 `X-Termi-Proxy-Secret`
- backend 读取 TinyAuth 转发的 `Remote-User / Remote-Email / Remote-Groups`
- 本地开发可以保留 `TERMI_ADMIN_LOCAL_LOGIN_ENABLED=true`
## 3. 为什么 frontend 不使用同样的 Nginx 模式?
因为当前 `frontend`**Astro SSR**
- `output: 'server'`
- `@astrojs/node` standalone
它需要在请求期执行服务端逻辑,因此更适合:
```text
Caddy -> frontend Node server
```
而不是先打成纯静态文件再由 Nginx 托管。
## 4. admin 容器内 Nginx 当前负责什么?
当前 `admin/nginx.conf` 主要负责:
- SPA fallback`try_files ... /index.html`
- `assets/` 长缓存hash 资源可 `immutable`
- `index.html` / `runtime-config.js` 禁缓存,避免配置或入口文件陈旧
- `gzip` 压缩
- 基础安全响应头
- `/healthz` 健康检查入口
### 为什么 `runtime-config.js` 要禁缓存?
因为 admin 现在支持运行时环境变量注入,例如:
- `ADMIN_API_BASE_URL`
- `ADMIN_FRONTEND_BASE_URL`
容器启动时会生成 `runtime-config.js`
如果它被强缓存,改完环境变量重启容器后,浏览器可能仍然读到旧地址。
## 5. 为什么没有在 admin 容器里启用 Brotli
当前基础镜像是官方 `nginx:alpine`
这个镜像默认不一定带 Brotli 模块,所以这里先启用通用的 `gzip`
如果后续确实需要 Brotli有两个常见做法
- 让宿主机层的 Caddy 统一负责压缩
- 改用带 Brotli 模块的自定义 Nginx 镜像
对当前项目而言,优先让 **宿主机 Caddy 做统一公网入口**admin 容器内部只负责稳妥地提供静态文件,是更简单的方案。
## 6. 推荐的 tohka 思路
如果 `tohka` 上已经有一个统一的大 Caddyfile推荐继续保持
- Caddy 统一暴露 `80/443`
- `frontend/admin/backend` 只走内网端口
- 不把数据库 / Redis 直接暴露到公网
- backend 如果启用了代理 SSO不要再把 `:5150` 直接开放给公网
仓库里已经额外提供:
- `deploy/docker/compose.tohka.override.yml`
用它叠加 `compose.package.yml` 后,会把:
- `frontend:4321`
- `admin:4322`
- `backend:5150`
都只绑定到 `127.0.0.1`
## 7. 当前和配置相关的关键文件
- 宿主机入口反代:`tohka` 上的大 Caddyfile
- admin 静态服务:`admin/nginx.conf`
- admin 镜像:`admin/Dockerfile`
- Caddy 参考模板:`deploy/caddy/Caddyfile.tohka.example`
- compose 示例:`deploy/docker/compose.package.yml`
- tohka override`deploy/docker/compose.tohka.override.yml`
- OIDC / Pocket ID 落地:`deploy/docker/TOHKA_POCKET_ID.md`
- 运行时环境示例:`deploy/docker/.env.example`
## 8. Caddy 路由推荐
默认更推荐:
- `blog.init.cool` -> frontend
- `admin.blog.init.cool` -> admin + backend(`/api/*`)
- `api.blog.init.cool` -> backend
这样最省心也最不容易碰到路径前缀、资源基路径、Cookie Path 等问题。
如果一定要用:
- `init.cool/admin`
- `init.cool/api`
也可以,但 `admin` 需要在构建时设置:
- `VITE_ADMIN_BASENAME=/admin`
对应模板见:
- `deploy/caddy/Caddyfile.tohka.example`
## 9. 备份 / 恢复入口
当前仓库内已经补了:
- `deploy/scripts/backup/backup-postgres.sh`
- `deploy/scripts/backup/backup-markdown.sh`
- `deploy/scripts/backup/backup-media.sh`
- `deploy/scripts/backup/restore-postgres.sh`
- `deploy/scripts/backup/restore-markdown.sh`
- `deploy/scripts/backup/restore-media.sh`
- `deploy/docker/BACKUP_AND_RECOVERY.md`
建议把这些接进生产上的 cron / systemd timer而不是只停留在仓库里。
## 10. 健康检查与启动顺序
当前推荐闭环:
- backend 启动时先自动跑 migration
- backend 提供 `/healthz`
- frontend 提供 `/healthz`
- admin 由 Nginx 提供 `/healthz`
- compose 中 frontend / admin / backend-worker 都依赖 backend healthy