mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 05:05:38 -04:00
Move auth protection to middleware and server layout
Replace client-side session checks (useSession + router.replace) with a two-layer server-side approach: Next.js middleware performs a fast cookie presence check for all protected routes, and the (pages) layout does a full server-side session validation before rendering. Move login/register into a dedicated (auth) route group with its own minimal layout so they sit outside the authenticated shell. Lift the Toaster to the root layout so it remains available across all route groups.
This commit is contained in:
@@ -8,7 +8,6 @@ import {
|
||||
import { motion } from "motion/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { DashboardSkeleton } from "@/components/skeletons";
|
||||
import { StatsSummary } from "@/components/stats-summary";
|
||||
@@ -72,8 +71,7 @@ const sectionVariants = {
|
||||
};
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { data: session, isPending } = useSession();
|
||||
const router = useRouter();
|
||||
const { data: session } = useSession();
|
||||
const [continueWatching, setContinueWatching] = useState<
|
||||
ContinueWatchingItem[]
|
||||
>([]);
|
||||
@@ -98,15 +96,10 @@ export default function DashboardPage() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPending) return;
|
||||
if (!session?.user) {
|
||||
router.replace("/login");
|
||||
return;
|
||||
}
|
||||
fetchFeeds();
|
||||
}, [session, isPending, router, fetchFeeds]);
|
||||
}, [fetchFeeds]);
|
||||
|
||||
if (isPending || loading) {
|
||||
if (loading) {
|
||||
return <DashboardSkeleton />;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { CommandPalette } from "@/components/command-palette";
|
||||
import { KeyboardHelpDialog } from "@/components/keyboard-help-dialog";
|
||||
import { KeyboardProvider } from "@/components/keyboard-provider";
|
||||
import { MobileTabBar } from "@/components/mobile-tab-bar";
|
||||
import { NavBar } from "@/components/nav-bar";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { auth } from "@/lib/auth/server";
|
||||
|
||||
export default function PagesLayout({
|
||||
export default async function PagesLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
if (!session) redirect("/login");
|
||||
|
||||
return (
|
||||
<KeyboardProvider>
|
||||
<div className="min-h-screen overflow-x-hidden pb-14 sm:pb-0">
|
||||
@@ -25,7 +28,6 @@ export default function PagesLayout({
|
||||
<MobileTabBar />
|
||||
<CommandPalette />
|
||||
<KeyboardHelpDialog />
|
||||
<Toaster position="bottom-right" />
|
||||
</KeyboardProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { AuthForm } from "@/components/auth-form";
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<div className="flex min-h-[80vh] items-center justify-center px-4">
|
||||
<AuthForm mode="login" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { IconLock } from "@tabler/icons-react";
|
||||
import { motion } from "motion/react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AuthForm } from "@/components/auth-form";
|
||||
|
||||
export default function RegisterPage() {
|
||||
const [registrationOpen, setRegistrationOpen] = useState<boolean | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/registration/status")
|
||||
.then((res) => res.json())
|
||||
.then((data) => setRegistrationOpen(data.registrationOpen))
|
||||
.catch(() => setRegistrationOpen(false));
|
||||
}, []);
|
||||
|
||||
if (registrationOpen === null) {
|
||||
return <div className="flex min-h-[80vh] items-center justify-center" />;
|
||||
}
|
||||
|
||||
if (!registrationOpen) {
|
||||
return (
|
||||
<div className="flex min-h-[80vh] items-center justify-center px-4">
|
||||
<div className="relative mx-auto w-full max-w-sm">
|
||||
<div className="absolute -inset-4 rounded-2xl bg-primary/3 blur-2xl" />
|
||||
<motion.div
|
||||
className="relative space-y-6 rounded-xl border border-border/50 bg-card/80 p-8 text-center backdrop-blur-sm"
|
||||
initial={{ opacity: 0, y: 20, scale: 0.98 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
transition={{
|
||||
type: "spring" as const,
|
||||
stiffness: 200,
|
||||
damping: 20,
|
||||
}}
|
||||
>
|
||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-primary/10">
|
||||
<IconLock size={24} className="text-primary" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h1 className="font-display text-xl tracking-tight">
|
||||
Registration Closed
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
New accounts are not being accepted right now. Contact the admin
|
||||
if you need access.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/login"
|
||||
className="inline-flex h-10 items-center rounded-lg bg-primary px-6 text-sm font-medium text-primary-foreground transition-all hover:shadow-md hover:shadow-primary/20"
|
||||
>
|
||||
Sign in instead
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[80vh] items-center justify-center px-4">
|
||||
<AuthForm mode="register" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -354,17 +354,13 @@ export default function SettingsPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isPending) return;
|
||||
if (!session?.user) {
|
||||
router.replace("/login");
|
||||
return;
|
||||
}
|
||||
fetchWebhooks();
|
||||
if (session.user.role === "admin") {
|
||||
if (session?.user?.role === "admin") {
|
||||
fetchSettings();
|
||||
} else {
|
||||
setLoadingSettings(false);
|
||||
}
|
||||
}, [session, isPending, router, fetchSettings, fetchWebhooks]);
|
||||
}, [session, isPending, fetchSettings, fetchWebhooks]);
|
||||
|
||||
async function handleToggleRegistration(checked: boolean) {
|
||||
setToggling(true);
|
||||
|
||||
Reference in New Issue
Block a user