import { computed, readonly, ref } from "vue"; import { loginRequest, logoutRequest, refreshTokenRequest } from "../api/auth.js"; import { setAccessToken, setRefreshCallback, setAuthErrorCallback } from "../api/client.js"; import { resetChatSingleton } from "./useChat.js"; const LOCAL_STORAGE_KEY = "auth.session"; const SESSION_STORAGE_KEY = "auth.session.temp"; function getStorage(kind) { if (typeof window === "undefined") return null; return kind === "local" ? window.localStorage : window.sessionStorage; } function loadStoredSession() { for (const kind of ["local", "session"]) { try { const storage = getStorage(kind); const raw = storage?.getItem(kind === "local" ? LOCAL_STORAGE_KEY : SESSION_STORAGE_KEY); if (!raw) continue; const parsed = JSON.parse(raw); if (parsed?.usuario) return parsed; } catch {} } return null; } function persistSession(sess) { try { getStorage("local")?.removeItem(LOCAL_STORAGE_KEY); getStorage("session")?.removeItem(SESSION_STORAGE_KEY); if (sess?.rememberMe) { getStorage("local")?.setItem(LOCAL_STORAGE_KEY, JSON.stringify(sess)); return; } if (sess) { getStorage("session")?.setItem(SESSION_STORAGE_KEY, JSON.stringify(sess)); } } catch {} } const session = ref(loadStoredSession()); const user = computed(() => session.value?.usuario ?? null); const isAuthenticated = computed(() => Boolean(user.value?.Id)); let _proactiveRefreshTimer = null; function scheduleProactiveRefresh(token) { clearTimeout(_proactiveRefreshTimer); if (!token) return; try { const payload = JSON.parse(atob(token.split(".")[1])); const delay = payload.exp * 1000 - Date.now() - 60_000; if (delay <= 0) return; _proactiveRefreshTimer = setTimeout(async () => { const rt = session.value?.refreshToken; if (!rt) return; try { const result = await refreshTokenRequest({ refreshToken: rt }); session.value = { ...session.value, accessToken: result.accessToken }; persistSession(session.value); setAccessToken(result.accessToken); scheduleProactiveRefresh(result.accessToken); } catch { // 401 interceptor handles expired token on next request } }, delay); } catch { // token não é JWT válido, ignora } } function setupAuth() { const token = session.value?.accessToken ?? null; setAccessToken(token); if (!token) return; scheduleProactiveRefresh(token); setRefreshCallback(async () => { const rt = session.value?.refreshToken; if (!rt) throw new Error("no_refresh_token"); const result = await refreshTokenRequest({ refreshToken: rt }); session.value = { ...session.value, accessToken: result.accessToken }; persistSession(session.value); setAccessToken(result.accessToken); scheduleProactiveRefresh(result.accessToken); return result.accessToken; }); setAuthErrorCallback(() => { clearTimeout(_proactiveRefreshTimer); resetChatSingleton(); session.value = null; persistSession(null); setAccessToken(null); setRefreshCallback(null); setAuthErrorCallback(null); window.location.href = "/login"; }); } setupAuth(); async function login({ login, senha, rememberMe = false }) { const payload = await loginRequest({ login, senha, rememberMe }); session.value = { usuario: payload.usuario, accessToken: payload.accessToken ?? null, refreshToken: payload.refreshToken ?? null, rememberMe, savedAt: new Date().toISOString() }; persistSession(session.value); setupAuth(); return payload; } async function logout() { clearTimeout(_proactiveRefreshTimer); const rt = session.value?.refreshToken; try { await logoutRequest({ refreshToken: rt }); } finally { resetChatSingleton(); session.value = null; persistSession(null); setAccessToken(null); setRefreshCallback(null); setAuthErrorCallback(null); } } function clearSession() { session.value = null; persistSession(null); setAccessToken(null); setRefreshCallback(null); setAuthErrorCallback(null); } export function useAuth() { return { session: readonly(session), user, isAuthenticated, login, logout, clearSession }; }