mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 22:16:41 +00:00
Add session-based username/password auth for CCS dashboard: - Backend: Express session middleware with bcrypt password verification - Frontend: React AuthContext, login page, protected routes - Config: dashboard_auth section in config.yaml + env var overrides - Security: Rate limiting (5 attempts/15min), persistent session secret - 16 unit tests for auth middleware Auth disabled by default for backward compatibility.
145 lines
5.3 KiB
TypeScript
145 lines
5.3 KiB
TypeScript
import { lazy, Suspense } from 'react';
|
|
import { BrowserRouter, Routes, Route } from 'react-router-dom';
|
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
import { Toaster } from 'sonner';
|
|
import { queryClient } from '@/lib/query-client';
|
|
import { ThemeProvider } from '@/components/layout/theme-provider';
|
|
import { PrivacyProvider } from '@/contexts/privacy-context';
|
|
import { AuthProvider } from '@/contexts/auth-context';
|
|
import { RequireAuth } from '@/components/auth/require-auth';
|
|
import { Layout } from '@/components/layout/layout';
|
|
import { Loader2 } from 'lucide-react';
|
|
|
|
// Eager load: HomePage (initial route) + LoginPage (auth flow)
|
|
import { HomePage } from '@/pages';
|
|
import { LoginPage } from '@/pages/login';
|
|
|
|
// Lazy load: heavy pages with charts or complex dependencies
|
|
const AnalyticsPage = lazy(() =>
|
|
import('@/pages/analytics').then((m) => ({ default: m.AnalyticsPage }))
|
|
);
|
|
const ApiPage = lazy(() => import('@/pages/api').then((m) => ({ default: m.ApiPage })));
|
|
const CliproxyPage = lazy(() =>
|
|
import('@/pages/cliproxy').then((m) => ({ default: m.CliproxyPage }))
|
|
);
|
|
const CliproxyControlPanelPage = lazy(() =>
|
|
import('@/pages/cliproxy-control-panel').then((m) => ({ default: m.CliproxyControlPanelPage }))
|
|
);
|
|
const CopilotPage = lazy(() => import('@/pages/copilot').then((m) => ({ default: m.CopilotPage })));
|
|
const AccountsPage = lazy(() =>
|
|
import('@/pages/accounts').then((m) => ({ default: m.AccountsPage }))
|
|
);
|
|
const SettingsPage = lazy(() =>
|
|
import('@/pages/settings').then((m) => ({ default: m.SettingsPage }))
|
|
);
|
|
const HealthPage = lazy(() => import('@/pages/health').then((m) => ({ default: m.HealthPage })));
|
|
const SharedPage = lazy(() => import('@/pages/shared').then((m) => ({ default: m.SharedPage })));
|
|
|
|
// Loading fallback for lazy components
|
|
function PageLoader() {
|
|
return (
|
|
<div className="flex h-64 items-center justify-center">
|
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
<ThemeProvider defaultTheme="system" storageKey="vite-ui-theme">
|
|
<PrivacyProvider>
|
|
<AuthProvider>
|
|
<BrowserRouter>
|
|
<Routes>
|
|
{/* Public route: Login page */}
|
|
<Route path="/login" element={<LoginPage />} />
|
|
|
|
{/* Protected routes: wrapped with RequireAuth */}
|
|
<Route element={<RequireAuth />}>
|
|
<Route element={<Layout />}>
|
|
<Route path="/" element={<HomePage />} />
|
|
<Route
|
|
path="/analytics"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<AnalyticsPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/providers"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<ApiPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/cliproxy"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<CliproxyPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/cliproxy/control-panel"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<CliproxyControlPanelPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/copilot"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<CopilotPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/accounts"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<AccountsPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/settings"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<SettingsPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/health"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<HealthPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/shared"
|
|
element={
|
|
<Suspense fallback={<PageLoader />}>
|
|
<SharedPage />
|
|
</Suspense>
|
|
}
|
|
/>
|
|
</Route>
|
|
</Route>
|
|
</Routes>
|
|
<Toaster position="top-right" />
|
|
</BrowserRouter>
|
|
</AuthProvider>
|
|
</PrivacyProvider>
|
|
</ThemeProvider>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|