diff --git a/public/api-client.js b/public/api-client.js index 480127d..ad9bbc2 100644 --- a/public/api-client.js +++ b/public/api-client.js @@ -2,23 +2,44 @@ class ApiClient { constructor() { - this.baseUrl = 'http://localhost:3001'; // Backend server URL + 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 { - const response = await fetch(`${this.baseUrl}/api/cluster/members`, { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - return await response.json(); + return await this.request('/api/cluster/members', { method: 'GET' }); } catch (error) { throw new Error(`Request failed: ${error.message}`); } @@ -26,18 +47,7 @@ class ApiClient { async getDiscoveryInfo() { try { - const response = await fetch(`${this.baseUrl}/api/discovery/nodes`, { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - return await response.json(); + return await this.request('/api/discovery/nodes', { method: 'GET' }); } catch (error) { throw new Error(`Request failed: ${error.message}`); } @@ -45,22 +55,10 @@ class ApiClient { async selectRandomPrimaryNode() { try { - const response = await fetch(`${this.baseUrl}/api/discovery/random-primary`, { + return await this.request('/api/discovery/random-primary', { method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - timestamp: new Date().toISOString() - }) + body: { timestamp: new Date().toISOString() } }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - return await response.json(); } catch (error) { throw new Error(`Request failed: ${error.message}`); } @@ -68,18 +66,7 @@ class ApiClient { async getNodeStatus(ip) { try { - const response = await fetch(`${this.baseUrl}/api/node/status/${encodeURIComponent(ip)}`, { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - return await response.json(); + return await this.request(`/api/node/status/${encodeURIComponent(ip)}`, { method: 'GET' }); } catch (error) { throw new Error(`Request failed: ${error.message}`); } @@ -87,21 +74,7 @@ class ApiClient { async getTasksStatus(ip) { try { - const url = ip - ? `${this.baseUrl}/api/tasks/status?ip=${encodeURIComponent(ip)}` - : `${this.baseUrl}/api/tasks/status`; - const response = await fetch(url, { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - return await response.json(); + return await this.request('/api/tasks/status', { method: 'GET', query: ip ? { ip } : undefined }); } catch (error) { throw new Error(`Request failed: ${error.message}`); } @@ -109,21 +82,7 @@ class ApiClient { async getCapabilities(ip) { try { - const url = ip - ? `${this.baseUrl}/api/capabilities?ip=${encodeURIComponent(ip)}` - : `${this.baseUrl}/api/capabilities`; - const response = await fetch(url, { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - return await response.json(); + return await this.request('/api/capabilities', { method: 'GET', query: ip ? { ip } : undefined }); } catch (error) { throw new Error(`Request failed: ${error.message}`); } @@ -131,19 +90,10 @@ class ApiClient { async callCapability({ ip, method, uri, params }) { try { - const response = await fetch(`${this.baseUrl}/api/proxy-call`, { + return await this.request('/api/proxy-call', { method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ ip, method, uri, params }) + body: { ip, method, uri, params } }); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`); - } - return await response.json(); } catch (error) { throw new Error(`Request failed: ${error.message}`); } @@ -153,18 +103,13 @@ class ApiClient { try { const formData = new FormData(); formData.append('file', file); - - const response = await fetch(`${this.baseUrl}/api/node/update?ip=${encodeURIComponent(nodeIp)}`, { + return await this.request(`/api/node/update`, { method: 'POST', - body: formData + query: { ip: nodeIp }, + body: formData, + isForm: true, + headers: {}, }); - - if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`); - } - - return await response.json(); } catch (error) { throw new Error(`Upload failed: ${error.message}`); } diff --git a/public/components.js b/public/components.js index 821bc9a..796fdbc 100644 --- a/public/components.js +++ b/public/components.js @@ -207,7 +207,7 @@ class ClusterMembersComponent extends Component { if (isLoading) { console.log('ClusterMembersComponent: Showing loading state'); - this.showLoadingState(); + this.renderLoading(`\n