Files
spore-ui/public/api-client.js

120 lines
4.0 KiB
JavaScript

// API Client for communicating with the backend
class ApiClient {
constructor() {
this.baseUrl = (typeof window !== 'undefined' && window.API_BASE_URL) || 'http://localhost:3001'; // Backend server URL
}
async request(path, { method = 'GET', headers = {}, body = undefined, query = undefined, isForm = false } = {}) {
const url = new URL(`${this.baseUrl}${path}`);
if (query && typeof query === 'object') {
Object.entries(query).forEach(([k, v]) => {
if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
});
}
const finalHeaders = { 'Accept': 'application/json', ...headers };
const options = { method, headers: finalHeaders };
if (body !== undefined) {
if (isForm) {
options.body = body;
} else {
options.headers['Content-Type'] = options.headers['Content-Type'] || 'application/json';
options.body = typeof body === 'string' ? body : JSON.stringify(body);
}
}
const response = await fetch(url.toString(), options);
let data;
const text = await response.text();
try {
data = text ? JSON.parse(text) : null;
} catch (_) {
data = text; // Non-JSON payload
}
if (!response.ok) {
const message = (data && data.message) || `HTTP ${response.status}: ${response.statusText}`;
throw new Error(message);
}
return data;
}
async getClusterMembers() {
try {
return await this.request('/api/cluster/members', { method: 'GET' });
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async getDiscoveryInfo() {
try {
return await this.request('/api/discovery/nodes', { method: 'GET' });
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async selectRandomPrimaryNode() {
try {
return await this.request('/api/discovery/random-primary', {
method: 'POST',
body: { timestamp: new Date().toISOString() }
});
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async getNodeStatus(ip) {
try {
return await this.request(`/api/node/status/${encodeURIComponent(ip)}`, { method: 'GET' });
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async getTasksStatus(ip) {
try {
return await this.request('/api/tasks/status', { method: 'GET', query: ip ? { ip } : undefined });
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async getCapabilities(ip) {
try {
return await this.request('/api/capabilities', { method: 'GET', query: ip ? { ip } : undefined });
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async callCapability({ ip, method, uri, params }) {
try {
return await this.request('/api/proxy-call', {
method: 'POST',
body: { ip, method, uri, params }
});
} catch (error) {
throw new Error(`Request failed: ${error.message}`);
}
}
async uploadFirmware(file, nodeIp) {
try {
const formData = new FormData();
formData.append('file', file);
return await this.request(`/api/node/update`, {
method: 'POST',
query: { ip: nodeIp },
body: formData,
isForm: true,
headers: {},
});
} catch (error) {
throw new Error(`Upload failed: ${error.message}`);
}
}
}
// Global API client instance
window.apiClient = new ApiClient();