diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx new file mode 100644 index 0000000..cecd581 --- /dev/null +++ b/app/(auth)/layout.tsx @@ -0,0 +1,7 @@ +export default function AuthLayout({ + children, +}: { + children: React.ReactNode; +}) { + return
{children}
; +} diff --git a/app/(pages)/login/page.tsx b/app/(auth)/login/page.tsx similarity index 100% rename from app/(pages)/login/page.tsx rename to app/(auth)/login/page.tsx diff --git a/app/(pages)/register/page.tsx b/app/(auth)/register/page.tsx similarity index 100% rename from app/(pages)/register/page.tsx rename to app/(auth)/register/page.tsx diff --git a/app/(pages)/dashboard/page.tsx b/app/(pages)/dashboard/page.tsx index c48176d..3846762 100644 --- a/app/(pages)/dashboard/page.tsx +++ b/app/(pages)/dashboard/page.tsx @@ -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 ; } diff --git a/app/(pages)/layout.tsx b/app/(pages)/layout.tsx index c6f4fa0..3c7ce27 100644 --- a/app/(pages)/layout.tsx +++ b/app/(pages)/layout.tsx @@ -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 (
@@ -25,7 +28,6 @@ export default function PagesLayout({ - ); } diff --git a/app/(pages)/settings/page.tsx b/app/(pages)/settings/page.tsx index 4fa8c34..3ac4aeb 100644 --- a/app/(pages)/settings/page.tsx +++ b/app/(pages)/settings/page.tsx @@ -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); diff --git a/app/globals.css b/app/globals.css index 59b0398..690e025 100644 --- a/app/globals.css +++ b/app/globals.css @@ -65,7 +65,7 @@ --secondary-foreground: oklch(0.85 0.02 80); --muted: oklch(0.22 0.008 55); --muted-foreground: oklch(0.63 0.02 80); - --accent: oklch(0.25 0.010 55); + --accent: oklch(0.25 0.01 55); --accent-foreground: oklch(0.93 0.015 80); --destructive: oklch(0.65 0.2 25); --border: oklch(1 0.015 65 / 10%); @@ -175,7 +175,7 @@ body::before { [data-slot="skeleton"] { animation: warm-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; - background: oklch(0.25 0.010 55); + background: oklch(0.25 0.01 55); } /* Scrollbar styling */ diff --git a/app/layout.tsx b/app/layout.tsx index 138da0f..fd18d2b 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,6 +3,7 @@ import { DM_Sans, DM_Serif_Display, Geist_Mono } from "next/font/google"; import { TooltipProvider } from "@/components/ui/tooltip"; import "./globals.css"; +import { Toaster } from "@/components/ui/sonner"; const dmSans = DM_Sans({ variable: "--font-dm-sans", @@ -36,6 +37,7 @@ export default function RootLayout({ className={`${dmSans.variable} ${dmSerif.variable} ${geistMono.variable} font-sans antialiased`} > {children} + ); diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..41b65d7 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,16 @@ +import { getSessionCookie } from "better-auth/cookies"; +import { type NextRequest, NextResponse } from "next/server"; + +export function middleware(request: NextRequest) { + const sessionCookie = getSessionCookie(request); + + if (!sessionCookie) { + return NextResponse.redirect(new URL("/login", request.url)); + } + + return NextResponse.next(); +} + +export const config = { + matcher: ["/dashboard", "/explore", "/settings", "/setup", "/titles/:path*"], +};