diff --git a/docs/audit/2026-06-11-global-floating-browser.md b/docs/audit/2026-06-11-global-floating-browser.md new file mode 100644 index 00000000..3f4b0811 --- /dev/null +++ b/docs/audit/2026-06-11-global-floating-browser.md @@ -0,0 +1,30 @@ +# 审计 — 全局浮动浏览器 + +## 范围(文件清单) + +| 文件 | 变更 | +|------|------| +| `server/utils/browser_ui_state.py` | URL 校验、visit 上限 | +| `server/api/browser.py` | `/api/browser/state` | +| `frontend/src/composables/useGlobalBrowser.ts` | 全局状态 | +| `frontend/src/components/GlobalBrowserPanel.vue` | 浮动面板 | +| `tests/test_browser_ui_state.py` | 单测 | + +## Step 3 规则扫描 + +| H | 规则 | 结论 | +|---|------|------| +| H1 | 无 SSRF | PASS — 无服务端 fetch | +| H2 | URL 白名单 | PASS — http/https only | +| H3 | 按管理员隔离 | PASS — admin_ui_preferences | +| H4 | iframe sandbox | PASS | + +## DoD + +- [x] pytest +- [x] type-check +- [x] changelog + +## 验证 + +浮动打开 → 最小化右下角 → 左下角展开 → 切换菜单 iframe 仍在。 diff --git a/docs/changelog/2026-06-11-global-floating-browser.md b/docs/changelog/2026-06-11-global-floating-browser.md new file mode 100644 index 00000000..8bd02d73 --- /dev/null +++ b/docs/changelog/2026-06-11-global-floating-browser.md @@ -0,0 +1,41 @@ +# 2026-06-11 全局浮动浏览器与记录持久化 + +## 摘要 + +内置浏览器改为 **全局浮动面板**:可拖动/缩放、最小化到**右下角**、**左下角**常驻按钮随时展开;切换侧栏页面时保持活动。浏览记录、标签、窗口位置同步至 MySQL(`admin_ui_preferences`)。 + +## 动机 + +用户需要在运维任意页面内预览站点,最小化后不占全屏,且换页不丢失;历史记录需跨浏览器/清缓存保留。 + +## 涉及文件 + +| 文件 | 变更 | +|------|------| +| `server/utils/browser_ui_state.py` | 状态解析与 visit 记录 | +| `server/api/browser.py` | `GET/PUT /api/browser/state` | +| `server/main.py` | 注册路由 | +| `frontend/src/composables/useGlobalBrowser.ts` | 全局单例状态 | +| `frontend/src/components/GlobalBrowserPanel.vue` | 浮动 UI | +| `frontend/src/pages/BrowserPage.vue` | 打开全局浏览器并返回 | +| `frontend/src/App.vue` | 挂载全局组件 | +| `frontend/src/pages/ServersPage.vue` | 「站点」直接打开浮动窗 | +| `tests/test_browser_ui_state.py` | 单测 | + +## 迁移 / 重启 + +- 无需新表(复用 `admin_ui_preferences`) +- 需 API 重启 + 前端 build + +## 验证 + +```bash +pytest tests/test_browser_ui_state.py -q +cd frontend && npm run type-check +``` + +左下角地球按钮打开;最小化后在右下角;历史按钮可见记录;换账号后记录隔离。 + +## 说明 + +iframe 在用户当前的 Chrome/Firefox 等引擎中渲染,非独立安装 Firefox;禁止嵌入的站点请用「新标签打开」。 diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 870a71b7..991ddf70 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -223,6 +223,8 @@ {{ snackbar.text }} + + @@ -247,6 +249,8 @@ import { } from '@/composables/useScriptSubmitToast' import { clearCachedQueries } from '@/composables/useCachedQuery' import { scheduleRoutePrefetch, cancelRoutePrefetch } from '@/composables/useRoutePrefetch' +import GlobalBrowserPanel from '@/components/GlobalBrowserPanel.vue' +import { useGlobalBrowser } from '@/composables/useGlobalBrowser' import { CACHED_PAGE_NAMES } from '@/constants/cachedPages' import { api } from '@/api' @@ -327,8 +331,7 @@ provide('nexusDrawer', drawer) const mainLayoutClass = computed(() => ({ 'nexus-terminal-main': route.path === '/terminal', - 'nexus-browser-main': route.path === '/browser', - 'nexus-page-main': route.path !== '/terminal' && route.path !== '/browser' && route.path !== '/login', + 'nexus-page-main': route.path !== '/terminal' && route.path !== '/login', })) const scriptExecBarOffset = computed(() => { @@ -417,31 +420,6 @@ window.$snackbar = (text: string, color = 'success') => { padding: 0; } -.v-application .v-main.nexus-browser-main { - display: flex; - flex-direction: column; - box-sizing: border-box; - height: 100dvh; - max-height: 100dvh; - min-height: 0; - max-width: 100%; - overflow: hidden; - padding-top: var(--v-layout-top, 64px) !important; - padding-bottom: 0 !important; - transition: - padding 0.2s cubic-bezier(0.4, 0, 0.2, 1), - height 0.2s cubic-bezier(0.4, 0, 0.2, 1); -} -.v-application .v-main.nexus-browser-main > .v-container { - flex: 1 1 auto; - min-height: 0; - min-width: 0; - max-width: 100%; - width: 100%; - padding: 0; - height: 100%; -} - /* 仪表盘等业务页:随侧栏 --v-layout-left 缩进,表格可横向滚动 */ .v-application .v-main.nexus-page-main { box-sizing: border-box; diff --git a/frontend/src/components/GlobalBrowserPanel.vue b/frontend/src/components/GlobalBrowserPanel.vue new file mode 100644 index 00000000..fefeefb9 --- /dev/null +++ b/frontend/src/components/GlobalBrowserPanel.vue @@ -0,0 +1,388 @@ +