import { createRouter, createWebHashHistory } from 'vue-router' import { useAuthStore } from '@/stores/auth' const routes = [ { path: '/', name: 'Dashboard', component: () => import('@/pages/DashboardPage.vue') }, { path: '/servers', name: 'Servers', component: () => import('@/pages/ServersPage.vue') }, { path: '/terminal', name: 'Terminal', component: () => import('@/pages/TerminalPage.vue') }, { path: '/browser', name: 'Browser', component: () => import('@/pages/BrowserPage.vue') }, { path: '/files', name: 'Files', component: () => import('@/pages/FilesPage.vue') }, { path: '/push', name: 'Push', component: () => import('@/pages/PushPage.vue') }, { path: '/scripts', name: 'Scripts', component: () => import('@/pages/ScriptsPage.vue') }, { path: '/script-runs', name: 'ScriptRuns', component: () => import('@/pages/ScriptRunsPage.vue') }, { path: '/script-runs/:id', name: 'ScriptRunDetail', component: () => import('@/pages/ScriptRunsPage.vue') }, { path: '/executions', name: 'Executions', component: () => import('@/pages/ScriptRunsPage.vue') }, { path: '/executions/:id', name: 'ExecutionDetail', component: () => import('@/pages/ScriptRunsPage.vue') }, { path: '/credentials', name: 'Credentials',component: () => import('@/pages/CredentialsPage.vue') }, { path: '/schedules', name: 'Schedules', component: () => import('@/pages/SchedulesPage.vue') }, { path: '/retries', name: 'Retries', component: () => import('@/pages/RetriesPage.vue') }, { path: '/commands', name: 'Commands', component: () => import('@/pages/CommandsPage.vue') }, { path: '/alerts', name: 'Alerts', component: () => import('@/pages/AlertsPage.vue') }, { path: '/audit', name: 'Audit', component: () => import('@/pages/AuditPage.vue') }, { path: '/settings', name: 'Settings', component: () => import('@/pages/SettingsPage.vue') }, { path: '/login', name: 'Login', component: () => import('@/pages/LoginPage.vue'), meta: { public: true } }, ] const router = createRouter({ history: createWebHashHistory(), routes }) const CHUNK_RELOAD_FLAG = 'nexus_chunk_reload_v1' function isChunkLoadError(message: string): boolean { return ( message.includes('Failed to fetch dynamically imported module') || message.includes('Importing a module script failed') || message.includes('error loading dynamically imported module') ) } /** 懒加载 chunk 缺失时静默 reload 一次(部署后旧标签页兜底);登录由 refresh cookie 恢复 */ router.onError((err, to) => { const msg = err?.message || '' if (!isChunkLoadError(msg)) return const w = window as Window & { $snackbar?: (text: string, color?: string) => void } if (!sessionStorage.getItem(CHUNK_RELOAD_FLAG)) { sessionStorage.setItem(CHUNK_RELOAD_FLAG, '1') window.location.reload() return } w.$snackbar?.( `页面「${String(to.name || to.path)}」资源已更新,请关闭此标签后重新打开后台`, 'error', ) }) router.afterEach(() => { sessionStorage.removeItem(CHUNK_RELOAD_FLAG) }) // Auth guard — restore session from HttpOnly refresh cookie when needed router.beforeEach(async (to) => { const auth = useAuthStore() if (!auth.bootstrapped) { await auth.tryRestoreSession() } if (!to.meta.public && !auth.isLoggedIn) { return { name: 'Login', query: { redirect: to.fullPath } } } if (to.name === 'Login' && auth.isLoggedIn) { return { name: 'Dashboard' } } }) export default router