Merge pull request #28 from damjan1996/auto-claude/044-implement-server-side-route-protection-for-dashboa

auto-claude: 044-implement-server-side-route-protection-for-dashboa
This commit is contained in:
Damjan Savic
2026-01-25 19:56:05 +01:00
committed by GitHub
4 changed files with 39 additions and 22 deletions
View File
+17
View File
@@ -1,6 +1,12 @@
import { connection } from 'next/server';
import { setRequestLocale } from 'next-intl/server';
import type { Metadata } from 'next';
import { redirect } from 'next/navigation';
import DashboardContent from '@/components/auth/DashboardContent';
import { createClient } from '@/lib/supabase/server';
// Force dynamic rendering to ensure auth checks run on every request
export const dynamic = 'force-dynamic';
type Props = {
params: Promise<{ locale: string }>;
@@ -29,8 +35,19 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
}
export default async function DashboardPage({ params }: Props) {
// CRITICAL: Use connection() API to guarantee dynamic rendering in Next.js 15
await connection();
const { locale } = await params;
setRequestLocale(locale);
// Server-side auth check (defense-in-depth)
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
redirect(`/${locale}/login`);
}
return <DashboardContent />;
}
+4 -22
View File
@@ -1,11 +1,9 @@
'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { Loader2, BarChart2, Users, Eye, Clock, TrendingUp, TrendingDown, LogOut } from 'lucide-react';
import { BarChart2, Users, Eye, Clock, TrendingUp, TrendingDown, LogOut } from 'lucide-react';
import { useTranslations, useLocale } from 'next-intl';
import { createClient } from '@/lib/supabase/client';
import type { User } from '@supabase/supabase-js';
interface MetricCard {
title: string;
@@ -20,6 +18,8 @@ export default function DashboardContent() {
const t = useTranslations('dashboard');
const locale = useLocale();
const router = useRouter();
<<<<<<< HEAD
=======
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
@@ -34,6 +34,7 @@ export default function DashboardContent() {
fetchUser();
}, []);
>>>>>>> origin/master
const handleLogout = async () => {
const supabase = createClient();
@@ -41,25 +42,6 @@ export default function DashboardContent() {
router.push(`/${locale}`);
};
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="flex flex-col items-center space-y-4">
<Loader2 className="h-8 w-8 text-zinc-400 animate-spin" />
<p className="text-zinc-400 text-sm">{t('status.loading')}</p>
</div>
</div>
);
}
if (!user) {
return (
<div className="min-h-screen flex items-center justify-center">
<p className="text-zinc-400">{t('status.notAuthenticated')}</p>
</div>
);
}
const metrics: MetricCard[] = [
{
title: t('metrics.pageViews.title'),
+18
View File
@@ -1,6 +1,10 @@
import { createServerClient } from '@supabase/ssr';
import { NextRequest, NextResponse } from 'next/server';
<<<<<<< HEAD
export function createClient(request: NextRequest, response: NextResponse) {
return createServerClient(
=======
export async function createClient(request: NextRequest) {
let response = NextResponse.next({
request: {
@@ -9,6 +13,7 @@ export async function createClient(request: NextRequest) {
});
const supabase = createServerClient(
>>>>>>> origin/master
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
@@ -17,6 +22,15 @@ export async function createClient(request: NextRequest) {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
<<<<<<< HEAD
try {
cookiesToSet.forEach(({ name, value, options }) => {
response.cookies.set(name, value, options);
});
} catch {
// Handle middleware context
}
=======
cookiesToSet.forEach(({ name, value, options }) =>
request.cookies.set(name, value)
);
@@ -28,10 +42,14 @@ export async function createClient(request: NextRequest) {
cookiesToSet.forEach(({ name, value, options }) =>
response.cookies.set(name, value, options)
);
>>>>>>> origin/master
},
},
}
);
<<<<<<< HEAD
=======
return { supabase, response };
>>>>>>> origin/master
}