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 { // V2 API key auth (primary) const apiKey = localStorage.getItem('kb_api_key'); const activeTenantId = localStorage.getItem('kb_active_tenant_id'); // Legacy JWT token (fallback, kept for compatibility during transition) const token = localStorage.getItem('authToken') || localStorage.getItem('token'); const language = localStorage.getItem('userLanguage') || 'ja'; return { 'Content-Type': 'application/json', 'x-user-language': language, ...(apiKey && { 'x-api-key': apiKey }), ...(activeTenantId && { 'x-tenant-id': activeTenantId }), ...(token && { Authorization: `Bearer ${token}` }), }; } private async handleResponse(response: Response): Promise> { const text = await response.text(); let data: any; try { data = text ? JSON.parse(text) : null; } catch (e) { data = null; } if (!response.ok) { throw new Error(data?.message || text || 'Request failed'); } return { data: data as T, status: response.status }; } // 新しい API 呼び出し方法、{ data, status } を返す async get(url: string): Promise> { const response = await fetch(`${this.baseURL}${url}`, { method: 'GET', headers: this.getAuthHeaders(), }); return this.handleResponse(response); } 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, }); return this.handleResponse(response); } 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, }); return this.handleResponse(response); } async patch(url: string, body?: any): Promise> { const response = await fetch(`${this.baseURL}${url}`, { method: 'PATCH', headers: this.getAuthHeaders(), body: body ? JSON.stringify(body) : undefined, }); return this.handleResponse(response); } async delete(url: string): Promise> { const response = await fetch(`${this.baseURL}${url}`, { method: 'DELETE', headers: this.getAuthHeaders(), }); return this.handleResponse(response); } // New methods for special formats async getBlob(url: string): Promise { const response = await fetch(`${this.baseURL}${url}`, { method: 'GET', headers: this.getAuthHeaders(), }); if (!response.ok) { throw new Error('Request failed'); } return await response.blob(); } async postMultipart(url: string, formData: FormData): Promise> { const headers = this.getAuthHeaders(); // Remove Content-Type to let the browser set it with the correct boundary delete headers['Content-Type']; const response = await fetch(`${this.baseURL}${url}`, { method: 'POST', headers, body: formData, }); return this.handleResponse(response); } // Legacy compatibility method — returns raw Response for streaming and other special cases async request(path: string, options: RequestInit = {}): Promise { const apiKey = localStorage.getItem('kb_api_key'); const activeTenantId = localStorage.getItem('kb_active_tenant_id'); const token = localStorage.getItem('authToken'); const headers = new Headers(options.headers); if (apiKey) headers.set('x-api-key', apiKey); if (activeTenantId) headers.set('x-tenant-id', activeTenantId); if (token) headers.set('Authorization', `Bearer ${token}`); 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) { localStorage.removeItem('kb_api_key'); localStorage.removeItem('authToken'); window.location.href = '/login'; throw new Error('Unauthorized'); } return response; } } export const apiClient = new ApiClient(API_BASE_URL);