FPSMS-frontend
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 

81 行
2.6 KiB

  1. import axios, { AxiosError, InternalAxiosRequestConfig } from "axios";
  2. import {
  3. LOGIN_SESSION_EXPIRED_HREF,
  4. readStoredRefreshToken,
  5. refreshBackendAccessToken,
  6. } from "@/app/utils/authToken";
  7. const axiosInstance = axios.create({
  8. baseURL: process.env.API_URL || process.env.NEXT_PUBLIC_API_URL,
  9. });
  10. type RetriableConfig = InternalAxiosRequestConfig & { _retry?: boolean };
  11. let requestInterceptorId: number | null = null;
  12. let responseInterceptorId: number | null = null;
  13. const clearInterceptors = () => {
  14. if (requestInterceptorId !== null) {
  15. axiosInstance.interceptors.request.eject(requestInterceptorId);
  16. requestInterceptorId = null;
  17. }
  18. if (responseInterceptorId !== null) {
  19. axiosInstance.interceptors.response.eject(responseInterceptorId);
  20. responseInterceptorId = null;
  21. }
  22. };
  23. /**
  24. * Always prefer localStorage accessToken (may have been refreshed) over any
  25. * closed-over token passed into SetupAxiosInterceptors.
  26. */
  27. export const SetupAxiosInterceptors = (_inputToken: string | null) => {
  28. clearInterceptors();
  29. requestInterceptorId = axiosInstance.interceptors.request.use(
  30. (config) => {
  31. try {
  32. const token =
  33. (typeof window !== "undefined"
  34. ? localStorage.getItem("accessToken")
  35. : null) ?? _inputToken;
  36. if (token) {
  37. config.headers.Authorization = `Bearer ${token}`;
  38. }
  39. } catch {
  40. if (_inputToken) {
  41. config.headers.Authorization = `Bearer ${_inputToken}`;
  42. }
  43. }
  44. return config;
  45. },
  46. (error) => Promise.reject(error),
  47. );
  48. responseInterceptorId = axiosInstance.interceptors.response.use(
  49. (response) => response,
  50. async (error: AxiosError) => {
  51. const status = error.response?.status;
  52. const original = error.config as RetriableConfig | undefined;
  53. // Only 401 = unauthenticated. Do not treat 403 (forbidden) as session expiry.
  54. if (status === 401 && original && !original._retry && typeof window !== "undefined") {
  55. original._retry = true;
  56. const refreshed = await refreshBackendAccessToken(readStoredRefreshToken());
  57. if (refreshed) {
  58. original.headers = original.headers ?? {};
  59. original.headers.Authorization = `Bearer ${refreshed}`;
  60. axiosInstance.defaults.headers.Authorization = `Bearer ${refreshed}`;
  61. return axiosInstance(original);
  62. }
  63. window.location.href = LOGIN_SESSION_EXPIRED_HREF;
  64. }
  65. console.error("API Error:", error.response?.data || error);
  66. return Promise.reject(error);
  67. },
  68. );
  69. };
  70. export default axiosInstance;