import { BrowserRouter, Routes, Route, Navigate, NavLink, Outlet, useNavigate, } from 'react-router-dom'; import { useState, useEffect } from 'react'; import { Toaster } from 'sonner'; import { Database, LayoutDashboard, Server, GitCompare, History, Key, Settings as SettingsIcon, LogOut, Menu, X, } from 'lucide-react'; import { ErrorBoundary } from './components/ErrorBoundary'; import { Spinner } from './components/ui/Spinner'; import { Button } from './components/ui/Button'; import { cn } from './lib/utils'; import Login from './pages/Login'; import Dashboard from './pages/Dashboard'; import Machines from './pages/Machines'; import SyncPairs from './pages/SyncPairs'; import JobHistory from './pages/JobHistory'; import JobDetail from './pages/JobDetail'; import SettingsPage from './pages/Settings'; import SSHKeys from './pages/SSHKeys'; const navItems = [ { to: '/', label: 'Dashboard', icon: LayoutDashboard }, { to: '/machines', label: 'Machines', icon: Server }, { to: '/sync-pairs', label: 'Sync Pairs', icon: GitCompare }, { to: '/jobs', label: 'Jobs', icon: History }, { to: '/ssh-keys', label: 'SSH Keys', icon: Key }, { to: '/settings', label: 'Settings', icon: SettingsIcon }, ]; function ProtectedRoute({ children }: { children: JSX.Element }) { const [authed, setAuthed] = useState(null); useEffect(() => { fetch('/api/auth/me', { credentials: 'include' }) .then(r => setAuthed(r.ok)) .catch(() => setAuthed(false)); }, []); if (authed === null) { return (
); } return authed ? children : ; } function NavBar() { const [mobileOpen, setMobileOpen] = useState(false); const navigate = useNavigate(); const handleLogout = async () => { try { await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }); } catch {} localStorage.removeItem('auth'); navigate('/login'); }; return (
SyncServer
{mobileOpen && ( )}
); } function Layout() { return (
Skip to content
); } export default function App() { return ( } /> } > } /> } /> } /> } /> } /> } /> } /> } /> ); }