2025-05-25 17:38:47 +02:00

227 lines
6.6 KiB
JavaScript

// 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 = {};
if (this.apiKey) {
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) {
throw new Error('API not configured. Please set API base URL.');
}
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) {
const skip = (page - 1) * limit;
let endpoint = `/images?skip=${skip}&limit=${limit}`;
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}`);
}
// Download image with authentication and return blob URL
async downloadImageBlob(imageId) {
this.updateConfig();
if (!this.baseUrl) {
throw new Error('API not configured. Please set API base URL.');
}
const url = `${this.baseUrl}/api/v1/images/${imageId}/download`;
const options = {
method: 'GET',
headers: this.getHeaders(false) // Don't include Content-Type for binary data
};
try {
const response = await fetch(url, options);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP ${response.status}: ${errorText}`);
}
const blob = await response.blob();
return URL.createObjectURL(blob);
} catch (error) {
console.error(`Image download failed: ${url}`, error);
throw error;
}
}
// Search API
async searchImages(query, similarityThreshold, maxResults = 20) {
const searchData = {
query,
similarity_threshold: similarityThreshold,
limit: maxResults
};
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;
}
}
// 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;
}