Refactor: Centralized Supabase Auth and implemented Auth Guards to prevent 401 errors

This commit is contained in:
2026-01-04 23:00:18 +01:00
parent 9d6a8b358f
commit 71586fd6a8
15 changed files with 18678 additions and 576 deletions

View File

@@ -1,28 +0,0 @@
'use client';
import { useEffect } from 'react';
import { createClient } from '@/lib/supabase/client';
export default function AuthListener() {
const supabase = createClient();
useEffect(() => {
// Listener für Auth-Status Änderungen
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event) => {
if (event === 'SIGNED_OUT') {
console.log(`[Auth] Event ${event} detected, forcing reload...`);
// Zwinge den Browser zum kompletten Neuladen, um Caches zu leeren
// Wir nutzen window.location.href statt router.push für einen harten Reload
window.location.href = '/';
}
});
return () => {
subscription.unsubscribe();
};
}, [supabase]);
return null;
}

View File

@@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react';
import { createClient } from '@/lib/supabase/client';
import { Users, UserPlus, Trash2, Loader2, ChevronDown, ChevronUp, Link2 } from 'lucide-react';
import { useI18n } from '@/i18n/I18nContext';
import { useAuth } from '@/context/AuthContext';
import { addBuddy, deleteBuddy } from '@/services/buddy';
import BuddyHandshake from './BuddyHandshake';
@@ -27,10 +28,13 @@ export default function BuddyList() {
return false;
});
const [isHandshakeOpen, setIsHandshakeOpen] = useState(false);
const { user, isLoading: isAuthLoading } = useAuth();
useEffect(() => {
fetchBuddies();
}, []);
if (!isAuthLoading && user) {
fetchBuddies();
}
}, [user, isAuthLoading]);
const fetchBuddies = async () => {
const { data: { user } } = await supabase.auth.getUser();

View File

@@ -8,6 +8,7 @@ import AvatarStack from './AvatarStack';
import { deleteSession } from '@/services/delete-session';
import { useI18n } from '@/i18n/I18nContext';
import { useSession } from '@/context/SessionContext';
import { useAuth } from '@/context/AuthContext';
interface Session {
id: string;
@@ -34,10 +35,13 @@ export default function SessionList() {
});
const [newName, setNewName] = useState('');
const { activeSession, setActiveSession } = useSession();
const { user, isLoading: isAuthLoading } = useAuth();
useEffect(() => {
fetchSessions();
}, []);
if (!isAuthLoading && user) {
fetchSessions();
}
}, [user, isAuthLoading]);
const fetchSessions = async () => {
const { data, error } = await supabase

View File

@@ -9,6 +9,7 @@ import {
} from 'lucide-react';
import { createClient } from '@/lib/supabase/client';
import { useI18n } from '@/i18n/I18nContext';
import { useAuth } from '@/context/AuthContext';
import { useSession } from '@/context/SessionContext';
import { getHostSplits, getParticipatingSplits } from '@/services/split-actions';
import AvatarStack from './AvatarStack';
@@ -49,6 +50,7 @@ export default function TastingHub({ isOpen, onClose }: TastingHubProps) {
const { t, locale } = useI18n();
const supabase = createClient();
const { activeSession, setActiveSession } = useSession();
const { user, isLoading: isAuthLoading } = useAuth();
const [activeTab, setActiveTab] = useState<'tastings' | 'splits'>('tastings');
const [mySessions, setMySessions] = useState<Session[]>([]);
@@ -62,10 +64,10 @@ export default function TastingHub({ isOpen, onClose }: TastingHubProps) {
const [newName, setNewName] = useState('');
useEffect(() => {
if (isOpen) {
if (isOpen && !isAuthLoading && user) {
fetchAll();
}
}, [isOpen]);
}, [isOpen, isAuthLoading, user]);
const fetchAll = async () => {
setIsLoading(true);

View File

@@ -7,8 +7,9 @@ import { createClient } from '@/lib/supabase/client';
import { useI18n } from '@/i18n/I18nContext';
import { useSession } from '@/context/SessionContext';
import TagSelector from './TagSelector';
import { useLiveQuery } from 'dexie-react-hooks';
import { db } from '@/lib/db';
import { useAuth } from '@/context/AuthContext';
import { useLiveQuery } from 'dexie-react-hooks';
import { AlertTriangle } from 'lucide-react';
import TastingFormBody from './TastingFormBody';
@@ -42,6 +43,7 @@ export default function TastingNoteForm({ bottleId, pendingBottleId, sessionId,
const [suggestedTags, setSuggestedTags] = useState<string[]>([]);
const [suggestedCustomTags, setSuggestedCustomTags] = useState<string[]>([]);
const { activeSession } = useSession();
const { user, isLoading: isAuthLoading } = useAuth();
const [lastDramInSession, setLastDramInSession] = useState<{ name: string; isSmoky: boolean; timestamp: number } | null>(null);
const [showPaletteWarning, setShowPaletteWarning] = useState(false);
@@ -56,16 +58,14 @@ export default function TastingNoteForm({ bottleId, pendingBottleId, sessionId,
const effectiveSessionId = sessionId || activeSession?.id;
useEffect(() => {
const getAuth = async () => {
const { data: { user } } = await supabase.auth.getUser();
if (user) setCurrentUserId(user.id);
};
getAuth();
}, [supabase]);
if (!isAuthLoading && user) {
setCurrentUserId(user.id);
}
}, [user, isAuthLoading]);
useEffect(() => {
const fetchData = async () => {
if (!bottleId) return;
if (!bottleId || isAuthLoading || !user) return;
// Fetch Bottle Suggestions and Owner
const { data: bottleData } = await supabase
@@ -130,7 +130,7 @@ export default function TastingNoteForm({ bottleId, pendingBottleId, sessionId,
}
};
fetchData();
}, [supabase, effectiveSessionId, bottleId]);
}, [supabase, effectiveSessionId, bottleId, user, isAuthLoading]);
// Live Palette Checker Logic
useEffect(() => {