client.ts 1.52 KB
import axios, { AxiosError, AxiosResponse } from 'axios';
import { BizError } from './errors';

let getAccessToken: () => string | null = () => null;

/**
 * 注册 token 提供者。Redux store 初始化后由 store/index.ts 调用,
 * 把 store.getState().auth.accessToken 接进来。
 * 避免直接 import store 形成循环依赖。
 */
export function registerAccessTokenProvider(fn: () => string | null) {
  getAccessToken = fn;
}

export const apiClient = axios.create({
  baseURL: (import.meta as any).env?.VITE_API_BASE_URL ?? '/api/v1',
  timeout: 10000,
});

apiClient.interceptors.request.use((config) => {
  const token = getAccessToken();
  if (token) {
    config.headers.set('Authorization', `Bearer ${token}`);
  }
  return config;
});

apiClient.interceptors.response.use(
  (response: AxiosResponse) => {
    const body = response.data;
    if (body && typeof body === 'object' && 'code' in body) {
      if (body.code === 200) {
        return body.data;
      }
      throw new BizError(body.code, body.message ?? '业务错误', body.data);
    }
    return body;
  },
  (error: AxiosError) => {
    if (error.response) {
      const body = error.response.data as { code?: number; message?: string; data?: unknown } | undefined;
      if (body && typeof body === 'object' && 'code' in body) {
        throw new BizError(body.code!, body.message ?? '请求失败', body.data);
      }
      throw new BizError(error.response.status, error.response.statusText ?? 'HTTP error');
    }
    throw new BizError(-1, 'NETWORK');
  },
);