227 lines
6.6 KiB
JavaScript
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,
|
|
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;
|
|
}
|