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
+64
View File
@@ -31,17 +31,69 @@ key). `PLUGIN_CONFIG_KEY` is required in every environment; use a randomly
generated secret in production and keep it stable across restarts so encrypted
plugin configuration remains decryptable.
When the optional subscription module is enabled, set `PLUGIN_PUBLIC_URL` to
the externally reachable Plugin Admin base URL (without `/admin`), for example
`https://CORE_ORIGIN/extensions/qiu.plugin-admin`. Menu apply then always emits
`/admin/#/modules/subscription/overview`; a subscription service URL is never
used as a browser entrypoint.
Open `/admin/` directly or expose the service through the reverse proxy in
`deploy/`. The first login is the existing Core administrator login; no plugin
user table is created. The control plane stores only a short-lived server-side
session and encrypted plugin configuration.
The administrator UI is organized as separate hash-routed pages: overview,
installed plugins, marketplace, and operations. Each plugin has its own detail
route with secondary tabs for health, revisions, configuration, menu
integration, and history. See
[`../../docs/PLUGIN_ADMIN_UI_INFORMATION_ARCHITECTURE.md`](../../docs/PLUGIN_ADMIN_UI_INFORMATION_ARCHITECTURE.md)
for the page responsibilities and responsive layout contract.
### TDesign UI build
The browser shell is a standalone Vue 3 application based on the Tencent
TDesign Vue Next starter. It is built outside the Core repository and then
embedded by this Go service:
```sh
./build-ui.sh
```
The command runs `npm ci`, type-checks the application, bundles TDesign Icons
and ECharts locally (the service CSP does not allow a CDN), and copies the
three runtime files into `ui/`. The generated shell keeps the existing
`/login`, `/api/me`, CSRF and lifecycle contracts; no Core source or frontend
build configuration is involved.
### 会话与令牌生命周期
插件进程是常驻服务,不会因为一次登录、一次请求或一次令牌刷新而重启。
Core 的 access token 过期时,插件后端在当前请求链路中使用对应的 refresh
token 刷新一次,并继续完成请求;刷新后的 token 仍只保存在插件服务端。
浏览器始终只持有插件的 HttpOnly 会话 Cookie 和 CSRF token。
默认会话策略如下:
| 情况 | 行为 |
| --- | --- |
| 持续使用 | 每次请求滑动续期,通常无需重新登录 |
| 空闲超过 30 分钟 | 插件会话失效,下一次访问回到登录页 |
| 会话达到 8 小时 | 绝对过期,需要重新登录 |
| Core access token 过期 | 后端透明 refresh,不重启插件 |
| refresh token 被撤销/失效 | 清除插件会话并要求重新登录 |
| 插件进程重启 | V1 内存会话清空,需要重新登录一次;已登记的插件由控制面按 registry 恢复 |
因此日常使用不需要“用完就重启”。生产多实例若需要跨实例或跨重启保留
会话,应接入插件自己的加密 Redis/会话存储,并使用稳定的密钥;不要把
Core token 写入浏览器、Core 数据库或 URL。
## Control-plane endpoints
```text
GET /healthz
GET /readyz
POST /login POST /login/2fa POST /logout
GET /api/captcha-config
GET /api/marketplace POST /api/marketplace/install (JSON: plugin_id, version)
GET /api/me GET /api/plugins GET /api/plugins/{id}
POST /api/plugins/install (multipart field: package)
@@ -53,6 +105,11 @@ GET|PUT /api/plugins/{id}/config
POST /api/plugins/{id}/menu-preview|menu-apply
POST /api/menu-items/preview|apply (JSON: {"plugin_id":"..."})
GET /api/audit
GET /api/subscription/status
GET /api/subscription/audit
GET /api/subscription/plans
GET /api/subscription/subscriptions[/{id}]
GET /api/subscription/users/{id}[/subscriptions]
```
Every mutation requires the plugin CSRF token and an `Idempotency-Key`. A
@@ -65,6 +122,13 @@ installs its runtime/menu. Failed installation and upgrade never replace the
active revision. Delete/uninstall is allowed only after disable and removes
plugin files, not Core data.
The login page reads only public CAPTCHA fields from `/api/captcha-config`.
When Core enables Turnstile, Tencent Captcha, or Aliyun Captcha, the matching
challenge is rendered in this page and its one-time proof is forwarded
server-side to Core. Provider secrets are never returned. Keep the provider
origins in the example CSP when CAPTCHA is enabled; changing the Core setting
is picked up on the next login-page load and does not require a plugin restart.
## Marketplace catalog
The marketplace is server-side only. The browser receives metadata and sends a