* feat(plugin): add webui metadata schema for plugins * feat(dashboard): serve plugin webui with scoped asset tokens * feat(dashboard): add plugin webui page and extension entry actions * test(dashboard): cover plugin webui auth and asset routing * fix(dashboard): use aiofiles for non-blocking plugin webui assets * fix(dashboard): streamline JWT extraction and validation for plugin webui paths * fix(dashboard): harden plugin webui bridge and auth cookie security * fix(dashboard): restore plugin webui bridge under sandbox iframe * refactor(dashboard): apply plugin webui review improvements * docs: 补充插件 WebUI 开发指南 * fix(plugin-webui): 统一 WebUI title 契约并修复桥接行为 * docs: 更新插件 WebUI 开发指南 * fix * feat: Introduce Plugin Pages feature - Added support for plugins to expose Dashboard pages via a `pages/` directory. - Updated `PluginDetailPage.vue` to include a button for opening plugin pages. - Refactored `useExtensionPage.js` to remove the deprecated `openPluginWebUI` function. - Updated documentation to replace references from "Plugin WebUI" to "Plugin Pages". - Created new documentation for Plugin Pages detailing structure, examples, and API usage. - Removed the old Plugin WebUI documentation. - Updated tests to reflect changes from Plugin WebUI to Plugin Pages, ensuring proper functionality and security checks. * feat: 增强插件页面功能,添加返回按钮逻辑并更新测试用例 * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Soulter <905617992@qq.com> Co-authored-by: Weilong Liao <37870767+Soulter@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
4.4 KiB
插件 Pages
AstrBot 支持插件通过 pages/ 目录暴露 Dashboard 页面。pages/ 下的每个一级子目录都是一个独立 Page:
astrbot_plugin_page_demo/
├─ main.py
└─ pages/
├─ bridge-demo/
│ ├─ index.html
│ ├─ app.js
│ ├─ style.css
│ └─ assets/
│ └─ logo.svg
└─ settings/
└─ index.html
AstrBot 会扫描 pages/<page_name>/index.html;没有 index.html 的目录会被忽略。
如果只是让用户填写几个配置项,优先使用 _conf_schema.json。插件 Pages 更适合复杂表单、Dashboard、日志、文件上传下载、SSE 和自定义交互流程。
最小前端示例
pages/bridge-demo/index.html
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>Plugin Page Demo</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<button id="ping">Ping</button>
<pre id="output"></pre>
<script type="module" src="./app.js"></script>
</body>
</html>
pages/bridge-demo/app.js
const bridge = window.AstrBotPluginPage;
const output = document.getElementById("output");
const context = await bridge.ready();
output.textContent = JSON.stringify(context, null, 2);
document.getElementById("ping").addEventListener("click", async () => {
const result = await bridge.apiGet("ping");
output.textContent = JSON.stringify(result, null, 2);
});
这里不需要手动引入 bridge SDK。AstrBot 会在返回的 HTML 里自动插入 /api/plugin/page/bridge-sdk.js。
注册后端 API
前端调用 bridge.apiGet("ping") 时,Dashboard 会转发到:
/api/plug/<plugin_name>/ping
因此注册 Web API 时,路由必须带上插件名作为前缀:
from quart import jsonify
from astrbot.api.star import Context, Star
PLUGIN_NAME = "astrbot_plugin_page_demo"
class MyPlugin(Star):
def __init__(self, context: Context):
super().__init__(context)
context.register_web_api(
f"/{PLUGIN_NAME}/ping",
self.page_ping,
["GET"],
"Page ping",
)
async def page_ping(self):
return jsonify({"message": "pong"})
Bridge API
插件 Page 中可直接使用 window.AstrBotPluginPage:
ready(): 等待 bridge 就绪并返回上下文getContext(): 读取当前上下文apiGet(endpoint, params): 发送 GET 请求apiPost(endpoint, body): 发送 POST 请求upload(endpoint, file): 以multipart/form-data上传单个文件download(endpoint, params, filename): 下载后端响应subscribeSSE(endpoint, handlers, params): 订阅 SSEunsubscribeSSE(subscriptionId): 取消 SSE 订阅
当前 ready() 上下文类似:
{
"pluginName": "astrbot_plugin_page_demo",
"displayName": "Plugin Page Demo"
}
endpoint 必须是插件内相对路径,不能为空,不能包含 \、URL scheme、query、hash,也不能包含 . 或 .. 路径片段。
静态资源路径规则
AstrBot 会重写相对资源路径,并自动补上短期 asset_token。你只需要正常写相对路径,不要自己拼接 /api/plugin/page/content/...。
AstrBot 会重写:
- HTML
src和href - CSS
url(...) - JavaScript
import - JavaScript
export ... from - JavaScript 动态
import()
建议把静态资源写成 ./style.css、./assets/logo.svg 这类相对路径。不要手动追加 asset_token,也不要依赖 .. 逃逸 Page 根目录。
如果你构建 SPA,建议使用 hash routing。静态资源服务按真实文件路径解析;history routing 刷新页面时需要对应路径上真的存在文件。
安全约束
插件 Pages 运行在受限 iframe 中:
allow-scripts allow-forms allow-downloads
Page 不能直接访问 Dashboard cookies、LocalStorage 或同源 DOM,也不能绕过 bridge 复用 Dashboard auth。
AstrBot 还会给资源响应添加安全头,包括:
X-Frame-Options: SAMEORIGINContent-Security-Policy: frame-ancestors 'self'; object-src 'none'; base-uri 'self'Cache-Control: no-store
调试建议
- 新增或删除 Page 目录后重载插件
- 修改
pages/<page_name>/下的大多数静态资源后,刷新 Page 即可 - 如果 Page 没出现,检查
pages/<page_name>/index.html是否存在,以及插件是否启用