import type { LoginRequest, LoginResponse, RefreshTokenResponse, AuthError, UserData } from '@/types/auth'; import { API_CONFIG, AUTH_ENDPOINTS, DEFAULT_HEADERS, buildApiUrl } from '@/lib/config'; import { debugRequest } from '@/lib/debug-utils'; import { ENV_CONFIG } from '@/lib/env-config'; /** * Classe de erro customizada para autenticação */ export class AuthenticationError extends Error { constructor( message: string, public code: string, public details?: any ) { super(message); this.name = 'AuthenticationError'; } } /** * Headers para requisições autenticadas (COM Bearer token) */ function getAuthHeaders(token: string): Record { return { "Content-Type": "application/json", "Accept": "application/json", "apikey": ENV_CONFIG.SUPABASE_ANON_KEY, "Authorization": `Bearer ${token}`, }; } /** * Headers APENAS para login (SEM Authorization Bearer) */ function getLoginHeaders(): Record { return { "Content-Type": "application/json", "Accept": "application/json", "apikey": ENV_CONFIG.SUPABASE_ANON_KEY, }; } /** * Utilitário para processar resposta da API */ async function processResponse(response: Response): Promise { console.log(`[AUTH] Response status: ${response.status} ${response.statusText}`); let data: any = null; try { const text = await response.text(); if (text) { data = JSON.parse(text); } } catch (error) { console.log('[AUTH] Response sem JSON ou vazia (normal para alguns endpoints)'); } if (!response.ok) { const errorMessage = data?.message || data?.error || response.statusText || 'Erro na autenticação'; const errorCode = data?.code || String(response.status); console.error('[AUTH ERROR]', { url: response.url, status: response.status, data, }); throw new AuthenticationError(errorMessage, errorCode, data); } console.log('[AUTH] Response data:', data); return data as T; } /** * Serviço para fazer login e obter token JWT * POST /auth/v1/token?grant_type=password */ export async function loginUser( email: string, password: string ): Promise { const url = `${AUTH_ENDPOINTS.LOGIN}?grant_type=password`; const headers = getLoginHeaders(); const body = JSON.stringify({ email, password }); console.log('[AUTH] Login request:', { url, email }); const response = await fetch(url, { method: 'POST', headers, body, }); return processResponse(response); } /** * Serviço para fazer logout do usuário * POST /auth/v1/logout */ export async function logoutUser(token: string): Promise { const url = AUTH_ENDPOINTS.LOGOUT; const headers = getAuthHeaders(token); console.log('[AUTH] Logout request'); const response = await fetch(url, { method: 'POST', headers, }); if (!response.ok && response.status !== 204) { await processResponse(response); } } /** * Serviço para renovar token JWT * POST /auth/v1/token?grant_type=refresh_token */ export async function refreshAuthToken(refreshToken: string): Promise { const url = `${AUTH_ENDPOINTS.LOGIN}?grant_type=refresh_token`; const headers = getLoginHeaders(); const body = JSON.stringify({ refresh_token: refreshToken }); console.log('[AUTH] Refresh token request'); const response = await fetch(url, { method: 'POST', headers, body, }); return processResponse(response); } /** * Serviço para obter dados do usuário atual * GET /auth/v1/user */ export async function getCurrentUser(token: string): Promise { const url = AUTH_ENDPOINTS.USER; const headers = getAuthHeaders(token); console.log('[AUTH] Get current user request'); const response = await fetch(url, { method: 'GET', headers, }); return processResponse(response); } /** * Utilitário para validar se um token está expirado */ export function isTokenExpired(expiryTimestamp: number): boolean { const now = Date.now(); const expiry = expiryTimestamp * 1000; // Converter para milliseconds const buffer = 5 * 60 * 1000; // Buffer de 5 minutos return now >= (expiry - buffer); } /** * Utilitário para interceptar requests e adicionar token automaticamente */ export function createAuthenticatedFetch(getToken: () => string | null) { return async (url: string, options: RequestInit = {}): Promise => { const token = getToken(); if (token) { const headers = { ...options.headers, ...getAuthHeaders(token), }; options = { ...options, headers, }; } return fetch(url, options); }; }