From 13909c46f6ef23894fa2ed9a96585b1b2966ed3f Mon Sep 17 00:00:00 2001 From: Pc Date: Sun, 4 Jan 2026 14:36:29 +0100 Subject: [PATCH] test: testing --- kittyurl-frontend/src/App.tsx | 37 ++++++++++++++-- .../src/context/AuthProvider.tsx | 43 +++++++++---------- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/kittyurl-frontend/src/App.tsx b/kittyurl-frontend/src/App.tsx index 028e565..cf9e146 100644 --- a/kittyurl-frontend/src/App.tsx +++ b/kittyurl-frontend/src/App.tsx @@ -7,19 +7,43 @@ import { KittyGame } from './components/KittyGame'; import { FlappyCat } from './components/FlappyCat'; import { useAuth } from './hooks/useAuth'; -// Eksportujemy typ, aby inne pliki mogły go użyć export type View = 'home' | 'login' | 'history' | 'jump-game' | 'flappy-game'; +const getSubdomain = () => { + const hostname = window.location.hostname; + const parts = hostname.split('.'); + if (parts.length <= 2) return null; + return parts[0]; +}; + function App() { const [url, setUrl] = useState(''); const [view, setView] = useState('home'); const { isAuthenticated, logout } = useAuth(); + const subdomain = getSubdomain(); + + /** + * STAN POCHODNY (Derived State) + * Rozwiązuje błąd "cascading renders". Jeśli użytkownik jest na subdomenie + * i nie jest zalogowany, automatycznie renderujemy widok logowania, + * ale nie nadpisujemy stanu 'view' w nieskończoność. + */ + const activeView = (subdomain && !isAuthenticated) ? 'login' : view; const renderView = () => { - switch (view) { + switch (activeView) { case 'login': - return setView('home')} onSuccess={() => setView('home')} />; + return ( + setView('home')} + onSuccess={() => setView('home')} + /> + ); case 'history': + // Strażnik dostępu dla widoku historii + if (!isAuthenticated) { + return setView('home')} onSuccess={() => setView('home')} />; + } return setView('home')} />; case 'jump-game': return setView('home')} />; @@ -37,7 +61,12 @@ function App() { isAuthenticated={isAuthenticated} onLogout={logout} /> -
{renderView()}
+
+ {/* Jeśli użytkownik jest zalogowany (SSO), activeView od razu + pokaże Generator, zamiast LoginView. + */} + {renderView()} +
); } diff --git a/kittyurl-frontend/src/context/AuthProvider.tsx b/kittyurl-frontend/src/context/AuthProvider.tsx index 5d3c056..f6b14c2 100644 --- a/kittyurl-frontend/src/context/AuthProvider.tsx +++ b/kittyurl-frontend/src/context/AuthProvider.tsx @@ -6,18 +6,21 @@ import type { AuthResponse } from '../types/auth'; const TOKEN_KEY = 'ktty_shared_token'; -// 1. DYNAMICZNE USTALANIE DOMENY +/** + * Konfiguracja ciasteczek dla SSO. + * Na localhost ciasteczka domenowe nie działają, więc używamy undefined. + * Na produkcji używamy '.ktty.is', co pozwala współdzielić sesję między subdomenami. + */ const getCookieConfig = () => { const hostname = window.location.hostname; + const isLocal = hostname === 'localhost' || hostname === '127.0.0.1'; - // Jeśli testujesz na localhost, nie ustawiamy atrybutu 'domain' - if (hostname === 'localhost' || hostname === '127.0.0.1') { - return { domain: undefined, secure: false }; - } - - // Na produkcji używamy domeny nadrzędnej z kropką - // To pozwoli s.ktty.is i ktty.is widzieć to samo ciasteczko - return { domain: '.ktty.is', secure: true }; + return { + domain: isLocal ? undefined : '.ktty.is', + secure: !isLocal, // Wymagane HTTPS na produkcji + sameSite: 'Lax' as const, + expires: 7 // Token ważny przez 7 dni + }; }; const getSubdomain = (): string | null => { @@ -31,7 +34,8 @@ export function AuthProvider({ children }: { children: ReactNode }) { const config = getCookieConfig(); const subdomain = getSubdomain(); - // Inicjalizacja stanu bezpośrednio z ciasteczka + // Inicjalizacja stanu: sprawdzamy ciasteczko natychmiast przy ładowaniu strony. + // Dzięki temu SSO działa bez opóźnień. const [token, setToken] = useState(() => Cookies.get(TOKEN_KEY) || null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); @@ -42,6 +46,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { try { const hashedPassword = await sha512(pass); + // Vite Proxy przekaże to na https://www.ktty.is/api/... const fullUrl = `/api/v1/user/${endpoint}`; const response = await fetch(fullUrl, { @@ -57,24 +62,18 @@ export function AuthProvider({ children }: { children: ReactNode }) { const data: AuthResponse = await response.json(); if (!response.ok) { - throw new Error(data?.error || data?.message || 'Błąd logowania'); + throw new Error(data?.error || data?.message || 'Błąd autoryzacji'); } if (data?.token) { - // 2. ZAPIS Z DYNAMICZNĄ KONFIGURACJĄ - Cookies.set(TOKEN_KEY, data.token, { - domain: config.domain, - expires: 7, // wydłużamy do 7 dni dla wygody - secure: config.secure, - sameSite: 'lax' - }); + // Zapisujemy token z flagą domain: '.ktty.is' + Cookies.set(TOKEN_KEY, data.token, config); setToken(data.token); } return data; } catch (err: unknown) { - let errorMessage = 'Wystąpił nieoczekiwany błąd'; - if (err instanceof Error) errorMessage = err.message; - setError(errorMessage); + const msg = err instanceof Error ? err.message : 'Wystąpił nieoczekiwany błąd'; + setError(msg); return null; } finally { setLoading(false); @@ -82,7 +81,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { }, [subdomain, config]); const logout = useCallback(() => { - // 3. USUNIĘCIE MUSI MIEĆ TĘ SAMĄ DOMENĘ CO ZAPIS + // Usuwamy ciasteczko z tej samej domeny, na której zostało zapisane Cookies.remove(TOKEN_KEY, { domain: config.domain }); setToken(null); }, [config]);