Spaces:
Sleeping
Sleeping
File size: 5,127 Bytes
0ff9b81 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
import React, { useEffect } from "react";
import { Routes, Route, Navigate, useLocation } from "react-router-dom";
import { useStore } from "./state/store";
import { Sidebar } from "./components/Sidebar";
import { Topbar } from "./components/Topbar";
import { Orbit } from "./components/Orbit";
// Pages
import Login from "./pages/user/Login";
import Signup from "./pages/user/Signup";
import Main from "./pages/user/Main";
import Chat from "./pages/user/Chat";
import History from "./pages/user/History";
import ApiKeys from "./pages/user/ApiKeys";
import HowTo from "./pages/user/HowTo";
import About from "./pages/user/About";
import Settings from "./pages/user/Settings";
// Admin pages
import Monitor from "./pages/admin/Monitor";
import Knowledge from "./pages/admin/Knowledge";
import Skills from "./pages/admin/Skills";
import VFS from "./pages/admin/VFS";
import Verify from "./pages/admin/Verify";
import Logs from "./pages/admin/Logs";
import Users from "./pages/admin/Users";
import Connections from "./pages/admin/Connections";
// Landing
import Landing from "./pages/Landing";
// Backend API
const API_BASE = "https://princeaka-multimodal-module.hf.space";
export async function apiFetch(endpoint, method = "GET", body = null, token = null) {
const headers = { "Content-Type": "application/json" };
if (token) headers["Authorization"] = `Bearer ${token}`;
const res = await fetch(`${API_BASE}${endpoint}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
const errText = await res.text();
throw new Error(`API ${res.status}: ${errText}`);
}
return res.json();
}
// β
Protected route wrapper
function Protected({ children, role }) {
const { auth } = useStore();
if (!auth.token) return <Navigate to="/login" replace />;
if (role && auth.role !== role) return <Navigate to="/" replace />;
return children;
}
export default function App() {
const { ui, setUi } = useStore();
const loc = useLocation();
const isAuthPage = loc.pathname === "/login" || loc.pathname === "/signup";
// β
Auto-clear toast after 4s
useEffect(() => {
if (ui.toast) {
const timer = setTimeout(() => setUi(u => ({ ...u, toast: null })), 4000);
return () => clearTimeout(timer);
}
}, [ui.toast, setUi]);
return (
<div style={{ height: "100%" }}>
<Routes>
{/* Landing page */}
<Route path="/landing" element={<Landing />} />
{/* Auth pages */}
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
{/* Protected app pages */}
<Route
path="/*"
element={
!isAuthPage ? (
<div className="app">
<Sidebar />
<div className="main">
<header className="topbar">
<Topbar />
</header>
<div className="content">
<Routes>
<Route path="/" element={<Protected><Main /></Protected>} />
<Route path="/chat" element={<Protected><Chat /></Protected>} />
<Route path="/history" element={<Protected><History /></Protected>} />
<Route path="/apikeys" element={<Protected><ApiKeys /></Protected>} />
<Route path="/howto" element={<Protected><HowTo /></Protected>} />
<Route path="/about" element={<Protected><About /></Protected>} />
<Route path="/settings" element={<Protected><Settings /></Protected>} />
{/* Admin routes */}
<Route path="/admin/monitor" element={<Protected role="admin"><Monitor /></Protected>} />
<Route path="/admin/knowledge" element={<Protected role="admin"><Knowledge /></Protected>} />
<Route path="/admin/skills" element={<Protected role="admin"><Skills /></Protected>} />
<Route path="/admin/vfs" element={<Protected role="admin"><VFS /></Protected>} />
<Route path="/admin/verify" element={<Protected role="admin"><Verify /></Protected>} />
<Route path="/admin/logs" element={<Protected role="admin"><Logs /></Protected>} />
<Route path="/admin/users" element={<Protected role="admin"><Users /></Protected>} />
<Route path="/admin/connections" element={<Protected role="admin"><Connections /></Protected>} />
</Routes>
</div>
</div>
<Orbit />
</div>
) : (
<Navigate to="/landing" replace />
)
}
/>
{/* Default redirect */}
<Route path="*" element={<Navigate to="/landing" replace />} />
</Routes>
{/* β
Toast */}
{ui.toast && (
<div className="toast fixed bottom-4 right-4 bg-black text-white px-4 py-2 rounded shadow-lg z-50">
{ui.toast}
</div>
)}
</div>
);
}
|