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:
@@ -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 />;
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user