From 36e75143fa5c2a5eaf954ee0b9c69d08065e414e Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Fri, 8 May 2026 17:21:03 +0200 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20Phase=209=20=E2=80=94=20restor?= =?UTF-8?q?e=20index=20dashboard,=20fix=20login/CSRF,=20port=20legacy=20st?= =?UTF-8?q?yles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Index dashboard regains the 8 cards that were lost in the SPA port (3X-UI panel info, Operation Hours, System Load, Usage, Overall Speed, Total Data, IP Addresses, Connection Stats), plus a Config button that shows the live xray config.json. Version display falls back through panelUpdateInfo → window.__X_UI_CUR_VER__ → '?' so dev mode isn't blank. - Xray config no longer hangs on load: useXraySetting surfaces failures instead of leaving a perpetual spinner, and the Vite dev proxy stops hijacking POST requests to migrated routes (only GETs get bypassed). - Inbound page no longer throws __asyncLoader/emitsOptions errors — inbound.js was missing imports (NumberFormatter, SizeFormatter, Wireguard) and InboundList kept emitting after unmount. - Login round-trip works after logout: a public /csrf-token endpoint bootstraps the SPA before authentication, axios caches the token module-level, and the dev 401 handler navigates to /login.html instead of reloading the dashboard into a redirect loop. - legacy.css mirrors the legacy panel's surface/text variables so dark and ultra-dark themes match main; every SPA entry imports it. Co-Authored-By: Claude Opus 4.7 --- frontend/src/api/axios-init.js | 18 +- frontend/src/components/AppSidebar.vue | 2 +- frontend/src/inbounds.js | 1 + frontend/src/index.js | 5 + frontend/src/login.js | 1 + frontend/src/models/inbound.js | 2 +- frontend/src/pages/inbounds/InboundList.vue | 6 +- frontend/src/pages/index/BackupModal.vue | 19 +- frontend/src/pages/index/CpuHistoryModal.vue | 18 +- .../src/pages/index/CustomGeoFormModal.vue | 19 +- frontend/src/pages/index/CustomGeoSection.vue | 62 ++-- frontend/src/pages/index/IndexPage.vue | 264 +++++++++++++++--- frontend/src/pages/index/LogModal.vue | 4 +- frontend/src/pages/index/PanelUpdateModal.vue | 35 ++- frontend/src/pages/index/StatusCard.vue | 40 +-- frontend/src/pages/index/VersionModal.vue | 28 +- frontend/src/pages/index/XrayLogModal.vue | 5 +- frontend/src/pages/index/XrayStatusCard.vue | 33 ++- frontend/src/pages/login/LoginPage.vue | 235 +++++++++------- frontend/src/pages/xray/useXraySetting.js | 21 +- frontend/src/settings.js | 1 + frontend/src/styles/legacy.css | 1 + frontend/src/xray.js | 1 + frontend/vite.config.js | 7 +- web/controller/dist.go | 30 +- web/controller/index.go | 17 ++ 26 files changed, 585 insertions(+), 290 deletions(-) create mode 100644 frontend/src/styles/legacy.css diff --git a/frontend/src/api/axios-init.js b/frontend/src/api/axios-init.js index a9cafc78..f651e82a 100644 --- a/frontend/src/api/axios-init.js +++ b/frontend/src/api/axios-init.js @@ -2,7 +2,10 @@ import axios from 'axios'; import qs from 'qs'; const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS', 'TRACE']); -const CSRF_TOKEN_PATH = '/panel/csrf-token'; +// Public CSRF endpoint — works pre-login (the panel-scoped +// /panel/csrf-token sits behind checkLogin and would 401 a fresh +// login page that hasn't authenticated yet). +const CSRF_TOKEN_PATH = '/csrf-token'; // Cached session CSRF token. The legacy panel injects it via a // tag rendered by Go; the new SPA pages @@ -79,7 +82,18 @@ export function setupAxios() { async (error) => { const status = error.response?.status; if (status === 401) { - window.location.reload(); + // 401 → session is gone. In production, the panel routes + // are gated by Go's checkLogin which redirects to base_path + // serving the login page; a reload is enough. In dev, Vite + // serves /index.html directly at "/", so a reload would put + // the user right back on the dashboard and the interceptor + // would loop. Navigate to the dev login entry instead. + if (import.meta.env.DEV) { + const basePath = window.__X_UI_BASE_PATH__ || '/'; + window.location.href = `${basePath}login.html`; + } else { + window.location.reload(); + } return Promise.reject(error); } // 403 with a stale/missing CSRF token: drop the cache, re-fetch, retry once. diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue index fb866d35..58b13c90 100644 --- a/frontend/src/components/AppSidebar.vue +++ b/frontend/src/components/AppSidebar.vue @@ -52,7 +52,7 @@ const tabs = computed(() => [ { key: `${prefix}panel/inbounds`, icon: 'user', title: t('menu.inbounds') }, { key: `${prefix}panel/settings`, icon: 'setting', title: t('menu.settings') }, { key: `${prefix}panel/xray`, icon: 'tool', title: t('menu.xray') }, - { key: `${prefix}logout/`, icon: 'logout', title: t('logout') }, + { key: `${prefix}logout`, icon: 'logout', title: t('logout') }, ]); const activeTab = ref([props.requestUri]); diff --git a/frontend/src/inbounds.js b/frontend/src/inbounds.js index 15b34d2e..ba90f0ed 100644 --- a/frontend/src/inbounds.js +++ b/frontend/src/inbounds.js @@ -1,6 +1,7 @@ import { createApp } from 'vue'; import Antd, { message } from 'ant-design-vue'; import 'ant-design-vue/dist/reset.css'; +import '@/styles/legacy.css'; import { setupAxios } from '@/api/axios-init.js'; import '@/composables/useTheme.js'; diff --git a/frontend/src/index.js b/frontend/src/index.js index 61d9dc12..b1712151 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,6 +1,11 @@ import { createApp } from 'vue'; import Antd, { message } from 'ant-design-vue'; import 'ant-design-vue/dist/reset.css'; +// Legacy panel CSS — overrides AD-Vue defaults to match the +// pre-migration look (palette, dark mode contrast, tag colors, +// table/tooltip styling). Loaded after AD-Vue's reset so its +// rules win. +import '@/styles/legacy.css'; import { setupAxios } from '@/api/axios-init.js'; // Importing useTheme triggers the boot side-effect that applies the diff --git a/frontend/src/login.js b/frontend/src/login.js index ec90a9fb..8ca1cc3d 100644 --- a/frontend/src/login.js +++ b/frontend/src/login.js @@ -1,6 +1,7 @@ import { createApp } from 'vue'; import Antd, { message } from 'ant-design-vue'; import 'ant-design-vue/dist/reset.css'; +import '@/styles/legacy.css'; import { setupAxios } from '@/api/axios-init.js'; // Importing this module triggers the boot side-effect that applies the diff --git a/frontend/src/models/inbound.js b/frontend/src/models/inbound.js index d2e2bac6..791885e9 100644 --- a/frontend/src/models/inbound.js +++ b/frontend/src/models/inbound.js @@ -1,4 +1,4 @@ -import { ObjectUtil, RandomUtil, Base64 } from '@/utils'; +import { ObjectUtil, RandomUtil, Base64, NumberFormatter, SizeFormatter, Wireguard } from '@/utils'; export const Protocols = { VMESS: 'vmess', diff --git a/frontend/src/pages/inbounds/InboundList.vue b/frontend/src/pages/inbounds/InboundList.vue index ea570a50..60760430 100644 --- a/frontend/src/pages/inbounds/InboundList.vue +++ b/frontend/src/pages/inbounds/InboundList.vue @@ -1,5 +1,5 @@