import { useCallback, useEffect, useState, type ReactNode } from "react"; import { ApiError, apiRequest, decodeAdminDTO, decodeLoggedOut, decodeLoginResponseDTO, refreshAccessToken, setAccessToken, subscribeSessionInvalidated, type AdminDTO, } from "@/shared/api/client"; import { AuthContext, type AuthStatus } from "@/shared/auth/auth-state"; export function AuthProvider({ children }: { children: ReactNode }) { const [admin, setAdmin] = useState(null); const [status, setStatus] = useState("restoring"); const restoreSession = useCallback(async (): Promise => { setStatus("restoring"); const refreshResult = await refreshAccessToken(); if (refreshResult === "invalid") { setAdmin(null); setStatus("anonymous"); return; } if (refreshResult === "unavailable") { setStatus("unavailable"); return; } try { const value = await apiRequest("/api/admin/v1/me", { retryAuth: false }, decodeAdminDTO); setAdmin(value); setStatus("authenticated"); } catch (error) { setAccessToken(null); setAdmin(null); setStatus(error instanceof ApiError && error.status === 401 ? "anonymous" : "unavailable"); } }, []); useEffect(() => { const unsubscribe = subscribeSessionInvalidated(() => { setAdmin(null); setStatus("anonymous"); }); const restoreTimer = window.setTimeout(() => { void restoreSession(); }, 0); return () => { window.clearTimeout(restoreTimer); unsubscribe(); }; }, [restoreSession]); async function login(username: string, password: string): Promise { const response = await apiRequest("/api/admin/v1/auth/login", { method: "POST", body: { username, password }, authenticated: false, retryAuth: false, }, decodeLoginResponseDTO); setAccessToken(response.tokens.accessToken); setAdmin(response.admin); setStatus("authenticated"); } async function logout(): Promise { try { await apiRequest("/api/admin/v1/auth/logout", { method: "POST", body: {}, authenticated: false, retryAuth: false, }, decodeLoggedOut); } finally { setAccessToken(null); setAdmin(null); setStatus("anonymous"); } } async function changePassword(currentPassword: string, newPassword: string): Promise { await apiRequest("/api/admin/v1/me/password", { method: "PUT", body: { currentPassword, newPassword }, }, () => undefined); } return ( {children} ); }