feat: complete unified plugin admin v1.1.0
Business Plugins CI / check (plugin-admin) (push) Successful in 1m42s
Business Plugins CI / check (subscription-admin) (push) Successful in 1m30s

This commit is contained in:
Qiufeng
2026-08-30 12:10:04 +08:00
parent 3c1a17f4d7
commit ada4ab3c21
69 changed files with 6681 additions and 901 deletions
+17 -8
View File
@@ -2,7 +2,9 @@
状态:Accepted Contract / V1 参考实现已完成本地验收
本文定义与 Sub2API Core 解耦的通用业务插件框架。业务插件是独立服务、独立端口、独立版本和独立 UI;它可以通过现有管理员自定义菜单嵌入 Core,也可以在新窗口运行。订阅管理只是一个可选业务插件,不能成为框架后台、Core 热路径或插件生命周期的固定组成部分。
本文定义与 Sub2API Core 解耦的通用业务插件框架。业务插件的后端服务、端口和版本独立;浏览器端由统一的 Plugin Admin 控制面承载,业务插件 UI 以模块方式挂载到同一个管理员 Shell 中。订阅管理只是一个可选业务模块,不能成为框架后台、Core 热路径或插件生命周期的固定组成部分。
> **前端架构修订(2026-08-30)**:此前“独立 UI/独立管理员后台”的表述仅指后端服务可以独立部署,不表示业务模块要再次登录。Plugin Admin 负责唯一的管理员登录、会话、导航和 CSRF;订阅模块安装并启用后才出现在控制面导航中,继承同一会话,不提供第二个登录页或第二套 Cookie。
## 1. 目标
@@ -14,7 +16,7 @@ V1 需要提供一个独立的插件控制面,负责:
- 保存插件版本、服务地址、运行状态、菜单声明和操作审计;
- 通过 Core 现有管理员鉴权复用操作者身份;
- 将已启用插件的管理员菜单注入 `custom_menu_items`;
- 让每个业务插件仅通过自己的 BFF 调用 Core 明确允许的 API。
- 让每个业务插件仅通过控制面或其受控模块 BFF 调用 Core 明确允许的 API。
V1 不改变 Core Go/Vue、数据库迁移、现有鉴权、前端路由或 `.s2plugin` transport ABI。控制面自己的注册表、安装目录、进程和配置存储属于独立服务;它不连接 Core PostgreSQL、Redis 或宿主业务表。
@@ -25,7 +27,7 @@ V1 不改变 Core Go/Vue、数据库迁移、现有鉴权、前端路由或 `.s2
| 运行方式 | Core 子进程 + gRPC | 独立服务 + HTTP/BFF |
| 能力 | `openai.oauth.outbound_transport.v1` | 由清单声明的业务能力 |
| 生命周期 | Core `PluginManager` | 独立 Plugin Control Plane |
| UI | 配置 iframe + UI Bridge | 业务后台/用户工具页面 |
| UI | 配置 iframe + UI Bridge | 统一控制面中的业务模块页面 |
| 数据边界 | Core 负责账号转发与计费 | Core 负责权威业务数据,插件只读/投影 |
| 菜单 | Core 固定插件管理页 | `custom_menu_items` 管理员入口 |
@@ -60,7 +62,7 @@ Sub2API Core 现有鉴权、Admin API 和领域账本
- `plugin_operator`:查看状态、日志摘要和健康诊断,不改变包或凭据。
- `plugin_readonly`:只读查看已登记插件。
V1 的控制面只允许 Core `role=admin` 登录。插件不创建第二套 Core 用户表;插件会话只保存 `plugin_id`、`admin_user_id`、角色、会话版本和过期时间。UI 隐藏按钮不等于授权,控制面和 Core API 均须重新校验权限。
V1 的 Plugin Admin 控制面只允许 Core `role=admin` 登录。插件不创建第二套 Core 用户表;控制面会话保存 `admin_user_id`、角色、会话版本和过期时间,订阅等业务模块直接继承该会话。模块不得创建自己的登录页、Cookie 或独立权限入口。UI 隐藏按钮不等于授权,控制面、模块后端和 Core API 均须重新校验权限。
## 5. 管理面契约
@@ -157,9 +159,16 @@ Core 返回 `401` 时,一次用户请求最多 refresh 一次;并发 refresh
## 9. 会话和嵌入
插件登录调用 Core 现有 `/auth/login`、按需 `/auth/login/2fa`,再调用 `/auth/me` 校验管理员角色。Core token 只存插件服务端会话,浏览器只持有 HttpOnly、Secure、SameSite Cookie 和插件 CSRF token。
Plugin Admin 登录调用 Core 现有 `/auth/login`、按需 `/auth/login/2fa`,再调用 `/auth/me` 校验管理员角色。Core token 只存控制面服务端会话,浏览器只持有控制面的 HttpOnly、Secure、SameSite Cookie 和 CSRF token;订阅模块请求沿用这套会话。
Core 的自定义页面当前使用 sandbox iframe,且不会自动继承 Core `localStorage` 登录态。因此 V1 必须同时提供新窗口入口;iframe 首屏显示插件登录页是已知行为。真正无感 SSO 需要 V1.1 的一次性 code/state 或受控 `postMessage` 交接,不得把 JWT 放在 URL。
会话和进程是两个独立的生命周期:插件作为常驻服务运行,Core access token
到期时由后端按需 refresh,不要求重启插件。默认会话空闲 30 分钟、绝对上限
8 小时;refresh 失败或 Core 撤销管理员后清除会话并要求重新登录。V1 会话
默认只在插件进程内存中保存,所以控制面重启后需要重新登录一次,但已启用
插件会按 registry 恢复。跨实例或跨重启免登录必须接入插件自有加密共享会话
存储,不能把 Core token 放入浏览器或 Core 数据库。
Core 的自定义页面当前使用 sandbox iframe,且不会自动继承 Core `localStorage` 登录态。因此 iframe 首屏由 Plugin Admin 显示一次登录页;进入订阅模块时不再追加登录。真正无感 Core SSO 仍需要 V1.1 的一次性 code/state 或受控 `postMessage` 交接,不得把 JWT 放在 URL。
菜单注入使用 Core 现有 `custom_menu_items`:
@@ -167,7 +176,7 @@ Core 的自定义页面当前使用 sandbox iframe,且不会自动继承 Core
{
"id": "DOMAIN_PLUGIN_ID",
"label": "DOMAIN_PLUGIN_LABEL",
"url": "https://CORE_ORIGIN/extensions/DOMAIN_PLUGIN_ID/",
"url": "https://PLUGIN_PUBLIC_ORIGIN/extensions/qiu.plugin-admin/admin/#/modules/DOMAIN_MODULE/overview",
"visibility": "admin",
"sort_order": 200
}
@@ -200,7 +209,7 @@ Core 始终是用户身份、余额、订阅、订单、用量、权限、计费
### Phase 2:业务插件适配
提供 `DOMAIN_PLUGIN_ID` 级别的 SDK/模板和契约测试。订阅管理作为首个独立业务插件接入,只实现自身领域页面和 Core 只读 API,不改变控制面。
提供 `DOMAIN_PLUGIN_ID` 级别的 SDK/模板和契约测试。订阅管理作为首个业务模块接入统一控制面,只实现自身领域页面和 Core 只读 API,不复制控制面的登录、导航和会话。
### Phase 3:生产增强