// Sesión de autenticación contra el API Gateway (JWT guardado en localStorage) const API_URL = import.meta.env.VITE_API_URL || '/api' const STORAGE_KEY = 'frasa_auth' export interface UsuarioSesion { nombre: string rol: string asignacion: string | null acciones?: string[] } export interface Sesion { token: string usuario: UsuarioSesion } /** Error de login con mensaje genérico (no filtra si el usuario existe) */ export class ErrorLogin extends Error { constructor(message = 'Usuario o contraseña incorrectos') { super(message) this.name = 'ErrorLogin' } } export function getSesion(): Sesion | null { try { const raw = localStorage.getItem(STORAGE_KEY) if (!raw) return null const sesion = JSON.parse(raw) as Sesion if (typeof sesion?.token !== 'string' || !sesion.usuario) return null return sesion } catch { return null } } export function getToken(): string | null { return getSesion()?.token ?? null } export function logout(): void { localStorage.removeItem(STORAGE_KEY) } /** POST /auth/login; guarda token + usuario en localStorage */ export async function login(username: string, password: string): Promise { let res: Response try { res = await fetch(`${API_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }) } catch { throw new ErrorLogin('No se pudo conectar con el servidor') } if (!res.ok) throw new ErrorLogin() const data = (await res.json()) as Sesion localStorage.setItem(STORAGE_KEY, JSON.stringify({ token: data.token, usuario: data.usuario })) return data.usuario } /** * Agrega Authorization: Bearer a un RequestInit si hay sesión. * Sin token devuelve el init tal cual (las llamadas siguen iguales que antes). */ export function conAuth(init: RequestInit = {}): RequestInit { const token = getToken() if (!token) return init return { ...init, headers: { ...(init.headers as Record), Authorization: `Bearer ${token}` } } } /** Si el API responde 401: cierra sesión y recarga para volver al login */ export function manejar401(res: Response): void { if (res.status !== 401) return logout() if (typeof window !== 'undefined') window.location.reload() } /** * POST /auth/cambiar-password. Lanza Error con el mensaje del API cuando * falla (p. ej. "La contraseña actual no es correcta"); no toca la sesión. */ export async function cambiarPassword(actual: string, nueva: string): Promise { let res: Response try { res = await fetch( `${API_URL}/auth/cambiar-password`, conAuth({ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ actual, nueva }), }), ) } catch { throw new Error('No se pudo conectar con el servidor') } manejar401(res) if (!res.ok) { const body = (await res.json().catch(() => null)) as { error?: string } | null throw new Error(body?.error ?? `Error ${res.status}`) } }