mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
Replaces the 7-entry MPA shell (index/clients/inbounds/nodes/settings/ xray/api-docs HTML files) with one main.tsx + createBrowserRouter. The Go backend now serves the same index.html for every authenticated panel route; React Router reads the URL and mounts the page from cache on subsequent navigation — no more full reloads between tabs. Frontend - main.tsx: single bootstrap (setupAxios, i18n, ThemeProvider, QueryProvider, RouterProvider) replacing 7 near-duplicate entries - routes.tsx: declarative router with lazy()-loaded pages, basename derived from window.X_UI_BASE_PATH so panels at /secret/panel work - layouts/PanelLayout.tsx: shell mount-point for the WS → queryClient bridge so connection survives navigation - api/websocketBridge.ts: subscribes the singleton WebSocketClient to queryClient and dispatches invalidate/outbounds events to cached queries (page-level useWebSocket handlers stay until Phase 3 hooks migrate) - AppSidebar: navigates via useNavigate + useLocation instead of window.location.href; drops basePath/requestUri props - Pages: drop the unused basePath/requestUri locals exposed only for the old sidebar Build - vite.config: 9 rollup inputs → 3 (index, login, subpage). Dev proxy bypass collapses /panel/* to index.html and skips API prefixes - vendor-tanstack + vendor-router chunks added to manualChunks Backend - xui.go: 7 per-page HTML handlers → one panelSPA handler serving index.html for /, /inbounds, /clients, /nodes, /settings, /xray, /api-docs. The /panel/api, /panel/setting, /panel/xray sub-routers are untouched
42 lines
1.6 KiB
TypeScript
42 lines
1.6 KiB
TypeScript
import { lazy, Suspense } from 'react';
|
|
import { createBrowserRouter, type RouteObject } from 'react-router-dom';
|
|
|
|
import PanelLayout from '@/layouts/PanelLayout';
|
|
|
|
const IndexPage = lazy(() => import('@/pages/index/IndexPage'));
|
|
const InboundsPage = lazy(() => import('@/pages/inbounds/InboundsPage'));
|
|
const ClientsPage = lazy(() => import('@/pages/clients/ClientsPage'));
|
|
const NodesPage = lazy(() => import('@/pages/nodes/NodesPage'));
|
|
const SettingsPage = lazy(() => import('@/pages/settings/SettingsPage'));
|
|
const XrayPage = lazy(() => import('@/pages/xray/XrayPage'));
|
|
const ApiDocsPage = lazy(() => import('@/pages/api-docs/ApiDocsPage'));
|
|
|
|
function withSuspense(node: React.ReactNode) {
|
|
return <Suspense fallback={null}>{node}</Suspense>;
|
|
}
|
|
|
|
const routes: RouteObject[] = [
|
|
{
|
|
path: '/',
|
|
element: <PanelLayout />,
|
|
children: [
|
|
{ index: true, element: withSuspense(<IndexPage />) },
|
|
{ path: 'inbounds', element: withSuspense(<InboundsPage />) },
|
|
{ path: 'clients', element: withSuspense(<ClientsPage />) },
|
|
{ path: 'nodes', element: withSuspense(<NodesPage />) },
|
|
{ path: 'settings', element: withSuspense(<SettingsPage />) },
|
|
{ path: 'xray', element: withSuspense(<XrayPage />) },
|
|
{ path: 'api-docs', element: withSuspense(<ApiDocsPage />) },
|
|
],
|
|
},
|
|
];
|
|
|
|
function computeBasename() {
|
|
const raw = (typeof window !== 'undefined' && window.X_UI_BASE_PATH) || '/';
|
|
const trimmed = raw.replace(/\/+$/, '');
|
|
return `${trimmed}/panel`;
|
|
}
|
|
|
|
export const router = createBrowserRouter(routes, {
|
|
basename: computeBasename(),
|
|
});
|