import { API_BASE_URL } from '../utils/constants'; interface ApiResponse { data: T; status: number; } class ApiClient { private baseURL: string; constructor(baseURL: string) { this.baseURL = baseURL; } private getAuthHeaders(): Record { const rawApiKey = localStorage.getItem('kb_api_key'); const rawToken = localStorage.getItem('authToken') || localStorage.getItem('token'); const activeTenantId = localStorage.getItem('kb_active_tenant_id'); const language = localStorage.getItem('userLanguage') || 'ja'; // Helper to filter out invalid values const isValid = (val: string | null) => { if (!val) return false; const v = val.trim().toLowerCase(); return v !== '' && v !== 'undefined' && v !== 'null' && v !== '[object object]'; }; const apiKey = isValid(rawApiKey) ? rawApiKey : null; const token = isValid(rawToken) ? rawToken : null; const headers: Record = { 'Content-Type': 'application/json', 'x-user-language': language, }; if (apiKey) { if (apiKey.startsWith('kb_')) { headers['x-api-key'] = apiKey; } else { headers['Authorization'] = `Bearer ${apiKey}`; } } else if (token) { headers['Authorization'] = `Bearer ${token}`; } // Final fail-safe: Ensure no header is 'undefined' string Object.keys(headers).forEach(key => { if (headers[key]?.toLowerCase().includes('undefined')) { delete headers[key]; } }); if (activeTenantId && isValid(activeTenantId)) { headers['x-tenant-id'] = activeTenantId; } // DEBUG: Only log first few chars console.log('[ApiClient] Auth Headers:', { hasApiKey: !!headers['x-api-key'], hasAuth: !!headers['Authorization'], authPreview: headers['Authorization']?.substring(0, 20), tenantId: headers['x-tenant-id'] }); return headers; } // New API call method, returns { data, status } async get(url: string): Promise> { const response = await fetch(`${this.baseURL}${url}`, { method: 'GET', headers: this.getAuthHeaders(), }); const data = await response.json(); if (response.status === 401) { this.handleUnauthorized(); throw new Error(data.message || 'Unauthorized'); } if (!response.ok) { throw new Error(data.message || 'Request failed'); } return { data, status: response.status }; } async post(url: string, body?: any): Promise> { const response = await fetch(`${this.baseURL}${url}`, { method: 'POST', headers: this.getAuthHeaders(), body: body ? JSON.stringify(body) : undefined, }); const data = await response.json(); if (response.status === 401) { this.handleUnauthorized(); throw new Error(data.message || 'Unauthorized'); } if (!response.ok) { throw new Error(data.message || 'Request failed'); } return { data, status: response.status }; } async put(url: string, body?: any): Promise> { const response = await fetch(`${this.baseURL}${url}`, { method: 'PUT', headers: this.getAuthHeaders(), body: body ? JSON.stringify(body) : undefined, }); const data = await response.json(); if (response.status === 401) { this.handleUnauthorized(); throw new Error(data.message || 'Unauthorized'); } if (!response.ok) { throw new Error(data.message || 'Request failed'); } return { data, status: response.status }; } async delete(url: string): Promise> { const response = await fetch(`${this.baseURL}${url}`, { method: 'DELETE', headers: this.getAuthHeaders(), }); const data = await response.json(); if (response.status === 401) { this.handleUnauthorized(); throw new Error(data.message || 'Unauthorized'); } if (!response.ok) { throw new Error(data.message || 'Request failed'); } return { data, status: response.status }; } // Legacy compatibility method — returns raw Response for streaming and other special cases async request(path: string, options: RequestInit = {}): Promise { const authHeaders = this.getAuthHeaders(); const headers = new Headers(options.headers); Object.entries(authHeaders).forEach(([k, v]) => headers.set(k, v)); const language = localStorage.getItem('userLanguage') || 'ja'; headers.set('x-user-language', language); let url = path; if (!path.startsWith('http')) { const cleanPath = path.startsWith('/') ? path : `/${path}`; url = `${this.baseURL}${cleanPath}`; } const response = await fetch(url, { ...options, headers, }); if (response.status === 401) { this.handleUnauthorized(); throw new Error('Unauthorized'); } return response; } private handleUnauthorized() { console.warn('[ApiClient] 401 Unauthorized detected. Cleaning up and redirecting to login...'); localStorage.removeItem('kb_api_key'); localStorage.removeItem('authToken'); localStorage.removeItem('token'); localStorage.removeItem('kb_active_tenant_id'); // Only redirect if we are not already on the login page if (window.location.pathname !== '/login') { window.location.href = '/login'; } } } export const apiClient = new ApiClient(API_BASE_URL);