103 lines
3.5 KiB
TypeScript
103 lines
3.5 KiB
TypeScript
import { useState, useCallback, type ReactNode } from 'react';
|
|
import Cookies from 'js-cookie';
|
|
import { AuthContext } from './AuthContext';
|
|
import { sha512 } from '../utils/crypto';
|
|
import type { AuthResponse } from '../types/auth';
|
|
|
|
const TOKEN_KEY = 'ktty_shared_token';
|
|
|
|
// 1. DYNAMICZNE USTALANIE DOMENY
|
|
const getCookieConfig = () => {
|
|
const hostname = window.location.hostname;
|
|
|
|
// 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 };
|
|
};
|
|
|
|
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 }) {
|
|
const config = getCookieConfig();
|
|
const subdomain = getSubdomain();
|
|
|
|
// Inicjalizacja stanu bezpośrednio z ciasteczka
|
|
const [token, setToken] = useState<string | null>(() => Cookies.get(TOKEN_KEY) || null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const authRequest = useCallback(async (endpoint: 'signUp' | 'signIn', name: string, pass: string) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const hashedPassword = await sha512(pass);
|
|
const fullUrl = `/api/v1/user/${endpoint}`;
|
|
|
|
const response = await fetch(fullUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'accept': 'application/json',
|
|
'Content-Type': 'application/json',
|
|
'X-Subdomain': subdomain || '',
|
|
},
|
|
body: JSON.stringify({ name, password: hashedPassword, ttl: 86400 }),
|
|
});
|
|
|
|
const data: AuthResponse = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data?.error || data?.message || 'Błąd logowania');
|
|
}
|
|
|
|
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'
|
|
});
|
|
setToken(data.token);
|
|
}
|
|
return data;
|
|
} catch (err: unknown) {
|
|
let errorMessage = 'Wystąpił nieoczekiwany błąd';
|
|
if (err instanceof Error) errorMessage = err.message;
|
|
setError(errorMessage);
|
|
return null;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [subdomain, config]);
|
|
|
|
const logout = useCallback(() => {
|
|
// 3. USUNIĘCIE MUSI MIEĆ TĘ SAMĄ DOMENĘ CO ZAPIS
|
|
Cookies.remove(TOKEN_KEY, { domain: config.domain });
|
|
setToken(null);
|
|
}, [config]);
|
|
|
|
return (
|
|
<AuthContext.Provider value={{
|
|
isAuthenticated: !!token,
|
|
token,
|
|
loading,
|
|
error,
|
|
signIn: (n, p) => authRequest('signIn', n, p),
|
|
signUp: (n, p) => authRequest('signUp', n, p),
|
|
logout
|
|
}}>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
);
|
|
} |