diff --git a/frontend/src/services/http.ts b/frontend/src/services/http.ts index 20495e3..2604f05 100644 --- a/frontend/src/services/http.ts +++ b/frontend/src/services/http.ts @@ -1,3 +1,5 @@ +import { useAuthStore } from '@/stores/auth' + export type ApiSuccess = { success: true data: T @@ -27,6 +29,35 @@ export class ApiError extends Error { } } +let lastUnauthorizedHandledAt = 0 + +function handleUnauthorized(token?: string | null) { + if (!token) return + if (typeof window === 'undefined') return + + const now = Date.now() + if (now - lastUnauthorizedHandledAt < 1000) return + lastUnauthorizedHandledAt = now + + try { + const auth = useAuthStore() + auth.logout() + } catch { + localStorage.removeItem('imageforge_auth') + } + + if (window.location.pathname === '/login') { + return + } + + const redirect = encodeURIComponent(`${window.location.pathname}${window.location.search}`) + window.location.replace(`/login?redirect=${redirect}`) +} + +function createApiError(status: number, error: ApiFailure['error']) { + return new ApiError(error.code, status, error.message, error.request_id) +} + async function parseEnvelope(res: Response): Promise> { const text = await res.text() if (!text) { @@ -86,7 +117,11 @@ export async function apiJson( const envelope = await parseEnvelope(res) if (envelope.success) return envelope.data - throw new ApiError(envelope.error.code, res.status, envelope.error.message, envelope.error.request_id) + if (res.status === 401 || envelope.error.code === 'UNAUTHORIZED') { + handleUnauthorized(token) + } + + throw createApiError(res.status, envelope.error) } export async function apiGet(path: string, token?: string | null, init?: RequestInit): Promise { @@ -102,7 +137,11 @@ export async function apiGet(path: string, token?: string | null, init?: Requ const envelope = await parseEnvelope(res) if (envelope.success) return envelope.data - throw new ApiError(envelope.error.code, res.status, envelope.error.message, envelope.error.request_id) + if (res.status === 401 || envelope.error.code === 'UNAUTHORIZED') { + handleUnauthorized(token) + } + + throw createApiError(res.status, envelope.error) } export async function apiMultipart( @@ -124,6 +163,9 @@ export async function apiMultipart( const envelope = await parseEnvelope(res) if (envelope.success) return envelope.data - throw new ApiError(envelope.error.code, res.status, envelope.error.message, envelope.error.request_id) -} + if (res.status === 401 || envelope.error.code === 'UNAUTHORIZED') { + handleUnauthorized(token) + } + throw createApiError(res.status, envelope.error) +}