fix(auth): auto-logout and redirect on 401
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
|
||||||
export type ApiSuccess<T> = {
|
export type ApiSuccess<T> = {
|
||||||
success: true
|
success: true
|
||||||
data: T
|
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<T>(res: Response): Promise<ApiEnvelope<T>> {
|
async function parseEnvelope<T>(res: Response): Promise<ApiEnvelope<T>> {
|
||||||
const text = await res.text()
|
const text = await res.text()
|
||||||
if (!text) {
|
if (!text) {
|
||||||
@@ -86,7 +117,11 @@ export async function apiJson<T>(
|
|||||||
const envelope = await parseEnvelope<T>(res)
|
const envelope = await parseEnvelope<T>(res)
|
||||||
if (envelope.success) return envelope.data
|
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<T>(path: string, token?: string | null, init?: RequestInit): Promise<T> {
|
export async function apiGet<T>(path: string, token?: string | null, init?: RequestInit): Promise<T> {
|
||||||
@@ -102,7 +137,11 @@ export async function apiGet<T>(path: string, token?: string | null, init?: Requ
|
|||||||
const envelope = await parseEnvelope<T>(res)
|
const envelope = await parseEnvelope<T>(res)
|
||||||
if (envelope.success) return envelope.data
|
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<T>(
|
export async function apiMultipart<T>(
|
||||||
@@ -124,6 +163,9 @@ export async function apiMultipart<T>(
|
|||||||
const envelope = await parseEnvelope<T>(res)
|
const envelope = await parseEnvelope<T>(res)
|
||||||
if (envelope.success) return envelope.data
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user