Fix middleware matcher basePath issue, always show logout button
All checks were successful
Deploy to Server / deploy (push) Successful in 1m10s

Next.js prepends basePath to matcher patterns, so patterns must not
include /admin prefix. Logout button now always visible in sidebar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-03-15 22:05:14 +01:00
parent 2f3f460e0f
commit 634ba8d494
2 changed files with 21 additions and 44 deletions

View File

@@ -226,41 +226,21 @@ export function Sidebar({ locale, mobileOpen = false, onClose }: SidebarProps) {
</div> </div>
</nav> </nav>
<div className="mt-2 border-t border-accent-200/50 pt-2"> <div className="mt-2 border-t border-accent-200/50 pt-2">
{!mounted ? ( <button
<Link type="button"
href="/login" onClick={async () => {
onClick={onClose} clearSession();
className={`${navLinkClass} w-full text-left text-gray-700 no-underline hover:bg-accent-100/80 hover:text-gray-900 ${pathname === "/login" ? "bg-accent-200/70 font-medium text-gray-900" : ""}`} setLogoutVersion((v) => v + 1);
> onClose?.();
<Icon icon="mdi:login" className="size-4" aria-hidden /> await fetch("/admin/api/auth/logout", { method: "POST" });
{t("login")} router.push("/login");
</Link> router.refresh();
) : hasStoredKey ? ( }}
<button className={`${navLinkClass} w-full text-left text-gray-700 hover:bg-accent-100/80 hover:text-gray-900`}
type="button" >
onClick={async () => { <Icon icon="mdi:logout" className="size-4" aria-hidden />
clearSession(); {t("logout")}
setLogoutVersion((v) => v + 1); </button>
onClose?.();
await fetch("/admin/api/auth/logout", { method: "POST" });
router.push("/login");
router.refresh();
}}
className={`${navLinkClass} w-full text-left text-gray-700 hover:bg-accent-100/80 hover:text-gray-900`}
>
<Icon icon="mdi:logout" className="size-4" aria-hidden />
{t("logout")}
</button>
) : !hasEnvKey ? (
<Link
href="/login"
onClick={onClose}
className={`${navLinkClass} w-full text-left text-gray-700 no-underline hover:bg-accent-100/80 hover:text-gray-900 ${pathname === "/login" ? "bg-accent-200/70 font-medium text-gray-900" : ""}`}
>
<Icon icon="mdi:login" className="size-4" aria-hidden />
{t("login")}
</Link>
) : null}
</div> </div>
<LocaleSwitcher locale={locale} /> <LocaleSwitcher locale={locale} />
</aside> </aside>

View File

@@ -3,15 +3,7 @@ import type { NextRequest } from "next/server";
import { getIronSession } from "iron-session"; import { getIronSession } from "iron-session";
import { sessionOptions, type SessionData } from "@/lib/session"; import { sessionOptions, type SessionData } from "@/lib/session";
const PUBLIC_PREFIXES = ["/admin/login", "/admin/api/auth"];
export async function middleware(request: NextRequest) { export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
if (PUBLIC_PREFIXES.some((p) => pathname.startsWith(p))) {
return NextResponse.next();
}
const response = NextResponse.next(); const response = NextResponse.next();
const session = await getIronSession<SessionData>(request, response, sessionOptions); const session = await getIronSession<SessionData>(request, response, sessionOptions);
@@ -23,6 +15,11 @@ export async function middleware(request: NextRequest) {
return response; return response;
} }
// Next.js prepends basePath (/admin) to these patterns automatically.
// "/" matches /admin, "/((?!login|api/auth|_next|favicon.ico).+)" matches /admin/content/... etc.
export const config = { export const config = {
matcher: ["/admin", "/admin/((?!_next|favicon.ico).*)"], matcher: [
"/",
"/((?!login|api/auth|_next|favicon.ico).+)",
],
}; };