File size: 2,466 Bytes
e026dc9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from "axios";
import { toast } from "react-hot-toast";

// Use relative URL if NEXT_PUBLIC_API_URL is empty (same domain deployment)
// Otherwise use the provided URL or default to localhost
const API_URL = process.env.NEXT_PUBLIC_API_URL === "" 
  ? ""  // Empty string means use relative URLs (same domain)
  : (process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000");

// Create axios instance
const apiClient: AxiosInstance = axios.create({
  baseURL: API_URL,
  timeout: 300000, // 5 minutes for long-running operations
  headers: {
    "Content-Type": "application/json",
  },
});

// Request interceptor
apiClient.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // Add auth token if available
    if (typeof window !== "undefined") {
      const authStorage = localStorage.getItem("auth-storage");
      if (authStorage) {
        try {
          const auth = JSON.parse(authStorage);
          if (auth.state?.token) {
            config.headers.Authorization = `Bearer ${auth.state.token}`;
          }
        } catch (e) {
          // Ignore parsing errors
        }
      }
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// Response interceptor for error handling
apiClient.interceptors.response.use(
  (response) => response,
  (error: AxiosError) => {
    if (error.response) {
      // Server responded with error status
      const status = error.response.status;
      const message = (error.response.data as any)?.detail || error.message || "An error occurred";
      
      // Handle 401 Unauthorized - redirect to login
      if (status === 401) {
        if (typeof window !== "undefined" && !window.location.pathname.includes("/login")) {
          // Clear auth storage
          localStorage.removeItem("auth-storage");
          // Redirect to login
          window.location.href = "/login";
        }
      }
      
      // Don't show toast for 404s (handled in components) or 401s (handled above)
      if (status !== 404 && status !== 401) {
        toast.error(message);
      }
    } else if (error.request) {
      // Request made but no response
      toast.error("Network error. Please check your connection.");
    } else {
      // Something else happened
      toast.error("An unexpected error occurred");
    }
    
    return Promise.reject(error);
  }
);

export default apiClient;