Refactor app instance to Keycloak SSO

This commit is contained in:
2026-06-15 15:54:39 +08:00
parent fc9fd93c36
commit 461d1300ad
246 changed files with 1350 additions and 52721 deletions

198
README.md
View File

@ -1,183 +1,75 @@
# Beaver Project
# Beaver App Instance
`Beaver Project` 是一套单机 Docker 部署的多实例运行环境:
This branch contains a standalone Beaver app instance for an external orchestrator to deploy.
- 用户先进入独立的 `auth-portal` 完成注册或登录。
- 注册会触发 `authz-service` 调用 `deploy-control`
- `deploy-control` 在同一台机器上创建一个独立的 `app-instance` 容器。
- `router-proxy` 按实例域名把流量转发到对应容器。
The app instance is a single Docker image that runs:
当前推荐的最小部署方式是一台 Linux / WSL2 Ubuntu 机器加 Docker。生产域名和 HTTPS 可以放在项目外层的 Nginx、Caddy、Traefik 或云负载均衡上。
- Next.js frontend
- FastAPI backend
- Nginx reverse proxy
## 组件
Authentication is delegated to Keycloak. This repository does not maintain local user registration, passwords, per-user instance routing, or deployment orchestration.
| 目录 | 职责 | 默认端口 |
| --- | --- | --- |
| `auth-portal/` | 用户登录、注册、模型配置引导入口 | `3081` |
| `authz-service/` | AuthZ 服务,负责账号和 backend 身份编排 | `19090` |
| `deploy-control/` | 部署控制面,调用 Docker 创建和管理实例 | `8090` |
| `router-proxy/` | 统一实例入口代理,按 Host 分发到实例容器 | `8088` |
| `app-instance/` | 单用户运行实例,容器内包含前端、后端和 Nginx | 容器内 `8080` |
## Runtime Contract
公网环境通常只暴露:
The external orchestrator is responsible for:
- `auth-portal`: `3081`,或外层代理后的 `https://portal.example.com`
- `router-proxy`: `8088`,或外层代理后的 `https://<slug>.apps.example.com`
- Building or pulling the app image
- Providing a mounted Beaver home directory
- Providing `config.json`
- Choosing the public host and port
- Registering the public callback URL and post-logout callback URL in Keycloak
- Managing lifecycle, routing, TLS, and scaling
不要直接把 `deploy-control:8090``authz-service:19090` 暴露到公网。
## 请求链路
注册:
The app instance exposes container port `8080`:
```text
Browser
-> auth-portal
-> authz-service POST /portal/register
-> deploy-control POST /api/instances/register
-> app-instance/create-instance.sh
-> app-instance POST /api/auth/register
-> authz-service /oauth/register or /backends/register
-> auth-portal provider onboarding
-> deploy-control POST /api/instances/configure-provider
/ frontend
/api backend API
/ws backend WebSocket
```
登录:
## Keycloak
Default IdP settings:
```text
Browser
-> auth-portal
-> deploy-control POST /api/instances/resolve
-> app-instance POST /api/auth/login
-> app-instance frontend URL
issuer: https://keycloak.bwgdi.com/realms/beaver
client_id: beaver-agnet
```
## 快速开始
For the current plain-IP deployment, register these values in Keycloak:
本机完整流程见:
```text
web_origin: http://172.19.0.245:18080
redirect_uri: http://172.19.0.245:18080/auth/callback
post_logout_redirect_uri: http://172.19.0.245:18080/logout/callback
```
- [部署指南.md](./部署指南.md)
The app validates Keycloak JWT issuer, signature, expiry, audience/azp, and nonce where applicable. The Keycloak `sub` claim is used as the application user ID.
域名、HTTPS、公网反向代理说明见
- [域名配置指引.md](./域名配置指引.md)
最小配置变量:
## Build
```bash
export PROJECT_ROOT=/home/ivan/xuan/beaver_project
export BEAVER_NET=beaver-instance-edge
export BEAVER_PROXY_CONTAINER_NAME=beaver-router-proxy
export BEAVER_DEPLOY_TOKEN="$(openssl rand -hex 32)"
export BEAVER_AUTHZ_INTERNAL_TOKEN="$(openssl rand -hex 32)"
export BEAVER_BASE_DOMAIN=localhost
export BEAVER_AUTHZ_URL='http://beaver-authz-service:19090'
export BEAVER_DEPLOY_URL='http://beaver-deploy-control:8090'
export BEAVER_OUTLOOK_MCP_URL=''
export BEAVER_OUTLOOK_MCP_SERVER_ID='outlook_mcp'
cd app-instance
docker build -t beaver/app-instance:keycloak-login .
```
启动顺序:
1. 创建运行目录。
2. 构建四个镜像。
3. 创建共享 Docker network。
4. 启动 `router-proxy`
5. 启动 `authz-service`
6. 启动 `deploy-control`
7. 启动 `auth-portal`
8. 打开 `http://127.0.0.1:3081/register` 测试注册。
## 关键配置关系
`DEPLOY_API_TOKEN``DEPLOY_CONTROL_API_TOKEN` 必须相等:
- `auth-portal` / `authz-service``DEPLOY_API_TOKEN` 请求 `deploy-control`
- `deploy-control``DEPLOY_CONTROL_API_TOKEN` 校验请求。
`AUTHZ_ISSUER` 在这套单机部署里要写容器网络地址:
```text
http://beaver-authz-service:19090
```
不要写成 `http://127.0.0.1:19090`,因为新创建的 `app-instance` 容器里的 `127.0.0.1` 指向它自己,不是 AuthZ 容器。
`DEPLOY_PUBLIC_*` 决定新实例展示给用户的 URL
## Run Locally
```bash
DEPLOY_PUBLIC_SCHEME=http
DEPLOY_PUBLIC_BASE_DOMAIN=localhost
DEPLOY_PUBLIC_PORT=8088
cd app-instance
./run-standalone.sh --build --replace
```
本机测试时实例 URL 形如:
The script prints the `web_origin`, `redirect_uri`, and `post_logout_redirect_uri` values that must be configured in Keycloak.
```text
http://alice.localhost:8088
```
## Useful Paths
正式 HTTPS 域名通常改成:
- `app-instance/` - app image, frontend, backend, runtime helper script, app tests
- `skills/` - reusable skill content retained for product/runtime packaging
- `agents/` - agent registry retained for runtime packaging
- `docs/` - product and architecture documentation
- `AGENTS.md` - local coding-agent instructions
```bash
DEPLOY_PUBLIC_SCHEME=https
DEPLOY_PUBLIC_BASE_DOMAIN=apps.example.com
DEPLOY_PUBLIC_PORT=443
```
实例 URL 形如:
```text
https://alice.apps.example.com
```
前提是你已经在项目外层把 `*.apps.example.com``80/443` 流量转发到 `router-proxy:8088`
## 模型配置方式
当前版本不会在注册创建实例时写入模型 provider、model 或 API key。
流程是:
1. 注册先创建一个不含模型凭证的实例。
2. `auth-portal` 进入模型配置引导页。
3. 用户确认后Portal 调用 `deploy-control /api/instances/configure-provider`
4. `deploy-control` 写入该实例的 `config.json` 并重启对应容器。
如果用户跳过引导,实例仍会创建成功,但后续需要在实例内补齐 provider 配置后才能正常调用模型。
## 持久化目录
至少保留:
```text
authz-service/runtime/data
app-instance/runtime/instances
app-instance/runtime/registry
router-proxy/runtime/conf.d
```
不要在需要保留账号、实例或配置时删除这些目录。
## 模板文件
可参考这些环境变量模板:
- [`.env.example`](./.env.example)
- [`auth-portal/src/.env.example`](./auth-portal/src/.env.example)
- [`authz-service/.env.example`](./authz-service/.env.example)
- [`deploy-control/.env.example`](./deploy-control/.env.example)
- [`router-proxy/.env.example`](./router-proxy/.env.example)
这些模板不会被脚本自动加载。你可以手动 `export`,也可以在 `docker run` 时使用 `--env-file`
## 子项目文档
- [`app-instance/README.md`](./app-instance/README.md)
- [`auth-portal/src/README.md`](./auth-portal/src/README.md)
- [`authz-service/README.md`](./authz-service/README.md)
- [`deploy-control/README.md`](./deploy-control/README.md)
- [`router-proxy/README.md`](./router-proxy/README.md)
See `app-instance/README.md` for the app-specific contract and environment variables.