mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 13:34:13 +03:00
Compare commits
2 Commits
bbe18d5d6b
...
eefa532ecc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eefa532ecc | ||
|
|
c136e5e8bd |
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"documentTitle": "Classification Models",
|
"documentTitle": "Classification Models - Frigate",
|
||||||
"details": {
|
"details": {
|
||||||
"scoreInfo": "Score represents the average classification confidence across all detections of this object."
|
"scoreInfo": "Score represents the average classification confidence across all detections of this object."
|
||||||
},
|
},
|
||||||
|
|||||||
12
web/src/api/auth-redirect.ts
Normal file
12
web/src/api/auth-redirect.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Module-level flag to prevent multiple simultaneous redirects
|
||||||
|
// (eg, when multiple SWR queries fail with 401 at once, or when
|
||||||
|
// both ApiProvider and ProtectedRoute try to redirect)
|
||||||
|
let _isRedirectingToLogin = false;
|
||||||
|
|
||||||
|
export function isRedirectingToLogin(): boolean {
|
||||||
|
return _isRedirectingToLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setRedirectingToLogin(value: boolean): void {
|
||||||
|
_isRedirectingToLogin = value;
|
||||||
|
}
|
||||||
@ -3,13 +3,10 @@ import { SWRConfig } from "swr";
|
|||||||
import { WsProvider } from "./ws";
|
import { WsProvider } from "./ws";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
import { isRedirectingToLogin, setRedirectingToLogin } from "./auth-redirect";
|
||||||
|
|
||||||
axios.defaults.baseURL = `${baseUrl}api/`;
|
axios.defaults.baseURL = `${baseUrl}api/`;
|
||||||
|
|
||||||
// Module-level flag to prevent multiple simultaneous redirects
|
|
||||||
// (eg, when multiple SWR queries fail with 401 at once)
|
|
||||||
let isRedirectingToLogin = false;
|
|
||||||
|
|
||||||
type ApiProviderType = {
|
type ApiProviderType = {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
options?: Record<string, unknown>;
|
options?: Record<string, unknown>;
|
||||||
@ -35,8 +32,8 @@ export function ApiProvider({ children, options }: ApiProviderType) {
|
|||||||
) {
|
) {
|
||||||
// redirect to the login page if not already there
|
// redirect to the login page if not already there
|
||||||
const loginPage = error.response.headers.get("location") ?? "login";
|
const loginPage = error.response.headers.get("location") ?? "login";
|
||||||
if (window.location.href !== loginPage && !isRedirectingToLogin) {
|
if (window.location.href !== loginPage && !isRedirectingToLogin()) {
|
||||||
isRedirectingToLogin = true;
|
setRedirectingToLogin(true);
|
||||||
window.location.href = loginPage;
|
window.location.href = loginPage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import { useContext } from "react";
|
import { useContext, useEffect } from "react";
|
||||||
import { Navigate, Outlet } from "react-router-dom";
|
import { Navigate, Outlet } from "react-router-dom";
|
||||||
import { AuthContext } from "@/context/auth-context";
|
import { AuthContext } from "@/context/auth-context";
|
||||||
import ActivityIndicator from "../indicators/activity-indicator";
|
import ActivityIndicator from "../indicators/activity-indicator";
|
||||||
|
import {
|
||||||
|
isRedirectingToLogin,
|
||||||
|
setRedirectingToLogin,
|
||||||
|
} from "@/api/auth-redirect";
|
||||||
|
|
||||||
export default function ProtectedRoute({
|
export default function ProtectedRoute({
|
||||||
requiredRoles,
|
requiredRoles,
|
||||||
@ -10,6 +14,20 @@ export default function ProtectedRoute({
|
|||||||
}) {
|
}) {
|
||||||
const { auth } = useContext(AuthContext);
|
const { auth } = useContext(AuthContext);
|
||||||
|
|
||||||
|
// Redirect to login page when not authenticated
|
||||||
|
// don't use <Navigate> because we need a full page load to reset state
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!auth.isLoading &&
|
||||||
|
auth.isAuthenticated &&
|
||||||
|
!auth.user &&
|
||||||
|
!isRedirectingToLogin()
|
||||||
|
) {
|
||||||
|
setRedirectingToLogin(true);
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
}, [auth.isLoading, auth.isAuthenticated, auth.user]);
|
||||||
|
|
||||||
if (auth.isLoading) {
|
if (auth.isLoading) {
|
||||||
return (
|
return (
|
||||||
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
||||||
@ -23,7 +41,9 @@ export default function ProtectedRoute({
|
|||||||
|
|
||||||
// Authenticated mode (8971): require login
|
// Authenticated mode (8971): require login
|
||||||
if (!auth.user) {
|
if (!auth.user) {
|
||||||
return <Navigate to="/login" replace />;
|
return (
|
||||||
|
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If role is null (shouldn’t happen if isAuthenticated, but type safety), fallback
|
// If role is null (shouldn’t happen if isAuthenticated, but type safety), fallback
|
||||||
|
|||||||
@ -84,6 +84,12 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
|||||||
const [page, setPage] = useState<string>("train");
|
const [page, setPage] = useState<string>("train");
|
||||||
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
|
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
|
||||||
|
|
||||||
|
// title
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = `${model.name} - ${t("documentTitle")}`;
|
||||||
|
}, [model.name, t]);
|
||||||
|
|
||||||
// model state
|
// model state
|
||||||
|
|
||||||
const [wasTraining, setWasTraining] = useState(false);
|
const [wasTraining, setWasTraining] = useState(false);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user