Files
iAOP/deploy/fba/README.md
T
bot_dev1 5667fad3e1 fix(#188): 推理 API 路径规范化,消除双 /v1 前缀对 nginx rewrite 的隐式依赖
问题:infer.ts BASE_URL=/v1,fetchModels/chatCompletion 再拼 /v1/* 导致
/v1/v1 双前缀,当前仅靠 nginx 对 /v1/ rewrite 去前缀巧合耦合才通。

修复(前后端+部署一起改):
1. nginx-fba.conf:新增 location /v1/,proxy_pass http://127.0.0.1:30800/
   (带末尾 /,不去前缀直接透传),消除对 rewrite 的隐式依赖。
2. infer.ts:BASE_URL 改为空串,调用方统一写 /v1/models、
   /v1/chat/completions、/v1/health(fetchHealth 原误用 /health 已修正)。
3. cockpit/index.vue:健康检查改走 infer.ts 的 fetchHealth 封装,
   不再裸 fetch('/v1/health'),路径口径统一。
4. ask() latencyMs 由硬编码 0 改为 performance.now() 实测耗时。
5. README 补充推理通道路径约定说明。

验收:浏览器 Network 确认 /v1/models、/v1/chat/completions、
/v1/health 均 200 且无 /v1/v1 请求。(本环境无 pnpm,构建验证转 bot_qa)
2026-08-06 16:11:37 +08:00

173 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# iAOP · FBA 基础设施部署(Epic #159 · Phase 0)
将 [fastapi-best-architecture](https://github.com/fastapi-practices/fastapi-best-architecture)
(下称 FBA)作为 iAOP 的统一认证 / 用户角色权限基座,以 Docker 方式与现有 iAOP
服务并行部署,由宿主机现有 nginx 以 `/fba/` 前缀统一反代。
## 架构位置
```
浏览器
│ http://39.101.182.167:8090
▼
宿主机 nginx(现有 iAOP 站点)
├── / → iAOP 前端(web/ 静态)
├── /api/v1/ → iAOP 后端(现有 FastAPI,业务接口)
└── /fba/ → 127.0.0.1:8001 → fba_server(FBA 后端,认证/RBAC)
├── fba_postgres(PG16)
└── fba_redis
```
> iAOP 自身也使用 `/api/v1` 前缀,因此 FBA 必须通过 `/fba/` 前缀隔离,
> 两者不能共用路径。
## 前置条件
1. 服务器已安装 Docker 与 Docker Compose 插件(`docker compose version` 可查)。
2. 获取 FBA 源码,放到本仓库同级目录:
```bash
cd /opt/apps # 假设 iAOP 在 /opt/apps/iAOP
git clone https://github.com/fastapi-practices/fastapi-best-architecture fba-backend
```
若放在其他位置,修改 `compose.env` 中的 `FBA_SRC`。
## 部署步骤
```bash
cd /opt/apps/iAOP/deploy/fba
# 1. 修改密钥与密码(生产必做)
# - compose.env: FBA_PG_PASSWORD
# - fba.env: DATABASE_PASSWORD(与上一致)、TOKEN_SECRET_KEY
python3 -c "import secrets; print(secrets.token_urlsafe(32))" # 生成 TOKEN_SECRET_KEY
# 2. 构建并启动
docker compose --env-file compose.env up -d --build
# 3. 初始化数据库表结构 + 内置数据(菜单/角色/管理员)
docker exec -it fba_server fba init
# 若 fba 命令不可用,改用:docker exec -it fba_server python backend/cli.py init
# 4. 验证服务
curl -s http://127.0.0.1:8001/api/v1/auth/captcha | head -c 200
# 5. 合并 nginx 片段(见 nginx-fba.conf 头部说明),然后 reload
nginx -t && nginx -s reload
# 6. 通过外部入口验证
curl -s http://39.101.182.167:8090/fba/api/v1/auth/captcha | head -c 200
```
FBA 默认管理员账号:`admin / 123456`(登录后请立即修改)。
## 常用运维命令
```bash
docker compose --env-file compose.env logs -f fba_server # 看日志
docker compose --env-file compose.env restart fba_server # 重启
docker compose --env-file compose.env down # 停止(数据保留在卷中)
docker exec -it fba_postgres psql -U postgres -d fba # 进数据库
```
## 与 iAOP 代码侧的关系(后续 Phase)
- **Phase 1**:iAOP 前端 `web/shared/session.js` 改为调用
`/fba/api/v1/auth/login`(JSON 登录)与 `/fba/api/v1/auth/codes`
(授权码)做登录与鉴权,现有 `core/auth` 退役。
- **Phase 2**:Sider 菜单由 FBA `sys_menu` 接口驱动(`GET /fba/api/v1/sys/menus/sidebar`,
服务端按角色授权过滤),替代前端硬编码角色过滤。**需先执行菜单播种**:
```bash
python3 deploy/fba/seed_iaop_menus.py \
--base http://127.0.0.1:8001/api/v1 \
--username admin --password '<FBA管理员密码>'
```
脚本幂等:注册 iAOP 6 模块菜单(含子菜单)、创建 `engineer`/`viewer`
角色并按 PRD 8.2 分配菜单。之后在 FBA 后台给用户分配角色即可。
未播种时前端自动降级为本地角色过滤,菜单不会空白。
- **Phase 3**:FBA UI(Vue3 + AntDV)管理界面已预构建,产物在
`deploy/fba/artifacts/fba-ui-dist.tar.gz`(构建参数 `VITE_BASE=/fba-admin/`、
`VITE_GLOB_API_URL=http://39.101.182.167:8090/fba`)。部署:
```bash
cd /opt/apps/iAOP
mkdir -p deploy/fba/fba-ui
tar -xzf deploy/fba/artifacts/fba-ui-dist.tar.gz -C deploy/fba/fba-ui/
# 确认 nginx 已 include 最新 nginx-fba.conf(含 /fba-admin/ 段)后 reload
nginx -t && nginx -s reload
```
入口 `http://39.101.182.167:8090/fba-admin/`,用 FBA 账号登录即可管理
用户/角色/菜单/日志。iAOP 顶栏对 admin 角色提供「系统管理」直达链接。
若需重新构建(如换域名):改 `fba-ui/apps/web-antdv-next/.env.production`
后 `pnpm --filter @vben/web-antdv-next build`(Vben5 monorepo,需 pnpm)。
- **Phase 4**:业务接口按钮级权限。
- 前端:`IAOP_SESSION.perms()` / `hasPerm(code)` 读取 FBA `/auth/codes`
权限码(超管自动拥有全部;菜单播种时已写入 `iaop:<module>`)。
- 后端:`core/auth/fba_jwt.py` 提供零依赖 HS256 校验器 `FbaAuth`
(与 FBA 的 JWT 同构:claims = sub/session_uuid/exp,密钥取环境变量
`FBA_TOKEN_SECRET_KEY`,**必须与 fba.env 的 TOKEN_SECRET_KEY 一致**)。
业务服务内 `FbaAuth.from_env()` 返回 None 时走旧 core/auth 会话(双轨过渡)。
按钮级校验用 `fba.require_perm(authorization, "iaop:xxx")`(回源
`/auth/codes`,带 5 分钟内存缓存)。
- 单元测试:`python -m unittest core.auth.tests.test_fba_jwt -v`(8 例全过)。
- **Phase 5**:收尾。待全部 Phase 部署验收后:旧 core/auth 登录轨与
UserStore 本地账号降级保留为演示模式;生产环境以 FBA 为唯一认证源。
## 注意事项
- `fba_postgres` / `fba_redis` 不映射宿主端口,仅容器网络内可达;
`fba_server` 仅绑定 `127.0.0.1:8001`。
- iAOP 现有数据(SQLite 业务库)与 FBA 的 PG 库完全独立,Phase 0 不迁移任何数据。
- FBA 源码目录(`fba-backend`)不属于本仓库,升级 FBA 时在
`fba-backend` 目录内 `git pull` 后重新 `--build` 即可。
---
## Epic #163 · FBA UI 原生重构(2026-08,issue #164-#177)
### 新架构
```
浏览器
│ http://39.101.182.167:8090
▼
宿主机 nginx(deploy/fba/nginx-fba.conf)
├── / → iAOP 静态外壳(web/,C1 后自动收口到 /fba-admin/)
├── /fba-admin/ → FBA UI 原生版(Vben5 web-antdv-next,构建产物 dist/)
│ · 菜单由 FBA 后端下发(seed_iaop_menus.py v2,type=1 原生菜单)
│ · 6 模块 → views/iaop/*(cockpit/chat/studio/admin)
│ · 推理通道 /v1/ → 127.0.0.1:30800(推理服务,独立于 /fba)
├── /fba/ → 127.0.0.1:8001 fba_server(认证/RBAC/系统管理)
└── /v1/ → 127.0.0.1:30800 推理服务(iAOP LLM 网关,**不去前缀直接透传**)
```
### 推理通道路径约定(issue #188)
- 推理服务真实路径即 `:30800/v1/*`(`/v1/models`、`/v1/chat/completions`、`/v1/health`)。
- nginx 反代 `/v1/` 用 `proxy_pass http://127.0.0.1:30800/`(带末尾 `/`,**不去前缀**,直接透传到后端的 `/v1/...`)。
- 前端 `api/iaop/infer.ts` 的 `BASE_URL` 为空串,调用方统一写 `/v1/models`、`/v1/chat/completions`、`/v1/health`,**不再出现 `/v1/v1` 双前缀**,也不依赖 nginx rewrite 去前缀的巧合耦合。
- nginx location 片段见 `deploy/fba/nginx-fba.conf`。
### 组件落点
- 受控源码:`fba-ui-src/`(fastapi-best-architecture-ui 工程,apps/web-antdv-next)
- 构建流水线:`build-ui.sh`(pnpm install → build → artifacts/fba-ui-dist.tar.gz)
- 菜单播种 v2:`seed_iaop_menus.py`(幂等 upsert + admin 角色 + 退役菜单隐藏)
- 部署:解包 dist → 替换 fba_ui 容器站点目录 → nginx reload(见 C2 验证记录)
### 旧架构存档
- `web/` 静态演示外壳保留(FBA 停掉时兜底;/index.html、/auth/login.html 已加 C1 自动收口跳转)
- 旧登录页三轨会话已精简为 FBA 单轨(session.js,FBA 接入版)
### 模型注册表服务(issue #182 [E1],PRD 5.3 服务化)
- 代码:`core/model-framework/registry_api.py`(标准库 http.server,零依赖)
- 运行:`REGISTRY_DATA_DIR=<可挂卷目录> python3 core/model-framework/registry_api.py`(默认 :8002,绑定 127.0.0.1)
- 数据:`REGISTRY_DATA_DIR/registry.json`(JSON 持久化;首次启动种子 4 条演示模型)
- 鉴权双轨:FBA JWT(`FBA_TOKEN_SECRET_KEY` 配置时启用,写操作要求 `iaop:admin`/`iaop:studio` 权限码);未配置时降级 core/auth 会话(engineer/admin 角色可写)
- **nginx 反代约定**(加到宿主机 nginx server):
```nginx
location /api/v1/registry/ {
proxy_pass http://127.0.0.1:8002;
proxy_set_header Host $host;
}
```
- 单测:`python core/model-framework/tests/test_registry_api.py`(11 用例:promote/rollback/403 权限拒绝/401/持久化)