// API client for making requests to the backend class ApiClient { constructor() { this.baseUrl = ''; this.apiKey = ''; } updateConfig() { this.baseUrl = config.getApiBaseUrl(); this.apiKey = config.getApiKey(); } getHeaders(includeContentType = true) { const headers = { 'X-API-Key': this.apiKey }; if (includeContentType) { headers['Content-Type'] = 'application/json'; } return headers; } async makeRequest(method, endpoint, data = null, isFormData = false) { this.updateConfig(); if (!this.baseUrl || !this.apiKey) { throw new Error('API not configured. Please set API base URL and key.'); } const url = `${this.baseUrl}/api/v1${endpoint}`; const options = { method, headers: this.getHeaders(!isFormData) }; if (data) { if (isFormData) { options.body = data; // FormData object } else { options.body = JSON.stringify(data); } } try { const response = await fetch(url, options); // Handle different response types if (response.status === 204) { return null; // No content } const contentType = response.headers.get('content-type'); let responseData; if (contentType && contentType.includes('application/json')) { responseData = await response.json(); } else { responseData = await response.text(); } if (!response.ok) { const errorMessage = responseData?.detail || responseData?.message || `HTTP ${response.status}`; throw new Error(errorMessage); } return responseData; } catch (error) { console.error(`API request failed: ${method} ${url}`, error); throw error; } } // Teams API async getTeams() { return this.makeRequest('GET', '/teams'); } async createTeam(teamData) { return this.makeRequest('POST', '/teams', teamData); } async updateTeam(teamId, teamData) { return this.makeRequest('PUT', `/teams/${teamId}`, teamData); } async deleteTeam(teamId) { return this.makeRequest('DELETE', `/teams/${teamId}`); } // Users API async getUsers() { return this.makeRequest('GET', '/users'); } async createUser(userData) { return this.makeRequest('POST', '/users', userData); } async updateUser(userId, userData) { return this.makeRequest('PUT', `/users/${userId}`, userData); } async deleteUser(userId) { return this.makeRequest('DELETE', `/users/${userId}`); } // API Keys API async getApiKeys() { return this.makeRequest('GET', '/auth/api-keys'); } async createApiKey(keyData) { return this.makeRequest('POST', '/auth/api-keys', keyData); } async deleteApiKey(keyId) { return this.makeRequest('DELETE', `/auth/api-keys/${keyId}`); } // Images API async getImages(page = 1, limit = 20, tags = null) { let endpoint = `/images?page=${page}&limit=${limit}`; if (tags) { endpoint += `&tags=${encodeURIComponent(tags)}`; } return this.makeRequest('GET', endpoint); } async getImage(imageId) { return this.makeRequest('GET', `/images/${imageId}`); } async uploadImage(formData) { return this.makeRequest('POST', '/images', formData, true); } async updateImage(imageId, imageData) { return this.makeRequest('PUT', `/images/${imageId}`, imageData); } async deleteImage(imageId) { return this.makeRequest('DELETE', `/images/${imageId}`); } // Search API async searchImages(query, similarityThreshold = 0.7, maxResults = 20, tags = null) { const searchData = { query, similarity_threshold: similarityThreshold, max_results: maxResults }; if (tags) { searchData.tags = tags; } return this.makeRequest('POST', '/search', searchData); } // Bootstrap API async bootstrap(bootstrapData) { return this.makeRequest('POST', '/auth/bootstrap', bootstrapData); } // Health check async healthCheck() { this.updateConfig(); const response = await fetch(`${this.baseUrl}/health`); return response.ok; } // Get image URL for display getImageUrl(imageId) { this.updateConfig(); return `${this.baseUrl}/api/v1/images/${imageId}/file`; } // Get image thumbnail URL getThumbnailUrl(imageId, size = 'medium') { this.updateConfig(); return `${this.baseUrl}/api/v1/images/${imageId}/thumbnail?size=${size}`; } } // Global API client instance const apiClient = new ApiClient(); // Helper function to handle API errors consistently function handleApiError(error, context = '') { console.error(`API Error ${context}:`, error); let message = error.message || 'An unknown error occurred'; // Handle common error types if (message.includes('Failed to fetch') || message.includes('NetworkError')) { message = 'Unable to connect to the server. Please check your connection and API configuration.'; } else if (message.includes('401')) { message = 'Authentication failed. Please check your API key.'; } else if (message.includes('403')) { message = 'Access denied. You don\'t have permission to perform this action.'; } else if (message.includes('404')) { message = 'The requested resource was not found.'; } else if (message.includes('500')) { message = 'Server error. Please try again later.'; } showAlert(message, 'danger'); return message; }