| import axios, {AxiosError, type AxiosRequestConfig} from "axios"; |
|
|
| import webConfig from "@/constants/common-env"; |
| import {clearStoredAuthSession, getStoredAuthKey} from "@/store/auth"; |
|
|
| type RequestConfig = AxiosRequestConfig & { |
| redirectOnUnauthorized?: boolean; |
| }; |
|
|
| type ErrorPayload = { |
| detail?: string | { error?: string | { message?: string } }; |
| error?: string | { message?: string }; |
| message?: string; |
| }; |
|
|
| function errorMessageFromValue(value: unknown): string { |
| if (typeof value === "string") { |
| return value; |
| } |
| if (!value || typeof value !== "object") { |
| return ""; |
| } |
|
|
| const item = value as { error?: unknown; message?: unknown }; |
| if (typeof item.message === "string") { |
| return item.message; |
| } |
| return errorMessageFromValue(item.error); |
| } |
|
|
| export const request = axios.create({ |
| baseURL: webConfig.apiUrl.replace(/\/$/, ""), |
| }); |
|
|
| request.interceptors.request.use(async (config) => { |
| const nextConfig = {...config}; |
| const authKey = await getStoredAuthKey(); |
| const headers = {...(nextConfig.headers || {})} as Record<string, string>; |
| if (authKey && !headers.Authorization) { |
| headers.Authorization = `Bearer ${authKey}`; |
| } |
| |
| |
| nextConfig.headers = headers; |
| return nextConfig; |
| }); |
|
|
| request.interceptors.response.use( |
| (response) => response, |
| async (error: AxiosError<ErrorPayload>) => { |
| const status = error.response?.status; |
| const shouldRedirect = (error.config as RequestConfig | undefined)?.redirectOnUnauthorized !== false; |
| if (status === 401 && shouldRedirect && typeof window !== "undefined") { |
| |
| if (!window.location.pathname.startsWith("/login")) { |
| await clearStoredAuthSession(); |
| window.location.replace("/login"); |
| |
| |
| return new Promise(() => {}); |
| } |
| } |
|
|
| const payload = error.response?.data; |
| const message = |
| errorMessageFromValue(payload?.detail) || |
| errorMessageFromValue(payload?.error) || |
| payload?.message || |
| error.message || |
| `请求失败 (${status || 500})`; |
| return Promise.reject(new Error(message)); |
| }, |
| ); |
|
|
| type RequestOptions = { |
| method?: string; |
| body?: unknown; |
| headers?: Record<string, string>; |
| redirectOnUnauthorized?: boolean; |
| }; |
|
|
| export async function httpRequest<T>(path: string, options: RequestOptions = {}) { |
| const {method = "GET", body, headers, redirectOnUnauthorized = true} = options; |
| const config: RequestConfig = { |
| url: path, |
| method, |
| data: body, |
| headers, |
| redirectOnUnauthorized, |
| }; |
| const response = await request.request<T>(config); |
| return response.data; |
| } |
|
|