Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 5,246 Bytes
61d29fc | 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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | // Native fetch-based API client - No axios dependency!
// Handles relative URLs correctly without HTTP/HTTPS conversion issues
// Environment-aware API base URL with NUCLEAR OPTION for production
let API_BASE_URL: string
if (import.meta.env.PROD) {
// π¨ NUCLEAR OPTION: HARDCODE /api in production - IGNORE ALL ENVIRONMENT VARIABLES
// This prevents HuggingFace build secrets from injecting http:// URLs
API_BASE_URL = '/api'
console.log('π [API] Production mode: HARDCODED relative path:', API_BASE_URL)
console.log('π¨ [API] Ignoring all environment variables (nuclear option enabled)')
// SAFETY CHECK: If somehow an http:// URL got through, log a warning
if (typeof import.meta.env.VITE_API_URL === 'string' && import.meta.env.VITE_API_URL.startsWith('http://')) {
console.warn('β οΈ [API] BLOCKED http:// URL from environment:', import.meta.env.VITE_API_URL)
console.warn('β οΈ [API] Using hardcoded /api instead')
}
} else {
// Development: Use environment variable or default to localhost
API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000/api'
console.log('π§ [API] Development mode:', API_BASE_URL)
}
console.log('π‘ [API] Final base URL:', API_BASE_URL)
console.log('π [API] Page protocol:', typeof window !== 'undefined' ? window.location.protocol : 'N/A')
// Response type that matches axios structure
interface APIResponse<T> {
data: T
status: number
statusText: string
}
// Fetch wrapper that mimics axios interface
class APIClient {
private baseURL: string
constructor(baseURL: string) {
this.baseURL = baseURL
}
private async request<T>(
url: string,
options: RequestInit = {}
): Promise<APIResponse<T>> {
// Build full URL
const fullUrl = url.startsWith('http') ? url : `${this.baseURL}${url}`
// π¨ PRODUCTION SAFETY CHECK: Block any http:// URLs
if (import.meta.env.PROD && fullUrl.startsWith('http://')) {
const httpsUrl = fullUrl.replace('http://', 'https://')
console.error('β [API] BLOCKED insecure HTTP request in production:', fullUrl)
console.error('β [API] This would cause Mixed Content errors')
console.error('β [API] Upgrading to HTTPS:', httpsUrl)
throw new Error(`BLOCKED: Attempted to make insecure HTTP request in production: ${fullUrl}`)
}
console.log('π [FETCH] Request URL:', fullUrl)
console.log('π [FETCH] Method:', options.method || 'GET')
// Add auth token if available
const token = localStorage.getItem('auth_token')
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers as Record<string, string>),
}
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
try {
const response = await fetch(fullUrl, {
...options,
headers,
})
// Handle 401 unauthorized
if (response.status === 401) {
localStorage.removeItem('auth_token')
}
// Parse response
let data: T
const contentType = response.headers.get('content-type')
if (contentType && contentType.includes('application/json')) {
data = await response.json()
} else {
data = (await response.text()) as unknown as T
}
if (!response.ok) {
throw {
response: {
data,
status: response.status,
statusText: response.statusText,
},
message: `HTTP ${response.status}: ${response.statusText}`,
}
}
console.log('β
[FETCH] Success:', response.status)
return {
data,
status: response.status,
statusText: response.statusText,
}
} catch (error) {
console.error('β [FETCH] Error:', error)
throw error
}
}
async get<T = any>(url: string, config?: { params?: Record<string, any> }): Promise<APIResponse<T>> {
// Build query string
let fullUrl = url
if (config?.params) {
const params = new URLSearchParams()
Object.entries(config.params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
params.append(key, String(value))
}
})
const queryString = params.toString()
if (queryString) {
fullUrl = `${url}?${queryString}`
}
}
return this.request<T>(fullUrl, { method: 'GET' })
}
async post<T = any>(url: string, data?: any): Promise<APIResponse<T>> {
return this.request<T>(url, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
})
}
async put<T = any>(url: string, data?: any): Promise<APIResponse<T>> {
return this.request<T>(url, {
method: 'PUT',
body: data ? JSON.stringify(data) : undefined,
})
}
async delete<T = any>(url: string): Promise<APIResponse<T>> {
return this.request<T>(url, { method: 'DELETE' })
}
async patch<T = any>(url: string, data?: any): Promise<APIResponse<T>> {
return this.request<T>(url, {
method: 'PATCH',
body: data ? JSON.stringify(data) : undefined,
})
}
}
// Create and export the API client instance
const api = new APIClient(API_BASE_URL)
export default api
|