From c73b642504ad9d311ceaebd1b12a47741e0887e9 Mon Sep 17 00:00:00 2001 From: RhenCloud Date: Wed, 12 Aug 2026 00:05:20 +0800 Subject: [PATCH] feat(admin): deep-linkable console URLs, stop collapsing unknown paths to /admin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root catch-all page ([...slug].vue) redirected every unknown URL to /admin, and the worker fell back to the SPA for any path — so no matter what page was opened, the URL ended up at baseurl/admin. - Extract the console into ConsolePage.vue; the active tab now mirrors the URL path: /admin (groups), /admin/groups, /admin/logs, /admin/audit (tab switches use router.replace; unknown slugs render 404) - admin.vue and admin/[slug].vue become thin wrappers - Delete the root [...slug].vue catch-all redirect - server.ts: notFound serves ASSETS (SPA + _nuxt chunks) only for /admin-prefixed paths; every other unknown URL returns a JSON 404 --- admin/components/ConsolePage.vue | 456 +++++++++++++++++++++++++++++++ admin/pages/[...slug].vue | 7 - admin/pages/admin.vue | 439 +---------------------------- admin/pages/admin/[slug].vue | 3 + docs/guide/configuration.md | 2 + docs/zh/guide/configuration.md | 2 + src/server.ts | 13 +- 7 files changed, 476 insertions(+), 446 deletions(-) create mode 100644 admin/components/ConsolePage.vue delete mode 100644 admin/pages/[...slug].vue create mode 100644 admin/pages/admin/[slug].vue diff --git a/admin/components/ConsolePage.vue b/admin/components/ConsolePage.vue new file mode 100644 index 0000000..f4a8abb --- /dev/null +++ b/admin/components/ConsolePage.vue @@ -0,0 +1,456 @@ + + + diff --git a/admin/pages/[...slug].vue b/admin/pages/[...slug].vue deleted file mode 100644 index eff5d5f..0000000 --- a/admin/pages/[...slug].vue +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/admin/pages/admin.vue b/admin/pages/admin.vue index 01a9ec6..771b572 100644 --- a/admin/pages/admin.vue +++ b/admin/pages/admin.vue @@ -1,440 +1,3 @@ - - diff --git a/admin/pages/admin/[slug].vue b/admin/pages/admin/[slug].vue new file mode 100644 index 0000000..771b572 --- /dev/null +++ b/admin/pages/admin/[slug].vue @@ -0,0 +1,3 @@ + diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 25eb040..9cf3e65 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -56,6 +56,8 @@ WebHooker ships with a built-in config console at `/admin` for managing routes i ### Endpoints +The console is served as an SPA at `/admin`; its tabs are deep-linkable via the URL path (`/admin/groups`, `/admin/logs`, `/admin/audit`). URLs outside `/admin` that do not match an endpoint below return a plain `404` instead of the console. + | Endpoint | Description | | ----------------------------------------- | -------------------------------------------- | | `GET /admin` | Config console UI | diff --git a/docs/zh/guide/configuration.md b/docs/zh/guide/configuration.md index 7fff21d..ef6d38f 100644 --- a/docs/zh/guide/configuration.md +++ b/docs/zh/guide/configuration.md @@ -56,6 +56,8 @@ WebHooker 内置了位于 `/admin` 的配置控制台,可在浏览器中管理 ### 端点 +控制台以 SPA 形式挂在 `/admin`,各标签页可通过 URL 路径直达(`/admin/groups`、`/admin/logs`、`/admin/audit`)。`/admin` 之外且未匹配下方端点的 URL 直接返回 `404`,不会再被吞进控制台。 + | 端点 | 说明 | | ------------------------------------- | ------------------------------------ | | `GET /admin` | 配置控制台页面 | diff --git a/src/server.ts b/src/server.ts index b404ea5..6df056e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -79,7 +79,18 @@ export function createServer(): Hono<{ Bindings: Env }> { app.notFound((c) => { if (c.env.ASSETS) { - return c.env.ASSETS.fetch(c.req.raw); + // The console SPA (and its _nuxt chunks) lives under /admin; the worker + // already owns /admin/api, /admin/login|logout|invite. Serve the SPA + // only for admin-scoped paths — every other unknown URL is a plain 404 + // instead of being swallowed into the console. + const pathname = new URL(c.req.url).pathname; + if ( + pathname === "/admin" || + pathname.startsWith("/admin/") || + pathname.startsWith("/_nuxt/") + ) { + return c.env.ASSETS.fetch(c.req.raw); + } } return c.json({ error: "Not found" }, 404); });