Show Admin nav only when GET /admin succeeds
Probe Authelia-protected /admin with credentials; keep the public site free of admin links for anonymous visitors.
This commit is contained in:
@@ -55,6 +55,11 @@
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
&__admin {
|
||||
color: var(--accent) !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__burger {
|
||||
display: none;
|
||||
width: 40px;
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, NavLink, Outlet, useLocation } from "react-router-dom";
|
||||
import { api, type SiteSettings } from "../lib/api";
|
||||
import { canAccessAdmin } from "../lib/adminAccess";
|
||||
import "./Layout.scss";
|
||||
|
||||
export function Layout() {
|
||||
const [site, setSite] = useState<SiteSettings | null>(null);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [showAdmin, setShowAdmin] = useState(false);
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
api.site().then(setSite).catch(() => setSite({ name: "jmartgraphix" }));
|
||||
}, []);
|
||||
|
||||
// Only surface Admin when Traefik/Authelia already allows GET /admin
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
canAccessAdmin().then((ok) => {
|
||||
if (!cancelled) setShowAdmin(ok);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
setMenuOpen(false);
|
||||
}, [location.pathname]);
|
||||
@@ -47,6 +60,11 @@ export function Layout() {
|
||||
>
|
||||
ArtStation
|
||||
</a>
|
||||
{showAdmin && (
|
||||
<NavLink to="/admin" className="site-header__admin">
|
||||
Admin
|
||||
</NavLink>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Probe Traefik/Authelia protection on /admin.
|
||||
* Returns true only when a same-origin GET succeeds (HTTP 200),
|
||||
* meaning the visitor is already authenticated for admin.
|
||||
* Failures (401/403/302/network) keep the public site admin-free.
|
||||
*/
|
||||
export async function canAccessAdmin(): Promise<boolean> {
|
||||
try {
|
||||
const res = await fetch("/admin", {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
redirect: "manual",
|
||||
cache: "no-store",
|
||||
headers: { Accept: "text/html" },
|
||||
});
|
||||
// opaqueredirect (0) = browser blocked reading a cross-origin redirect
|
||||
// 3xx with redirect:manual also means not authorized for the resource
|
||||
if (res.type === "opaqueredirect") return false;
|
||||
if (res.status >= 300 && res.status < 400) return false;
|
||||
return res.status === 200;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -90,8 +90,7 @@ export function HomePage() {
|
||||
</div>
|
||||
{recent.length === 0 ? (
|
||||
<p className="home-empty">
|
||||
Projects will appear here once published. Use the{" "}
|
||||
<Link to="/admin">admin panel</Link> to add work, or import from ArtStation.
|
||||
Projects will appear here once published.
|
||||
</p>
|
||||
) : (
|
||||
<div className="project-grid">
|
||||
|
||||
Reference in New Issue
Block a user