refactor, security updates, cv extraction upgrades

This commit is contained in:
2026-04-11 01:34:32 +02:00
parent 806b200ac5
commit 27fd70a2d7
59 changed files with 6817 additions and 1561 deletions
+46 -6
View File
@@ -32,7 +32,7 @@ import ForgotPasswordPage from "./pages/ForgotPasswordPage";
import ResetPasswordPage from "./pages/ResetPasswordPage";
import RouteErrorPage from "./pages/RouteErrorPage";
import { api } from "./api";
import { clearAuthToken, getAuthToken } from "./auth";
import { clearAuthClientState, setAuthUserKey } from "./auth";
import AppShell, { NavItem } from "./layout/AppShell";
import { clearAccentColor, getAccentColor, getThemeModePref, setAccentColor, setThemeModePref, ThemeModePref } from "./themePrefs";
@@ -112,6 +112,7 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
const [quickOpen, setQuickOpen] = useState(false);
const [refreshToken, setRefreshToken] = useState(0);
const [requireAuth, setRequireAuth] = useState<boolean | null>(null);
const [authResolved, setAuthResolved] = useState(false);
const [isAdmin, setIsAdmin] = useState(false);
const [me, setMe] = useState<MeResponse | null>(null);
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
@@ -124,7 +125,26 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
api.get<AuthConfig>("/auth/config").then((r) => setRequireAuth(Boolean(r.data?.requireAuth))).catch(() => setRequireAuth(false));
}, []);
useEffect(() => {
api.get<MeResponse>("/auth/me").then((r) => { setMe(r.data); setIsAdmin(Boolean(r.data?.roles?.includes("Admin"))); }).catch(() => { setMe(null); setIsAdmin(false); });
let active = true;
api.get<MeResponse>("/auth/me")
.then((r) => {
if (!active) return;
setMe(r.data);
setIsAdmin(Boolean(r.data?.roles?.includes("Admin")));
setAuthUserKey(r.data?.id || r.data?.email || r.data?.userName || null, false);
})
.catch(() => {
if (!active) return;
setMe(null);
setIsAdmin(false);
clearAuthClientState(false);
})
.finally(() => {
if (active) setAuthResolved(true);
});
return () => {
active = false;
};
}, []);
useEffect(() => {
const load = () => {
@@ -134,6 +154,27 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
const id = window.setInterval(load, 60000);
return () => window.clearInterval(id);
}, []);
useEffect(() => {
const onAuthChanged = () => {
setAuthResolved(false);
api.get<MeResponse>("/auth/me")
.then((r) => {
setMe(r.data);
setIsAdmin(Boolean(r.data?.roles?.includes("Admin")));
setAuthUserKey(r.data?.id || r.data?.email || r.data?.userName || null, false);
})
.catch(() => {
setMe(null);
setIsAdmin(false);
clearAuthClientState(false);
})
.finally(() => setAuthResolved(true));
};
window.addEventListener("auth-changed", onAuthChanged);
return () => window.removeEventListener("auth-changed", onAuthChanged);
}, []);
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
@@ -145,9 +186,8 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
return () => window.removeEventListener("keydown", onKeyDown);
}, []);
const token = getAuthToken();
if (requireAuth === null) return <Box sx={{ p: 4 }}><Typography variant="h6">Loading...</Typography></Box>;
if (requireAuth && !token) return <Navigate to="/login" replace state={{ from: path }} />;
if (requireAuth === null || !authResolved) return <Box sx={{ p: 4 }}><Typography variant="h6">Loading...</Typography></Box>;
if (requireAuth && !me) return <Navigate to="/login" replace state={{ from: path }} />;
const pageTitle = titleFor(path, t);
const breadcrumbs = breadcrumbsFor(path, t);
@@ -223,7 +263,7 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo
onOpenNotifications={() => navigate("/reminders")}
onOpenSettings={() => navigate("/settings")}
onOpenProfile={() => navigate("/profile")}
onSignOut={() => { clearAuthToken(); navigate("/login"); }}
onSignOut={() => { void api.post("/auth/logout").catch(() => undefined).finally(() => { clearAuthClientState(); navigate("/login"); }); }}
rightActions={rightActions}
>
<Suspense fallback={<PageLoader />}>