Refactor UUID generation, remove unused logger and encryption utilities, and clean up request handling
- Updated `generateUUID` function for improved readability and performance. - Deleted `logger.ts`, `other.ts`, `request.ts`, `storage.ts`, `tansParams.ts`, and `validate.ts` as they were no longer needed. - Simplified TypeScript configuration by removing unnecessary paths and aliases. - Enhanced Vite configuration for better project structure and maintainability.
This commit is contained in:
101
src/router/auth-session.ts
Normal file
101
src/router/auth-session.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
export const TOKEN_STORAGE_KEYS = ['token', 'access_token', 'refresh_token'] as const;
|
||||
|
||||
const AUTH_LOGOUT_EVENT = 'zn:auth-logout';
|
||||
|
||||
export type AuthLogoutDetail = {
|
||||
from?: string;
|
||||
reason?: 'manual' | 'unauthorized';
|
||||
};
|
||||
|
||||
function normalizeStoredToken(value: string | null): string | null {
|
||||
if (!value) return null;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(value) as unknown;
|
||||
return typeof parsed === 'string' ? parsed : value;
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function readCookie(name: string): string | null {
|
||||
if (typeof document === 'undefined' || !document.cookie) return null;
|
||||
|
||||
const match = document.cookie.match(new RegExp(`(?:^|; )${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}=([^;]*)`));
|
||||
return match ? decodeURIComponent(match[1]) : null;
|
||||
}
|
||||
|
||||
export function readPersistedAuthToken(): string | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
|
||||
const storageCandidates = [window.sessionStorage, window.localStorage];
|
||||
for (const storage of storageCandidates) {
|
||||
for (const key of TOKEN_STORAGE_KEYS) {
|
||||
const value = normalizeStoredToken(storage.getItem(key));
|
||||
if (value) return value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of TOKEN_STORAGE_KEYS) {
|
||||
const value = normalizeStoredToken(readCookie(key));
|
||||
if (value) return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function persistAuthTokens(accessToken: string, refreshToken?: string | null): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
window.sessionStorage.setItem('token', JSON.stringify(accessToken));
|
||||
Cookies.set('token', accessToken);
|
||||
|
||||
if (refreshToken) {
|
||||
window.sessionStorage.setItem('refresh_token', JSON.stringify(refreshToken));
|
||||
Cookies.set('refresh_token', refreshToken);
|
||||
return;
|
||||
}
|
||||
|
||||
window.sessionStorage.removeItem('refresh_token');
|
||||
window.localStorage.removeItem('refresh_token');
|
||||
Cookies.remove('refresh_token');
|
||||
}
|
||||
|
||||
export function clearPersistedAuth(): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
const storageCandidates = [window.sessionStorage, window.localStorage];
|
||||
for (const storage of storageCandidates) {
|
||||
for (const key of TOKEN_STORAGE_KEYS) {
|
||||
storage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of TOKEN_STORAGE_KEYS) {
|
||||
Cookies.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
export function logout(detail: AuthLogoutDetail = {}): void {
|
||||
clearPersistedAuth();
|
||||
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
window.dispatchEvent(new CustomEvent<AuthLogoutDetail>(AUTH_LOGOUT_EVENT, { detail }));
|
||||
}
|
||||
|
||||
export function onAuthLogout(callback: (detail: AuthLogoutDetail) => void): () => void {
|
||||
if (typeof window === 'undefined') {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const listener = (event: Event) => {
|
||||
const customEvent = event as CustomEvent<AuthLogoutDetail>;
|
||||
callback(customEvent.detail ?? {});
|
||||
};
|
||||
|
||||
window.addEventListener(AUTH_LOGOUT_EVENT, listener);
|
||||
return () => window.removeEventListener(AUTH_LOGOUT_EVENT, listener);
|
||||
}
|
||||
42
src/router/auth.tsx
Normal file
42
src/router/auth.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Navigate, Outlet, useLocation } from 'react-router-dom';
|
||||
import { DEFAULT_PATH, normalizeWorkspacePath, type WorkspacePath } from './routes';
|
||||
import { clearPersistedAuth, logout, readPersistedAuthToken } from './auth-session';
|
||||
|
||||
type AuthRedirectState = {
|
||||
from?: string;
|
||||
};
|
||||
|
||||
export function isAuthenticated(): boolean {
|
||||
return Boolean(readPersistedAuthToken());
|
||||
}
|
||||
|
||||
export { clearPersistedAuth, logout, readPersistedAuthToken };
|
||||
|
||||
export function resolveWorkspacePath(pathname?: string | null): WorkspacePath {
|
||||
if (!pathname) return DEFAULT_PATH;
|
||||
return normalizeWorkspacePath(pathname);
|
||||
}
|
||||
|
||||
export function resolvePostLoginPath(state?: AuthRedirectState | null): WorkspacePath {
|
||||
return resolveWorkspacePath(state?.from);
|
||||
}
|
||||
|
||||
export function RequireAuth() {
|
||||
const location = useLocation();
|
||||
|
||||
if (!isAuthenticated()) {
|
||||
return <Navigate to="/login" replace state={{ from: location.pathname } satisfies AuthRedirectState} />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
export function RedirectAuthenticated() {
|
||||
const location = useLocation();
|
||||
|
||||
if (isAuthenticated()) {
|
||||
return <Navigate to={resolvePostLoginPath(location.state as AuthRedirectState | null)} replace />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { createRouter, createMemoryHistory } from "vue-router";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/home'
|
||||
},
|
||||
{
|
||||
path: "/login",
|
||||
component: () => import("@src/pages/login/index.vue"),
|
||||
name: "Login",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/home",
|
||||
component: () => import("@src/pages/home/index.vue"),
|
||||
name: "Home",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/agents",
|
||||
component: () => import("@src/pages/agents/index.vue"),
|
||||
name: "Agents",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/knowledge",
|
||||
component: () => import("@src/pages/knowledge/index.vue"),
|
||||
name: "Knowledge",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/skills",
|
||||
component: () => import("@src/pages/skills/index.vue"),
|
||||
name: "Skills",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/cron",
|
||||
component: () => import("@src/pages/cron/index.vue"),
|
||||
name: "Cron",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/scripts",
|
||||
component: () => import("@src/pages/scripts/index.vue"),
|
||||
name: "Scripts",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/setting",
|
||||
component: () => import("@src/pages/setting/index.vue"),
|
||||
name: "Setting",
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createMemoryHistory(),
|
||||
routes,
|
||||
scrollBehavior(_to: any, _from: any, savedPosition: any) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
}
|
||||
|
||||
return { top: 0 }
|
||||
},
|
||||
});
|
||||
|
||||
export default router;
|
||||
61
src/router/index.tsx
Normal file
61
src/router/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||
import MainLayout from '../components/layout/MainLayout';
|
||||
import HomePage from '../pages/Home';
|
||||
import LoginPage from '../pages/Login';
|
||||
import AgentsPage from '../pages/Agents';
|
||||
import SkillsPage from '../pages/Skills';
|
||||
import CronPage from '../pages/Cron';
|
||||
import ScriptsPage from '../pages/Scripts';
|
||||
import SettingPage from '../pages/Setting';
|
||||
import KnowledgePage from '../pages/Knowledge';
|
||||
import { DEFAULT_PATH } from './routes';
|
||||
import { RedirectAuthenticated, RequireAuth, isAuthenticated } from './auth';
|
||||
import { onAuthLogout } from './auth-session';
|
||||
|
||||
function AuthLogoutRedirector() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
return onAuthLogout(({ from }) => {
|
||||
const currentPath = location.pathname === '/login' ? undefined : location.pathname;
|
||||
navigate('/login', {
|
||||
replace: true,
|
||||
state: { from: from ?? currentPath },
|
||||
});
|
||||
});
|
||||
}, [location.pathname, navigate]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function AppRouter() {
|
||||
const initialPath = isAuthenticated() ? DEFAULT_PATH : '/login';
|
||||
|
||||
return (
|
||||
<>
|
||||
<AuthLogoutRedirector />
|
||||
<Routes>
|
||||
<Route path="/" element={<Navigate to={initialPath} replace />} />
|
||||
<Route element={<RedirectAuthenticated />}>
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
</Route>
|
||||
|
||||
<Route element={<RequireAuth />}>
|
||||
<Route element={<MainLayout />}>
|
||||
<Route path="/home" element={<HomePage />} />
|
||||
<Route path="/agents" element={<AgentsPage />} />
|
||||
<Route path="/skills" element={<SkillsPage />} />
|
||||
<Route path="/cron" element={<CronPage />} />
|
||||
<Route path="/scripts" element={<ScriptsPage />} />
|
||||
<Route path="/setting" element={<SettingPage />} />
|
||||
<Route path="/knowledge" element={<KnowledgePage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
<Route path="*" element={<Navigate to={initialPath} replace />} />
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
}
|
||||
44
src/router/routes.ts
Normal file
44
src/router/routes.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export type AppPath =
|
||||
| '/home'
|
||||
| '/agents'
|
||||
| '/skills'
|
||||
| '/cron'
|
||||
| '/scripts'
|
||||
| '/setting'
|
||||
| '/knowledge'
|
||||
| '/login';
|
||||
|
||||
export type WorkspacePath = Exclude<AppPath, '/login'>;
|
||||
|
||||
export type NavItem = {
|
||||
path: WorkspacePath;
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export const DEFAULT_PATH: WorkspacePath = '/home';
|
||||
|
||||
export const NAV_ITEMS: NavItem[] = [
|
||||
{ path: '/home', label: '首页', description: '对话与首页入口' },
|
||||
{ path: '/knowledge', label: '知识库', description: '知识库与内容管理' },
|
||||
{ path: '/agents', label: '模型', description: '智能体与提供方' },
|
||||
{ path: '/skills', label: '技能', description: '技能与能力集' },
|
||||
{ path: '/cron', label: '定时任务', description: '计划与调度' },
|
||||
{ path: '/scripts', label: '脚本', description: '脚本与自动化' },
|
||||
{ path: '/setting', label: '设置', description: '应用配置' },
|
||||
];
|
||||
|
||||
export function normalizeWorkspacePath(pathname: string): WorkspacePath {
|
||||
switch (pathname) {
|
||||
case '/knowledge':
|
||||
case '/agents':
|
||||
case '/skills':
|
||||
case '/cron':
|
||||
case '/scripts':
|
||||
case '/setting':
|
||||
return pathname;
|
||||
case '/home':
|
||||
default:
|
||||
return DEFAULT_PATH;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user