import { User } from "./types"; import { getApiUrl } from "./config"; export type { User }; const AUTH_KEY = "job-portal-auth"; // API URL now uses runtime config const getApiV1Url = () => `${getApiUrl()}/api/v1`; interface LoginResponse { token: string; user: { id: string; name: string; email: string; roles: string[]; status: string; created_at: string; }; } export async function login( email: string, password: string, role?: "candidate" | "admin" | "company" // Deprecated argument, kept for signature compatibility if needed, but ignored ): Promise { try { console.log("%c[AUTH] Attempting login...", "color: #3b82f6; font-weight: bold", { email }); const res = await fetch(`${getApiV1Url()}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json", }, credentials: "include", // Send and receive cookies body: JSON.stringify({ email, password }), }); if (!res.ok) { if (res.status === 401) { throw new Error("Credenciais inválidas"); } throw new Error("Erro no servidor"); } const data: LoginResponse = await res.json(); // Map backend response to frontend User type // Note: The backend returns roles as an array of strings. The frontend expects a single 'role' or we need to adapt. // For now we map the first role or main role to the 'role' field. let userRole: "candidate" | "admin" | "company" | "superadmin" = "candidate"; // Check for SuperAdmin (Platform Admin) if (data.user.roles.includes("superadmin") || data.user.roles.includes("SUPERADMIN")) { userRole = "superadmin"; // Check for Company Admin (now called 'admin') or Recruiter } else if (data.user.roles.includes("admin") || data.user.roles.includes("recruiter")) { userRole = "company"; } const user: User = { id: data.user.id, name: data.user.name, email: data.user.email, role: userRole, roles: data.user.roles, // Extend User type if needed, or just keep it here profileComplete: 80, // Mocked for now }; // Store user info in sessionStorage (not token - token is in httpOnly cookie) if (typeof window !== "undefined") { localStorage.setItem(AUTH_KEY, JSON.stringify(user)); } return user; } catch (error) { console.error("%c[AUTH] Login Error:", "color: #ef4444; font-weight: bold", error); throw error; } } export async function logout(): Promise { if (typeof window !== "undefined") { localStorage.removeItem(AUTH_KEY); // Call backend to clear the httpOnly cookie try { await fetch(`${getApiV1Url()}/auth/logout`, { method: "POST", credentials: "include", }); } catch (error) { console.error("[AUTH] Logout error:", error); } } } export function getCurrentUser(): User | null { if (typeof window !== "undefined") { const stored = localStorage.getItem(AUTH_KEY); if (stored) { const user = JSON.parse(stored); console.log("%c[AUTH] User Loaded from Storage", "color: #10b981", user.email); return user; } // User not in storage (normal state) console.log("%c[AUTH] No user session found", "color: #94a3b8"); } return null; } // Helper function to map backend roles to frontend role function mapRoleFromBackend(roles: string[]): "candidate" | "admin" | "company" | "superadmin" { if (roles.includes("superadmin") || roles.includes("SUPERADMIN")) { return "superadmin"; } if (roles.includes("admin") || roles.includes("recruiter")) { return "company"; } return "candidate"; } /** * Refreshes the session by calling /users/me with the HTTPOnly cookie. * Use this on app mount to restore session across tabs/reloads. */ export async function refreshSession(): Promise { try { console.log("%c[AUTH] Attempting to refresh session...", "color: #3b82f6"); const res = await fetch(`${getApiV1Url()}/users/me`, { method: "GET", credentials: "include", // Send HTTPOnly cookie }); if (!res.ok) { // Cookie expired or invalid - clear local storage console.log("%c[AUTH] Session refresh: No session", "color: #94a3b8", res.status); localStorage.removeItem(AUTH_KEY); return null; } const userData = await res.json(); const user: User = { id: userData.id, name: userData.name || userData.full_name, email: userData.email, role: mapRoleFromBackend(userData.roles || []), roles: userData.roles || [], profileComplete: 80, }; localStorage.setItem(AUTH_KEY, JSON.stringify(user)); console.log("%c[AUTH] Session restored from cookie", "color: #10b981", user.email); return user; } catch (error) { console.error("%c[AUTH] Failed to refresh session:", "color: #ef4444", error); return null; } } export function isAdminUser(user: User | null): boolean { if (!user) return false; const roles = user.roles || []; return ( user.role === "admin" || roles.includes("superadmin") || roles.includes("admin") || roles.includes("ADMIN") || roles.includes("SUPERADMIN") ); } export function isAuthenticated(): boolean { return getCurrentUser() !== null; } export interface RegisterCandidateData { name: string; email: string; password: string; username: string; // identifier phone: string; } export async function registerCandidate(data: RegisterCandidateData): Promise { console.log('[registerCandidate] Sending request:', { ...data, password: '***' }); const res = await fetch(`${getApiV1Url()}/auth/register`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(data), }); if (!res.ok) { const errorData = await res.json().catch(() => ({})); console.error('[registerCandidate] Error response:', res.status, errorData); throw new Error(errorData.message || `Erro no registro: ${res.status}`); } const responseData = await res.json().catch(() => ({})); console.log('[registerCandidate] Success response:', { ...responseData, token: responseData.token ? '***' : undefined }); } export function getToken(): string | null { // Token is now in httpOnly cookie, not accessible from JS // This function is kept for backward compatibility but returns null return null; } // Company Registration export interface RegisterCompanyData { companyName: string; cnpj: string; email: string; phone: string; } export async function registerCompany(data: RegisterCompanyData): Promise { console.log('[registerCompany] Sending request:', data); // Map frontend fields to backend DTO const payload = { name: data.companyName, document: data.cnpj, contact: data.phone, admin_email: data.email, }; const res = await fetch(`${getApiV1Url()}/companies`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }); if (!res.ok) { const errorData = await res.json().catch(() => ({})); console.error('[registerCompany] Error response:', res.status, errorData); throw new Error(errorData.message || `Erro no registro: ${res.status}`); } const responseData = await res.json().catch(() => ({})); console.log('[registerCompany] Success - Company created:', responseData); }