mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 21:44:13 +03:00
* update config for roles and add validator * ensure admin and viewer are never overridden * add class method to user to retrieve all allowed cameras * enforce config roles in auth api endpoints * add camera access api dependency functions * protect review endpoints * protect preview endpoints * rename param name for better fastapi injection matching * remove unneeded * protect export endpoints * protect event endpoints * protect media endpoints * update auth hook for allowed cameras * update default app view * ensure anonymous user always returns all cameras * limit cameras in explore * cameras is already a list * limit cameras in review/history * limit cameras in live view * limit cameras in camera groups * only show face library and classification in sidebar for admin * remove check in delete reviews since admin role is required, no need to check camera access. fixes failing test * pass request with camera access for tests * more async * camera access tests * fix proxy auth tests * allowed cameras for review tests * combine event tests and refactor for camera access * fix post validation for roles * don't limit roles in create user dialog * fix triggers endpoints no need to run require camera access dep since the required role is admin * fix type * create and edit role dialogs * delete role dialog * fix role change dialog * update settings view for roles * i18n changes * minor spacing tweaks * docs * use badges and camera name label component * clarify docs * display all cameras badge for admin and viewer * i18n fix * use validator to prevent reserved and empty roles from being assigned * split users and roles into separate tabs in settings * tweak docs * clarify docs * change icon * don't memoize roles always recalculate on component render
119 lines
4.0 KiB
TypeScript
119 lines
4.0 KiB
TypeScript
import Providers from "@/context/providers";
|
|
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
|
import Wrapper from "@/components/Wrapper";
|
|
import Sidebar from "@/components/navigation/Sidebar";
|
|
|
|
import { isDesktop, isMobile } from "react-device-detect";
|
|
import Statusbar from "./components/Statusbar";
|
|
import Bottombar from "./components/navigation/Bottombar";
|
|
import { Suspense, lazy } from "react";
|
|
import { Redirect } from "./components/navigation/Redirect";
|
|
import { cn } from "./lib/utils";
|
|
import { isPWA } from "./utils/isPWA";
|
|
import ProtectedRoute from "@/components/auth/ProtectedRoute";
|
|
import { AuthProvider } from "@/context/auth-context";
|
|
import useSWR from "swr";
|
|
import { FrigateConfig } from "./types/frigateConfig";
|
|
|
|
const Live = lazy(() => import("@/pages/Live"));
|
|
const Events = lazy(() => import("@/pages/Events"));
|
|
const Explore = lazy(() => import("@/pages/Explore"));
|
|
const Exports = lazy(() => import("@/pages/Exports"));
|
|
const ConfigEditor = lazy(() => import("@/pages/ConfigEditor"));
|
|
const System = lazy(() => import("@/pages/System"));
|
|
const Settings = lazy(() => import("@/pages/Settings"));
|
|
const UIPlayground = lazy(() => import("@/pages/UIPlayground"));
|
|
const FaceLibrary = lazy(() => import("@/pages/FaceLibrary"));
|
|
const Classification = lazy(() => import("@/pages/ClassificationModel"));
|
|
const Logs = lazy(() => import("@/pages/Logs"));
|
|
const AccessDenied = lazy(() => import("@/pages/AccessDenied"));
|
|
|
|
function App() {
|
|
const { data: config } = useSWR<FrigateConfig>("config", {
|
|
revalidateOnFocus: false,
|
|
});
|
|
|
|
return (
|
|
<Providers>
|
|
<AuthProvider>
|
|
<BrowserRouter basename={window.baseUrl}>
|
|
<Wrapper>
|
|
{config?.safe_mode ? <SafeAppView /> : <DefaultAppView />}
|
|
</Wrapper>
|
|
</BrowserRouter>
|
|
</AuthProvider>
|
|
</Providers>
|
|
);
|
|
}
|
|
|
|
function DefaultAppView() {
|
|
const { data: config } = useSWR<FrigateConfig>("config", {
|
|
revalidateOnFocus: false,
|
|
});
|
|
return (
|
|
<div className="size-full overflow-hidden">
|
|
{isDesktop && <Sidebar />}
|
|
{isDesktop && <Statusbar />}
|
|
{isMobile && <Bottombar />}
|
|
<div
|
|
id="pageRoot"
|
|
className={cn(
|
|
"absolute right-0 top-0 overflow-hidden",
|
|
isMobile
|
|
? `bottom-${isPWA ? 16 : 12} left-0 md:bottom-16 landscape:bottom-14 landscape:md:bottom-16`
|
|
: "bottom-8 left-[52px]",
|
|
)}
|
|
>
|
|
<Suspense>
|
|
<Routes>
|
|
<Route
|
|
element={
|
|
<ProtectedRoute
|
|
requiredRoles={
|
|
config?.auth.roles
|
|
? Object.keys(config.auth.roles)
|
|
: ["admin", "viewer"]
|
|
}
|
|
/>
|
|
}
|
|
>
|
|
<Route index element={<Live />} />
|
|
<Route path="/review" element={<Events />} />
|
|
<Route path="/explore" element={<Explore />} />
|
|
<Route path="/export" element={<Exports />} />
|
|
<Route path="/settings" element={<Settings />} />
|
|
</Route>
|
|
<Route element={<ProtectedRoute requiredRoles={["admin"]} />}>
|
|
<Route path="/system" element={<System />} />
|
|
<Route path="/config" element={<ConfigEditor />} />
|
|
<Route path="/logs" element={<Logs />} />
|
|
<Route path="/faces" element={<FaceLibrary />} />
|
|
<Route path="/classification" element={<Classification />} />
|
|
<Route path="/playground" element={<UIPlayground />} />
|
|
</Route>
|
|
<Route path="/unauthorized" element={<AccessDenied />} />
|
|
<Route path="*" element={<Redirect to="/" />} />
|
|
</Routes>
|
|
</Suspense>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function SafeAppView() {
|
|
return (
|
|
<div className="size-full overflow-hidden">
|
|
<div
|
|
id="pageRoot"
|
|
className={cn("absolute bottom-0 left-0 right-0 top-0 overflow-hidden")}
|
|
>
|
|
<Suspense>
|
|
<ConfigEditor />
|
|
</Suspense>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|