Compare commits

...
Author SHA1 Message Date
damjan_savic 206fc3e8a9 Merge origin/master 2026-01-25 19:55:29 +01:00
Damjan SavicandGitHub b8233bdc2f Merge pull request #26 from damjan1996/auto-claude/002-recherchiere-weitere-seo-optimierungen-an-der-webs
auto-claude: 002-recherchiere-weitere-seo-optimierungen-an-der-webs
2026-01-25 19:52:20 +01:00
damjan_savic 919fe083f2 Merge origin/master 2026-01-25 19:47:38 +01:00
damjan_savicandClaude Sonnet 4.5 131c883425 fix: use connection() API to force dynamic rendering in Next.js 15 (qa-requested)
- Add connection() API call to guarantee dynamic rendering
- Fixes critical auth bypass where page was still being pre-rendered
- Previous fix (force-dynamic) was insufficient in Next.js 15
- connection() API is the official Next.js 15 recommendation

Resolves QA Session 2 Critical Issue #1

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 12:41:31 +01:00
damjan_savicandClaude Sonnet 4.5 5551226353 fix: Address QA issues - force dynamic rendering and document middleware trade-off (qa-requested)
Fixes:
- Add dynamic export to dashboard page to prevent Next.js pre-rendering
- Fixes critical auth bypass where cached page was served to all users
- Document middleware response propagation trade-off

QA Issues Fixed:
- Issue #1: Dashboard page pre-rendering bypasses authentication
- Issue #3: Middleware response object not propagated (documented)

Verified:
- TypeScript compilation passes
- Code follows Next.js 15 auth best practices
- Middleware trade-off documented per QA recommendation

QA Fix Session: 1

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 12:20:49 +01:00
damjan_savicandClaude Sonnet 4.5 23c79f1fa2 auto-claude: subtask-2-2 - Simplify client-side auth check in DashboardContent
Removed redundant client-side authentication logic since server-side
protection is now in place (middleware + page-level checks). The
component now:
- No longer performs useEffect auth check on mount
- No loading state for authentication
- Renders dashboard content immediately
- Maintains logout functionality
- Assumes user is authenticated (guaranteed by server)

This eliminates the flash of loading state and improves UX while
maintaining security through server-side protection.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 12:04:22 +01:00
damjan_savicandClaude Sonnet 4.5 a792db0290 auto-claude: subtask-2-1 - Add server-side auth check to dashboard page
- Added server-side authentication check in DashboardPage component
- Created Supabase client using createClient from @/lib/supabase/server
- Check user authentication with getUser() before rendering
- Redirect to /${locale}/login if user is not authenticated
- Provides defense-in-depth security alongside middleware protection

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 12:01:47 +01:00
damjan_savicandClaude Sonnet 4.5 2337b15e53 auto-claude: subtask-1-2 - Update middleware.ts to add authentication protect
- Add server-side authentication check for /[locale]/dashboard routes
- Create Supabase client in middleware to verify user session
- Redirect unauthenticated users to /[locale]/login with locale preservation
- Chain to existing i18n middleware for all other routes
- Authentication check runs before i18n middleware processing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 11:59:53 +01:00
damjan_savic d9f209c40c auto-claude: subtask-1-1 - Create Supabase middleware client utility 2026-01-25 11:58:00 +01:00
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 { setRequestLocale } from 'next-intl/server';
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { redirect } from 'next/navigation';
import DashboardContent from '@/components/auth/DashboardContent'; 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 = { type Props = {
params: Promise<{ locale: string }>; params: Promise<{ locale: string }>;
@@ -29,8 +35,19 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
} }
export default async function DashboardPage({ params }: Props) { 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; const { locale } = await params;
setRequestLocale(locale); 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 />; return <DashboardContent />;
} }
+4 -22
View File
@@ -1,11 +1,9 @@
'use client'; 'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation'; 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 { useTranslations, useLocale } from 'next-intl';
import { createClient } from '@/lib/supabase/client'; import { createClient } from '@/lib/supabase/client';
import type { User } from '@supabase/supabase-js';
interface MetricCard { interface MetricCard {
title: string; title: string;
@@ -20,6 +18,8 @@ export default function DashboardContent() {
const t = useTranslations('dashboard'); const t = useTranslations('dashboard');
const locale = useLocale(); const locale = useLocale();
const router = useRouter(); const router = useRouter();
<<<<<<< HEAD
=======
const [user, setUser] = useState<User | null>(null); const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@@ -34,6 +34,7 @@ export default function DashboardContent() {
fetchUser(); fetchUser();
}, []); }, []);
>>>>>>> origin/master
const handleLogout = async () => { const handleLogout = async () => {
const supabase = createClient(); const supabase = createClient();
@@ -41,25 +42,6 @@ export default function DashboardContent() {
router.push(`/${locale}`); 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[] = [ const metrics: MetricCard[] = [
{ {
title: t('metrics.pageViews.title'), title: t('metrics.pageViews.title'),
+18
View File
@@ -1,6 +1,10 @@
import { createServerClient } from '@supabase/ssr'; import { createServerClient } from '@supabase/ssr';
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
<<<<<<< HEAD
export function createClient(request: NextRequest, response: NextResponse) {
return createServerClient(
=======
export async function createClient(request: NextRequest) { export async function createClient(request: NextRequest) {
let response = NextResponse.next({ let response = NextResponse.next({
request: { request: {
@@ -9,6 +13,7 @@ export async function createClient(request: NextRequest) {
}); });
const supabase = createServerClient( const supabase = createServerClient(
>>>>>>> origin/master
process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ {
@@ -17,6 +22,15 @@ export async function createClient(request: NextRequest) {
return request.cookies.getAll(); return request.cookies.getAll();
}, },
setAll(cookiesToSet) { setAll(cookiesToSet) {
<<<<<<< HEAD
try {
cookiesToSet.forEach(({ name, value, options }) => {
response.cookies.set(name, value, options);
});
} catch {
// Handle middleware context
}
=======
cookiesToSet.forEach(({ name, value, options }) => cookiesToSet.forEach(({ name, value, options }) =>
request.cookies.set(name, value) request.cookies.set(name, value)
); );
@@ -28,10 +42,14 @@ export async function createClient(request: NextRequest) {
cookiesToSet.forEach(({ name, value, options }) => cookiesToSet.forEach(({ name, value, options }) =>
response.cookies.set(name, value, options) response.cookies.set(name, value, options)
); );
>>>>>>> origin/master
}, },
}, },
} }
); );
<<<<<<< HEAD
=======
return { supabase, response }; return { supabase, response };
>>>>>>> origin/master
} }