72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import { usePathname } from "next/navigation";
|
|
import { useEffect, useState, type ReactNode } from "react";
|
|
import { authFetch, getApiBase, getToken } from "../lib/auth";
|
|
import { canAccess, featureForPath, type FeatureAccess } from "../lib/features";
|
|
import { useEffectiveRole } from "../lib/viewMode";
|
|
import { isAdminPage } from "../lib/user-view-policy";
|
|
|
|
export function useFeatureUser() {
|
|
const pathname = usePathname();
|
|
const [state, setState] = useState<{
|
|
path: string;
|
|
user: { role?: string; features?: FeatureAccess; invite_management_enabled?: boolean } | null;
|
|
}>({ path: "", user: null });
|
|
const role = useEffectiveRole(state.user?.role);
|
|
useEffect(() => {
|
|
let active = true;
|
|
const load = async () => {
|
|
if (!getToken()) {
|
|
if (active) setState({ path: pathname, user: null });
|
|
return;
|
|
}
|
|
try {
|
|
const response = await authFetch(`${getApiBase()}/auth/me`);
|
|
const user = response.ok ? await response.json() : null;
|
|
if (active) setState({ path: pathname, user });
|
|
} catch {
|
|
if (active) setState({ path: pathname, user: null });
|
|
}
|
|
};
|
|
void load();
|
|
window.addEventListener("focus", load);
|
|
return () => {
|
|
active = false;
|
|
window.removeEventListener("focus", load);
|
|
};
|
|
}, [pathname]);
|
|
return { user: state.user ? { ...state.user, role: role ?? undefined } : null, ready: state.path === pathname };
|
|
}
|
|
|
|
export default function FeatureGate({ children }: { children: ReactNode }) {
|
|
const pathname = usePathname();
|
|
const { user, ready } = useFeatureUser();
|
|
const feature = featureForPath(pathname);
|
|
if (isAdminPage(pathname, false)) {
|
|
if (!ready) return <main className="card">Checking administrator access...</main>;
|
|
if (user?.role !== "admin") {
|
|
return (
|
|
<main className="card">
|
|
<h1>Administrator access required</h1>
|
|
<p>Sign in with an administrator account to use configuration and administration tools.</p>
|
|
<a href="/login">Sign in</a>
|
|
</main>
|
|
);
|
|
}
|
|
return children;
|
|
}
|
|
if (!feature) return children;
|
|
if (!ready) return <main className="card">Loading account access...</main>;
|
|
if (!getToken()) return children;
|
|
if (!canAccess(user, feature))
|
|
return (
|
|
<main className="card">
|
|
<h1>Feature unavailable</h1>
|
|
<p>Your account does not have access to this feature. Ask an administrator if you need it enabled.</p>
|
|
<a href="/profile">Go to my profile</a>
|
|
</main>
|
|
);
|
|
return children;
|
|
}
|