test:v2
All checks were successful
Update changelog / changelog (push) Successful in 28s

This commit is contained in:
Pc
2026-01-04 16:15:08 +01:00
parent 13909c46f6
commit 5dd751600c
2 changed files with 37 additions and 48 deletions

View File

@@ -2,98 +2,72 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { AuthContext } from './AuthContext'; import { AuthContext } from './AuthContext';
import { sha512 } from '../utils/crypto'; import { sha512 } from '../utils/crypto';
import type { AuthResponse } from '../types/auth';
const TOKEN_KEY = 'ktty_shared_token'; const TOKEN_KEY = 'ktty_shared_token';
/**
* 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 getCookieConfig = () => {
const hostname = window.location.hostname; const hostname = window.location.hostname;
const isLocal = hostname === 'localhost' || hostname === '127.0.0.1'; const isLocal = hostname === 'localhost' || hostname === '127.0.0.1';
return { return {
domain: isLocal ? undefined : '.ktty.is', domain: isLocal ? undefined : '.ktty.is',
secure: !isLocal, // Wymagane HTTPS na produkcji secure: !isLocal,
sameSite: 'Lax' as const, sameSite: 'Lax' as const,
expires: 7 // Token ważny przez 7 dni path: '/',
expires: 7
}; };
}; };
const getSubdomain = (): string | null => {
const hostname = window.location.hostname;
const parts = hostname.split('.');
if (parts.length <= 2) return null;
return parts[0];
};
export function AuthProvider({ children }: { children: ReactNode }) { export function AuthProvider({ children }: { children: ReactNode }) {
const config = getCookieConfig();
const subdomain = getSubdomain();
// Inicjalizacja stanu: sprawdzamy ciasteczko natychmiast przy ładowaniu strony.
// Dzięki temu SSO działa bez opóźnień.
const [token, setToken] = useState<string | null>(() => Cookies.get(TOKEN_KEY) || null); const [token, setToken] = useState<string | null>(() => Cookies.get(TOKEN_KEY) || null);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null); // DODANE
const authRequest = useCallback(async (endpoint: 'signUp' | 'signIn', name: string, pass: string) => { const authRequest = useCallback(async (endpoint: 'signIn' | 'signUp', name: string, pass: string) => {
setLoading(true); setLoading(true);
setError(null); setError(null);
try { try {
const hashedPassword = await sha512(pass); const hashedPassword = await sha512(pass);
// Vite Proxy przekaże to na https://www.ktty.is/api/... const response = await fetch(`/api/v1/user/${endpoint}`, {
const fullUrl = `/api/v1/user/${endpoint}`;
const response = await fetch(fullUrl, {
method: 'POST', method: 'POST',
headers: { headers: { 'Content-Type': 'application/json' },
'accept': 'application/json', credentials: 'include',
'Content-Type': 'application/json', body: JSON.stringify({ name, password: hashedPassword }),
'X-Subdomain': subdomain || '',
},
body: JSON.stringify({ name, password: hashedPassword, ttl: 86400 }),
}); });
const data: AuthResponse = await response.json(); const data = await response.json();
if (!response.ok) { if (!response.ok) {
throw new Error(data?.error || data?.message || 'Błąd autoryzacji'); throw new Error(data?.message || 'Błąd autoryzacji');
} }
if (data?.token) { if (data?.token) {
// Zapisujemy token z flagą domain: '.ktty.is' Cookies.set(TOKEN_KEY, data.token, getCookieConfig());
Cookies.set(TOKEN_KEY, data.token, config);
setToken(data.token); setToken(data.token);
} }
return data; return data;
} catch (err: unknown) { } catch (err: unknown) {
const msg = err instanceof Error ? err.message : 'Wystąpił nieoczekiwany błąd'; const msg = err instanceof Error ? err.message : 'Wystąpił błąd';
setError(msg); setError(msg);
return null; return null;
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [subdomain, config]); }, []);
const logout = useCallback(() => { const logout = useCallback(() => {
// Usuwamy ciasteczko z tej samej domeny, na której zostało zapisane Cookies.remove(TOKEN_KEY, { domain: getCookieConfig().domain, path: '/' });
Cookies.remove(TOKEN_KEY, { domain: config.domain });
setToken(null); setToken(null);
}, [config]); }, []);
return ( return (
<AuthContext.Provider value={{ <AuthContext.Provider value={{
isAuthenticated: !!token, isAuthenticated: !!token,
token, token,
loading, loading,
error, error, // TERAZ OBECNE
signIn: (n, p) => authRequest('signIn', n, p), signIn: (n, p) => authRequest('signIn', n, p),
signUp: (n, p) => authRequest('signUp', n, p), signUp: (n, p) => authRequest('signUp', n, p), // TERAZ OBECNE
logout logout
}}> }}>
{children} {children}

View File

@@ -21,13 +21,23 @@ export default defineConfig(({ mode }) => {
], ],
server: { server: {
port: 6568, port: 6568,
host: true, host: true, // Pozwala na dostęp przez IP w sieci lokalnej
// Jeśli testujesz subdomeny lokalnie (np. app.local.ktty.is), dodaj to:
allowedHosts: [
'.ktty.is',
'localhost',
'127.0.0.1',
],
// vite.config.ts
proxy: { proxy: {
'/api': { '/api': {
target: backendTarget, target: backendTarget,
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,
cookieDomainRewrite: {
"*": ""
},
}, },
}, },
}, },
@@ -35,5 +45,10 @@ export default defineConfig(({ mode }) => {
port: 6568, port: 6568,
allowedHosts: true, allowedHosts: true,
}, },
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
} }
}) })