From: Giorgio Ravera Date: Thu, 28 May 2026 17:02:04 +0000 (+0200) Subject: Refactored js services X-Git-Url: http://git.giorgioravera.it/?a=commitdiff_plain;h=a7af27449698919a0ee45be6d38e3aa5893cefd0;p=network-manager.git Refactored js services --- diff --git a/frontend/js/aliases.js b/frontend/js/aliases.js index 2488b2b..ed3754d 100644 --- a/frontend/js/aliases.js +++ b/frontend/js/aliases.js @@ -1,7 +1,7 @@ // Import common js import { loadModals, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; -import { reloadDNS, reloadDHCP } from './services.js'; -import { apiMap, fetchData } from './api.js'; +// Import services +import { serviceReloadDNS, serviceReloadDHCP, serviceGetAliases, serviceGetAlias, serviceCreateAlias, serviceUpdateAlias, serviceDeleteAlias } from './services.js'; // ----------------------------- // State variables @@ -12,7 +12,7 @@ let editingAliasId = null; const sortState = { sortDirection: {}, lastSort: null }; // ----------------------------- -// Fetch hosts from API +// Fetch aliases from API // ----------------------------- async function fetchAliases () { const loader = document.getElementById("loader"); @@ -26,7 +26,7 @@ async function fetchAliases () { loader.style.display = "block"; // Fetch aliases - allAliases = await fetchData(apiMap.aliases); + allAliases = await serviceGetAliases(); viewAliases = [...allAliases]; } catch (err) { @@ -247,50 +247,27 @@ async function editAlias(id) { // Clear form first clearAddAliasForm(); - // Fetch alias - const res = await fetch(`/api/aliases/${id}`, { - headers: { Accept: 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Fetch failed for alias ${id}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; try { - data = await res.json(); - } catch { - throw new Error(`Fetch failed for alias ${id}: Invalid JSON payload`); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Fetch failed for alias ${id}`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } - - // Store the ID of the alias being edited - editingAliasId = id; - - // Pre-fill the form fields - document.getElementById("aliasName").value = data.name ?? ""; - document.getElementById("aliasTarget").value = data.target ?? ""; - document.getElementById("aliasDescription").value = data.description ?? ""; - document.getElementById("aliasSSL").checked = !!data.ssl_enabled; - if (data.visibility == 2) { - document.getElementById("aliasVisibilityAlias").checked = true; - } else if (data.visibility == 1){ - document.getElementById("aliasVisibilityGlobal").checked = true; - } else { - document.getElementById("aliasVisibilityLocal").checked = true; + const data = await serviceGetAlias(id); + + // Store the ID of the alias being edited + editingAliasId = id; + + // Pre-fill the form fields + document.getElementById("aliasName").value = data.name ?? ""; + document.getElementById("aliasTarget").value = data.target ?? ""; + document.getElementById("aliasDescription").value = data.description ?? ""; + document.getElementById("aliasSSL").checked = !!data.ssl_enabled; + if (data.visibility == 2) { + document.getElementById("aliasVisibilityAlias").checked = true; + } else if (data.visibility == 1){ + document.getElementById("aliasVisibilityGlobal").checked = true; + } else { + document.getElementById("aliasVisibilityLocal").checked = true; + } + } catch (err) { + console.error(err?.message || "Error loading alias"); + showToast(err?.message || "Error loading alias", false); } } @@ -309,92 +286,34 @@ async function saveAlias(aliasData) { return false; } - if (editingAliasId !== null) { - // Update existing alias - const res = await fetch(`/api/aliases/${editingAliasId}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(aliasData) - }); - - // Success without JSON - if (res.status === 204) { - showToast('Alias updated successfully', true); - return true; - } + try { + let result; - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; + if (editingAliasId !== null) { + // Update + result = await serviceUpdateAlias(editingAliasId, aliasData); + } else { + // Create + result = await serviceCreateAlias(aliasData); } - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : editingAliasId !== null + ? 'Alias updated successfully' + : 'Alias created successfully'; - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error updating alias`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + showToast(msg, true); - // Success - showToast(data?.message || 'Alias updated successfully', true); return true; - } else { - // Create new alias - const res = await fetch(`/api/aliases`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(aliasData) - }); - - // Success without JSON - if (res.status === 204) { - showToast('Alias created successfully', true); - return true; - } - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + } catch (err) { + console.error(err?.message || "Error saving alias"); + showToast(err?.message || "Error saving alias", false); + } - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error adding alias`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + return false; - // Success - showToast(data?.message || 'Alias created successfully', true); - return true - } } // ----------------------------- @@ -440,7 +359,7 @@ async function handleAddAliasSubmit(e) { if (ok !== false) { // close modal and reload aliases closeAddAliasModal(); - await loadAliases(); + await fetchAliases(); updateTable(); return true } @@ -463,50 +382,23 @@ async function handleDeleteAlias(e, el) { // Get alias ID const id = Number(el.dataset.aliasId); if (!Number.isFinite(id)) { - console.warn('Delete: alias id not valid for delete:', id); showToast('Alias id not valid for delete', false); return; } - // Execute delete try { - // Fetch data - const res = await fetch(`/api/aliases/${id}`, { - method: 'DELETE', - headers: { 'Accept': 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } + const result = await serviceDeleteAlias(id); - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error deleting alias`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : 'Alias deleted successfully'; - // Success - showToast(data?.message || 'Alias deleted successfully', true); + showToast(msg, true); // Reload aliases await fetchAliases(); updateTable(); + return true; } catch (err) { @@ -532,7 +424,7 @@ const actionHandlers = { // Reload DNS reloadDns: async () => { try { - const result = await reloadDNS(); + const result = await serviceReloadDNS(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DNS reload successfully'; @@ -544,7 +436,7 @@ const actionHandlers = { // Reload DHCP reloadDhcp: async () => { try { - const result = await reloadDHCP(); + const result = await serviceReloadDHCP(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DHCP reload successfully'; @@ -631,7 +523,7 @@ function initModalLifecycle() { modalEl.addEventListener('show.bs.modal', async (ev) => { lastTriggerEl = ev.relatedTarget; // trigger (Add o Edit) - // check Add or Edit mode based on presence of data-host-id in the trigger element + // check Add or Edit mode based on presence of data-alias-id in the trigger element const id = Number(lastTriggerEl?.dataset?.aliasId); if (Number.isFinite(id)) { diff --git a/frontend/js/api.js b/frontend/js/api.js index c12be8a..34a1e75 100644 --- a/frontend/js/api.js +++ b/frontend/js/api.js @@ -1,61 +1,87 @@ -// ----------------------------- -// API Endpoints -// ----------------------------- -export const apiMap = { - hosts: { - url: "/api/hosts", - name: "Hosts" - }, - aliases: { - url: "/api/aliases", - name: "Aliases" - }, - leases: { - url: "/api/dhcp/leases", - name: "Leases" - }, - devices: { - url: "/api/devices", - name: "Devices" - } -}; - -// ----------------------------- -// Fetch Data functions -// ----------------------------- -export async function fetchData(api) { +// ------------------------------------------------------- +// API CORE - Generic request wrapper +// ------------------------------------------------------- +export async function apiRequest( + url, + { + method = 'GET', + headers = {}, + body = null, + } = {}, + errorPrefix = 'Request error' +) { + let res; - let items = []; + try { + res = await fetch(url, { + method, + headers: { + 'Accept': 'application/json', + ...headers + }, + body + }); + } catch (err) { + throw new Error( + `${errorPrefix}: network error${err?.message ? `: ${err.message}` : ''}`, + { cause: err } + ); + } - // Fetch data - const res = await fetch(api.url, { - headers: { Accept: 'application/json' }, - }); + // 204 No Content + if (res.status === 204) { + return true; + } - // Check content-type to avoid parsing errors + // Content-Type check const contentType = res.headers.get("content-type") || ""; if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); + const err = new Error( + `${errorPrefix}: ${res.status} ${res.statusText || 'Unexpected response'}` + ); err.status = res.status; throw err; } - // Check JSON + // Parse JSON let data; try { data = await res.json(); - items = Array.isArray(data) ? data : (Array.isArray(data?.data) ? data.data : []); } catch { - throw new Error('Invalid JSON payload'); + throw new Error(`${errorPrefix}: Invalid JSON payload`); } - // Check JSON errors + // Handle HTTP error if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error loading ${api.name}`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); + const serverMsg = + data?.detail?.message?.trim() + || (typeof data?.detail === 'string' ? data.detail.trim() : '') + || data?.message?.trim() + || data?.error?.message?.trim() + || (typeof data?.error === 'string' ? data.error.trim() : ''); + + const err = new Error( + `${errorPrefix}${serverMsg ? `: ${serverMsg}` : ''}` + ); err.status = res.status; throw err; } - return items; + + return data; +} + +export function apiGet(url, errorPrefix = 'Fetch error') { + return apiRequest(url, { method: 'GET' }, errorPrefix); +} + +export function apiPost(url, payload, errorPrefix = 'Request error') { + return apiRequest( + url, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }, + errorPrefix + ); } diff --git a/frontend/js/devices.js b/frontend/js/devices.js index edf65f0..377c2c3 100644 --- a/frontend/js/devices.js +++ b/frontend/js/devices.js @@ -1,7 +1,7 @@ // Import common js import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; -import { reloadDNS, reloadDHCP } from './services.js'; -import { apiMap, fetchData } from './api.js'; +// Import services +import { serviceReloadDNS, serviceReloadDHCP, serviceGetDHCPLeases, serviceGetDHCPLease, serviceDeleteDHCPLease, serviceGetDevices, serviceGetHost, serviceCreateHost, serviceUpdateHost, serviceDeleteHost } from './services.js'; // ----------------------------- // State variables @@ -26,7 +26,7 @@ async function fetchDevices () { loader.style.display = "block"; // Fetch devices - allDevices = await fetchData(apiMap.devices); + allDevices = await serviceGetDevices(); viewDevices = [...allDevices]; } catch (err) { @@ -87,8 +87,8 @@ function updateTable () { } else if (id.startsWith("d-")) { type = 2; } else { - console.error("loadDevices: unknown device type:", id); - showToast("loadDevices: unknown device type:", false); + console.error("updateTable: unknown device type:", id); + showToast("updateTable: unknown device type:", false); } const tr = document.createElement("tr"); @@ -306,21 +306,19 @@ function updateTable () { // ----------------------------- async function editHost(id) { - let fetchUrl = ""; let host = false; // Clear form first clearAddHostForm(); + // host or lease if (id !== null) { // Static or Dynamic? if (id.startsWith("s-")) { // static - fetchUrl = `/api/hosts/${id.slice(2)}`; host = true; } else if (id.startsWith("d-")) { // dynamic - fetchUrl = `/api/dhcp/leases/${id.slice(2)}`; host = false; } else { throw new Error("Invalid Device ID format for edit"); @@ -330,54 +328,36 @@ async function editHost(id) { throw new Error("Invalid Device ID for edit"); } - // Fetch host - const res = await fetch(fetchUrl, { - headers: { Accept: 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Fetch failed for host ${id}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; try { - data = await res.json(); - } catch { - throw new Error(`Fetch failed for host ${id}: Invalid JSON payload`); - } + let data; - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Fetch failed for host ${id}`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + if(host){ + data = await serviceGetHost(id); - if(host) { - // Store the ID of the host being edited - editingHostId = id; - } + // Store the ID of the host being edited + editingHostId = id; + } else { + data = await serviceGetDHCPLease(id); + } - // Pre-fill the form fields - document.getElementById("hostName").value = data.name ?? ""; - document.getElementById("hostIPv4").value = data.ipv4 ?? ""; - document.getElementById("hostIPv6").value = data.ipv6 ?? ""; - document.getElementById("hostMAC").value = data.mac ?? ""; - document.getElementById("hostDescription").value = data.description ?? ""; - document.getElementById("hostSSL").checked = !!data.ssl_enabled; - if (data.visibility == 2) { - document.getElementById("hostVisibilityAlias").checked = true; - } else if (data.visibility == 1){ - document.getElementById("hostVisibilityGlobal").checked = true; - } else { - document.getElementById("hostVisibilityLocal").checked = true; + // Pre-fill the form fields + document.getElementById("hostName").value = data.name ?? ""; + document.getElementById("hostIPv4").value = data.ipv4 ?? ""; + document.getElementById("hostIPv6").value = data.ipv6 ?? ""; + document.getElementById("hostMAC").value = data.mac ?? ""; + document.getElementById("hostDescription").value = data.description ?? ""; + document.getElementById("hostSSL").checked = !!data.ssl_enabled; + if (data.visibility == 2) { + document.getElementById("hostVisibilityAlias").checked = true; + } else if (data.visibility == 1){ + document.getElementById("hostVisibilityGlobal").checked = true; + } else { + document.getElementById("hostVisibilityLocal").checked = true; + } + + } catch (err) { + console.error(err?.message || "Error loading device"); + showToast(err?.message || "Error loading device", false); } } @@ -406,92 +386,34 @@ async function saveHost(hostData) { return false; } - if (editingHostId !== null) { - // Update existing host - const res = await fetch(`/api/hosts/${editingHostId}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(hostData) - }); - - // Success without JSON - if (res.status === 204) { - showToast('Host updated successfully', true); - return true; - } + try { + let result; - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; + if (editingHostId !== null) { + // Update + result = await serviceUpdateHost(editingHostId, hostData); + } else { + // Create + result = await serviceCreateHost(hostData); } - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : editingHostId !== null + ? 'Host updated successfully' + : 'Host created successfully'; - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error updating host`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + showToast(msg, true); - // Success - showToast(data?.message || 'Host updated successfully', true); return true; - } else { - // Create new host - const res = await fetch(`/api/hosts`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(hostData) - }); - - // Success without JSON - if (res.status === 204) { - showToast('Host created successfully', true); - return true; - } - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + } catch (err) { + console.error(err?.message || "Error saving host"); + showToast(err?.message || "Error saving host", false); + } - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error adding host`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + return false; - // Success - showToast(data?.message || 'Host created successfully', true); - return true - } } // ----------------------------- @@ -539,7 +461,7 @@ async function handleAddHostSubmit(e) { if (ok !== false) { // close modal and reload hosts closeAddHostModal(); - await loadDevices(); + await fetchDevices(); updateTable(); return true } @@ -556,11 +478,14 @@ async function handleAddHostSubmit(e) { // Handle delete device action // ----------------------------- async function handleDeleteDevice(e, el) { + + let host = false; + // Prevent default action e.preventDefault(); // Get device ID - const id = el.dataset.deviceId; + let id = el.dataset.deviceId; if (!id) { console.warn('Delete: device id not valid for delete:', id); @@ -568,60 +493,41 @@ async function handleDeleteDevice(e, el) { return; } - let deleteUrl = ""; - - // Static or Dynamic? - if (id.startsWith("s-")) { - // static → delete su DB - deleteUrl = `/api/hosts/${id.slice(2)}` - } else if (id.startsWith("d-")) { - // dynamic → delete su DHCP server - deleteUrl = `/api/dhcp/leases/${id.slice(2)}` + // host or lease + if (id !== null) { + // Static or Dynamic? + if (id.startsWith("s-")) { + // static + host = true; + } else if (id.startsWith("d-")) { + // dynamic + host = false; + } else { + throw new Error("Invalid Device ID format for edit"); + } + id = Number(id.slice(2)); } else { - console.error("Delete: unknown device type:", id); - showToast("Delete: unknown device type:", false); - return; + throw new Error("Invalid Device ID for edit"); } - // Execute delete try { - // Fetch data - const res = await fetch(deleteUrl, { - method: 'DELETE', - headers: { 'Accept': 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } + if(host){ + const result = await serviceDeleteHost(id); - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); + } else { + const result = await serviceDeleteDHCPLease(id); } - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error deleting device`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : 'Host deleted successfully'; - // Success - showToast(data?.message || 'Device deleted successfully', true); + showToast(msg, true); // Reload devices await fetchDevices(); updateTable(); + return true; } catch (err) { @@ -647,7 +553,7 @@ const actionHandlers = { // Reload DNS reloadDns: async () => { try { - const result = await reloadDNS(); + const result = await serviceReloadDNS(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DNS reload successfully'; @@ -659,7 +565,7 @@ const actionHandlers = { // Reload DHCP reloadDhcp: async () => { try { - const result = await reloadDHCP(); + const result = await serviceReloadDHCP(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DHCP reload successfully'; diff --git a/frontend/js/hosts.js b/frontend/js/hosts.js index 364590e..b35b2e5 100644 --- a/frontend/js/hosts.js +++ b/frontend/js/hosts.js @@ -1,7 +1,7 @@ // Import common js import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; -import { reloadDNS, reloadDHCP } from './services.js'; -import { apiMap, fetchData } from './api.js'; +// Import services +import { serviceReloadDNS, serviceReloadDHCP, serviceGetHosts, serviceGetHost, serviceCreateHost, serviceUpdateHost, serviceDeleteHost } from './services.js'; // ----------------------------- // State variables @@ -26,7 +26,7 @@ async function fetchHosts () { loader.style.display = "block"; // Fetch hosts - allHosts = await fetchData(apiMap.hosts); + allHosts = await serviceGetHosts(); viewHosts = [...allHosts]; } catch (err) { @@ -257,52 +257,30 @@ async function editHost(id) { // Clear form first clearAddHostForm(); - // Fetch host - const res = await fetch(`/api/hosts/${id}`, { - headers: { Accept: 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Fetch failed for host ${id}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; try { - data = await res.json(); - } catch { - throw new Error(`Fetch failed for host ${id}: Invalid JSON payload`); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Fetch failed for host ${id}`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + const data = await serviceGetHost(id); + + // Store the ID of the host being edited + editingHostId = id; + + // Pre-fill the form fields + document.getElementById("hostName").value = data.name ?? ""; + document.getElementById("hostIPv4").value = data.ipv4 ?? ""; + document.getElementById("hostIPv6").value = data.ipv6 ?? ""; + document.getElementById("hostMAC").value = data.mac ?? ""; + document.getElementById("hostDescription").value = data.description ?? ""; + document.getElementById("hostSSL").checked = !!data.ssl_enabled; + if (data.visibility == 2) { + document.getElementById("hostVisibilityAlias").checked = true; + } else if (data.visibility == 1){ + document.getElementById("hostVisibilityGlobal").checked = true; + } else { + document.getElementById("hostVisibilityLocal").checked = true; + } - // Store the ID of the host being edited - editingHostId = id; - - // Pre-fill the form fields - document.getElementById("hostName").value = data.name ?? ""; - document.getElementById("hostIPv4").value = data.ipv4 ?? ""; - document.getElementById("hostIPv6").value = data.ipv6 ?? ""; - document.getElementById("hostMAC").value = data.mac ?? ""; - document.getElementById("hostDescription").value = data.description ?? ""; - document.getElementById("hostSSL").checked = !!data.ssl_enabled; - if (data.visibility == 2) { - document.getElementById("hostVisibilityAlias").checked = true; - } else if (data.visibility == 1){ - document.getElementById("hostVisibilityGlobal").checked = true; - } else { - document.getElementById("hostVisibilityLocal").checked = true; + } catch (err) { + console.error(err?.message || "Error loading host"); + showToast(err?.message || "Error loading host", false); } } @@ -331,92 +309,34 @@ async function saveHost(hostData) { return false; } - if (editingHostId !== null) { - // Update existing host - const res = await fetch(`/api/hosts/${editingHostId}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(hostData) - }); - - // Success without JSON - if (res.status === 204) { - showToast('Host updated successfully', true); - return true; - } + try { + let result; - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; + if (editingHostId !== null) { + // Update + result = await serviceUpdateHost(editingHostId, hostData); + } else { + // Create + result = await serviceCreateHost(hostData); } - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : editingHostId !== null + ? 'Host updated successfully' + : 'Host created successfully'; - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error updating host`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + showToast(msg, true); - // Success - showToast(data?.message || 'Host updated successfully', true); return true; - } else { - // Create new host - const res = await fetch(`/api/hosts`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(hostData) - }); - - // Success without JSON - if (res.status === 204) { - showToast('Host created successfully', true); - return true; - } - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + } catch (err) { + console.error(err?.message || "Error saving host"); + showToast(err?.message || "Error saving host", false); + } - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error adding host`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + return false; - // Success - showToast(data?.message || 'Host created successfully', true); - return true - } } // ----------------------------- @@ -487,50 +407,23 @@ async function handleDeleteHost(e, el) { // Get host ID const id = Number(el.dataset.hostId); if (!Number.isFinite(id)) { - console.warn('Delete: host id not valid for delete:', id); showToast('Host id not valid for delete', false); return; } - // Execute delete try { - // Fetch data - const res = await fetch(`/api/hosts/${id}`, { - method: 'DELETE', - headers: { 'Accept': 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + const result = await serviceDeleteHost(id); - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error deleting host`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : 'Host deleted successfully'; - // Success - showToast(data?.message || 'Host deleted successfully', true); + showToast(msg, true); // Reload hosts await fetchHosts(); updateTable(); + return true; } catch (err) { @@ -556,7 +449,7 @@ const actionHandlers = { // Reload DNS reloadDns: async () => { try { - const result = await reloadDNS(); + const result = await serviceReloadDNS(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DNS reload successfully'; @@ -568,7 +461,7 @@ const actionHandlers = { // Reload DHCP reloadDhcp: async () => { try { - const result = await reloadDHCP(); + const result = await serviceReloadDHCP(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DHCP reload successfully'; diff --git a/frontend/js/index.js b/frontend/js/index.js index d4d59f4..249c2b1 100644 --- a/frontend/js/index.js +++ b/frontend/js/index.js @@ -2,7 +2,7 @@ // IMPORT // ------------------------------------------------------- import { loadModals, showToast } from './common.js'; -import { apiCheck, reloadDNS, reloadDHCP, CreateBackup, fetchBackups, RestoreBackup, deleteBackup, checkHealth } from './services.js'; +import { serviceCheckAbount, serviceCheckHealth , serviceReloadDNS, serviceReloadDHCP, serviceBackupCreate, serviceBackupList, serviceBackupRestore, deleteBackup} from './services.js'; // ------------------------------------------------------- // BACKUP MODAL OPEN/CLOSE @@ -25,7 +25,7 @@ async function openBackupModal() { } try { - const data = await fetchBackups(); + const data = await serviceBackupList(); renderBackupList(data); } catch (err) { console.error(err); @@ -133,7 +133,7 @@ function getSelectedBackup() { } // ------------------------------------------------------- -// HEALTH MODAL OPEN/CLOSE + RENDER (usa checkHealth()) +// HEALTH MODAL OPEN/CLOSE + RENDER // ------------------------------------------------------- function openHealthModal() { const modal = document.getElementById('healthModal'); @@ -160,11 +160,11 @@ function openHealthModal() { } if (updatedAtEl) updatedAtEl.textContent = ''; - // Usa checkHealth() per ottenere il payload health + // Usa serviceCheckHealth() per ottenere il payload health Promise.resolve() - .then(() => checkHealth()) + .then(() => serviceCheckHealth()) .then((data) => { - // Se checkHealth ritorna true o {message}, non abbiamo i dettagli: mostra un messaggio + // Se serviceCheckHealth ritorna true o {message}, non abbiamo i dettagli: mostra un messaggio const isDetailed = data && typeof data === 'object' && ('status' in data || 'latency_ms' in data || 'database' in data); @@ -267,7 +267,7 @@ const actionHandlers = { label.textContent = ' Exporting…'; try { - const result = await CreateBackup(); + const result = await serviceBackupCreate(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'Backup completed successfully'; @@ -299,7 +299,7 @@ const actionHandlers = { label.textContent = ' Restoring…'; try { - const result = await RestoreBackup(id); + const result = await serviceBackupRestore(id); const msg = (typeof result === 'object' && result?.message) ? result.message : 'Restore completed successfully'; @@ -334,7 +334,7 @@ const actionHandlers = { showToast(msg, true); // reload list - const data = await fetchBackups(); + const data = await serviceBackupList(); renderBackupList(data); } catch (err) { @@ -348,7 +348,7 @@ const actionHandlers = { // Reload DNS reloadDns: async () => { try { - const result = await reloadDNS(); + const result = await serviceReloadDNS(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DNS reload successfully'; @@ -360,7 +360,7 @@ const actionHandlers = { // Reload DHCP reloadDhcp: async () => { try { - const result = await reloadDHCP(); + const result = await serviceReloadDHCP(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DHCP reload successfully'; @@ -371,7 +371,7 @@ const actionHandlers = { }, // Check API status apiCheck: async () => { - const result = await apiCheck(); + const result = await serviceCheckAbount(); if(result) { showToast('API status updated succesfully', true); } else { @@ -450,7 +450,7 @@ function initBackupModal() { // Periodic API Check // ------------------------------------------------------- async function periodicTest() { - await apiCheck(); + await serviceCheckAbount(); setTimeout(periodicTest, 10000); } diff --git a/frontend/js/leases.js b/frontend/js/leases.js index 51ed645..1a0144b 100644 --- a/frontend/js/leases.js +++ b/frontend/js/leases.js @@ -1,7 +1,7 @@ // Import common js import { loadModals, isValidIPv4, isValidIPv6, isValidMAC, showToast, sortTable, initSortableTable, resetSorting, filterTable, clearSearch } from './common.js'; -import { reloadDNS, reloadDHCP } from './services.js'; -import { apiMap, fetchData } from './api.js'; +// Import services +import { serviceReloadDNS, serviceReloadDHCP, serviceGetDHCPLeases, serviceDeleteDHCPLease, serviceGetDHCPLease, serviceCreateHost} from './services.js'; // ----------------------------- // State variables @@ -25,7 +25,7 @@ async function fetchLeases () { loader.style.display = "block"; // Fetch leases - allLeases = await fetchData(apiMap.leases); + allLeases = await serviceGetDHCPLeases(); viewLeases = [...allLeases]; } catch (err) { @@ -272,49 +272,27 @@ async function addHost(id) { // Clear form first clearAddHostForm(); - // Fetch lease - const res = await fetch(`/api/dhcp/leases/${id}`, { - headers: { Accept: 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Fetch failed for lease ${id}: ${res.statusText}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data; try { - data = await res.json(); - } catch { - throw new Error(`Fetch failed for lease ${id}: Invalid JSON payload`); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Fetch failed for lease ${id}`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } + const data = await serviceGetDHCPLease(id); + + // Pre-fill the form fields + document.getElementById("hostName").value = data.name ?? ""; + document.getElementById("hostIPv4").value = data.ipv4 ?? ""; + document.getElementById("hostIPv6").value = data.ipv6 ?? ""; + document.getElementById("hostMAC").value = data.mac ?? ""; + document.getElementById("hostDescription").value = data.description ?? ""; + document.getElementById("hostSSL").checked = !!data.ssl_enabled; + if (data.visibility == 2) { + document.getElementById("hostVisibilityAlias").checked = true; + } else if (data.visibility == 1){ + document.getElementById("hostVisibilityGlobal").checked = true; + } else { + document.getElementById("hostVisibilityLocal").checked = true; + } - // Pre-fill the form fields - document.getElementById("hostName").value = data.name ?? ""; - document.getElementById("hostIPv4").value = data.ipv4 ?? ""; - document.getElementById("hostIPv6").value = data.ipv6 ?? ""; - document.getElementById("hostMAC").value = data.mac ?? ""; - document.getElementById("hostDescription").value = data.description ?? ""; - document.getElementById("hostSSL").checked = !!data.ssl_enabled; - if (data.visibility == 2) { - document.getElementById("hostVisibilityAlias").checked = true; - } else if (data.visibility == 1){ - document.getElementById("hostVisibilityGlobal").checked = true; - } else { - document.getElementById("hostVisibilityLocal").checked = true; + } catch (err) { + console.error(err?.message || "Error loading lease"); + showToast(err?.message || "Error loading lease", false); } } @@ -343,48 +321,26 @@ async function saveHost(hostData) { return false; } - // Create new host - const res = await fetch(`/api/hosts`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(hostData) - }); + try { + const result = await serviceCreateHost(hostData); - // Success without JSON - if (res.status === 204) { - showToast('Host created successfully', true); - return true; - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : 'Host created successfully'; - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } + showToast(msg, true); - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + return true; - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error adding host`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; + } catch (err) { + console.error(err?.message || "Error saving host"); + showToast(err?.message || "Error saving host", false); } - // Success - showToast(data?.message || 'Host created successfully', true); - return true + return false; + } + // ----------------------------- // Prepare add host form // ----------------------------- @@ -451,50 +407,23 @@ async function handleDeleteLease(e, el) { // Get lease ID const id = Number(el.dataset.leaseId); if (!Number.isFinite(id)) { - console.warn('Delete: lease id not valid for delete:', id); showToast('Lease id not valid for delete', false); return; } - // Execute delete try { - // Fetch data - const res = await fetch(`/api/dhcp/leases/${id}`, { - method: 'DELETE', - headers: { 'Accept': 'application/json' }, - }); - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`${res.status}: ${res.statusText}`); - err.status = res.status; - throw err; - } + const result = await serviceDeleteDHCPLease(id); - // Check JSON - let data; - try { - data = await res.json(); - } catch { - throw new Error('Invalid JSON payload'); - } + const msg = (typeof result === 'object' && result?.message) + ? result.message + : 'Lease deleted successfully'; - // Check JSON errors - if (!res.ok) { - const serverMsg = data?.detail?.message?.trim(); - const base = `Error deleting lease`; - const err = new Error(serverMsg ? `${base}: ${serverMsg}` : base); - err.status = res.status; - throw err; - } - - // Success - showToast(data?.message || 'Lease deleted successfully', true); + showToast(msg, true); // Reload leases await fetchLeases(); updateTable(); + return true; } catch (err) { @@ -520,7 +449,7 @@ const actionHandlers = { // Reload DNS reloadDns: async () => { try { - const result = await reloadDNS(); + const result = await serviceReloadDNS(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DNS reload successfully'; @@ -532,7 +461,7 @@ const actionHandlers = { // Reload DHCP reloadDhcp: async () => { try { - const result = await reloadDHCP(); + const result = await serviceReloadDHCP(); const msg = (typeof result === 'object' && result?.message) ? result.message : 'DHCP reload successfully'; @@ -626,7 +555,7 @@ function initModalLifecycle() { try { await addHost(id); } catch (err) { - showToast(err?.message || "Error loading host for edit", false); + showToast(err?.message || "Error loading host", false); // Close modal modalEl.addEventListener('shown.bs.modal', () => { closeAddHostModal(lastTriggerEl); diff --git a/frontend/js/services.js b/frontend/js/services.js index 894c5bb..e34a17b 100644 --- a/frontend/js/services.js +++ b/frontend/js/services.js @@ -1,7 +1,10 @@ +// import api +import { apiRequest, apiGet, apiPost } from './api.js'; + // ------------------------------------------------------- -// API Health Check +// Check Abount // ------------------------------------------------------- -export async function apiCheck() { +export async function serviceCheckAbount() { const pill = document.getElementById('api-pill'); if (!pill) return; @@ -22,431 +25,269 @@ export async function apiCheck() { } } +// ------------------------------------------------------- +// Check Health +// ------------------------------------------------------- +export async function serviceCheckHealth() { + return await apiGet("/api/health", "Error performing health check"); +} + // ----------------------------- // Reload DNS // ----------------------------- -export async function reloadDNS() { - let res; - try { - // Fetch data - res = await fetch('/api/dns/reload', { - method: 'POST', - headers: { 'Accept': 'application/json' }, - }); - } catch (err) { - const msg = 'Network error while reloading DNS' + (err?.message ? `: ${err.message}` : ''); - throw new Error(msg, { cause: err }); - } - - // Success without JSON - if (res.status === 204) { - return true; - } +export async function serviceReloadDNS() { + const data = await apiPost( + "/api/dns/reload", + null, + "Error reloading DNS" + ); + + return data?.message ? { message: data.message } : true; +} - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Error reloading DNS: ${res.status}: ${res.statusText || 'Unexpected response'}`); - err.status = res.status; - throw err; - } +// ----------------------------- +// Reload DHCP action +// ----------------------------- +export async function serviceReloadDHCP() { + const data = await apiPost( + "/api/dhcp/reload", + null, + "Error reloading DHCP" + ); + + return data?.message ? { message: data.message } : true; +} - // Check JSON - let data = {}; - try { - data = await res.json(); - } catch { - throw new Error('Error reloading DNS: Invalid JSON payload'); - } +// ----------------------------- +// Get DHCP Leaseses +// ----------------------------- +export async function serviceGetDHCPLeases() { + return await apiGet("/api/dhcp/leases", "Error loading DHCP leases"); +} - // Check JSON errors - if (!res.ok) { - const serverMsg = - data?.detail?.message?.trim() - || (typeof data?.detail === 'string' ? data.detail.trim() : '') - || data?.message?.trim() - || data?.error?.message?.trim() - || (typeof data?.error === 'string' ? data.error.trim() : ''); - const err = new Error('Error reloading DNS' + (serverMsg ? `: ${serverMsg}` : '')); - err.status = res.status; - throw err; - } +// ----------------------------- +// Get a single DHCP Leases +// ----------------------------- +export async function serviceGetDHCPLease(id) { + return await apiRequest( + `/api/dhcp/leases/${id}`, + { method: "GET" }, + `Error loading host ${id}` + ); +} - if (res.ok && (data.status === 'success' || data.code === 'DNS_RELOAD_OK')) { - // Success - return data?.message ? { message: data.message } : true; - } else { - // Failed with JSON error message - return data?.message ? { message: data.message } : false; - } +// ----------------------------- +// Delete Hosts +// ----------------------------- +export async function serviceDeleteDHCPLease(id) { + const data = await apiRequest( + `/api/dhcp/leases/${id}`, + { method: "DELETE" }, + "Error deleting host" + ); + + return data?.message ? { message: data.message } : true; } // ----------------------------- -// Reload DHCP action +// Get Hosts // ----------------------------- -export async function reloadDHCP() { - let res; - try { - // Fetch data - res = await fetch(`/api/dhcp/reload`, { - method: 'POST', - headers: { 'Accept': 'application/json' }, - }); - } catch (err) { - const msg = 'Network error while reloading DHCP' + (err?.message ? `: ${err.message}` : ''); - throw new Error(msg, { cause: err }); - } +export async function serviceGetHosts() { + return await apiGet("/api/hosts", "Error loading hosts"); +} - // Success without JSON - if (res.status === 204) { - return true; - } +// ----------------------------- +// Get a single host +// ----------------------------- +export async function serviceGetHost(id) { + return await apiRequest( + `/api/hosts/${id}`, + { method: "GET" }, + `Error loading host ${id}` + ); +} - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Error reloading DHCP: ${res.status}: ${res.statusText || 'Unexpected response'}`); - err.status = res.status; - throw err; - } +// ----------------------------- +// Create a new host +// ----------------------------- +export async function serviceCreateHost(hostData) { + const data = await apiRequest( + "/api/hosts", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(hostData) + }, + "Error creating host" + ); + + return data?.message ? { message: data.message } : true; +} - // Check JSON - let data = {}; - try { - data = await res.json(); - } catch { - throw new Error('Error reloading DHCP: Invalid JSON payload'); - } +// ----------------------------- +// Update an host +// ----------------------------- +export async function serviceUpdateHost(id, hostData) { + const data = await apiRequest( + `/api/hosts/${id}`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(hostData) + }, + "Error updating host" + ); + + return data?.message ? { message: data.message } : true; +} - // Check JSON errors - if (!res.ok) { - const serverMsg = - data?.detail?.message?.trim() - || (typeof data?.detail === 'string' ? data.detail.trim() : '') - || data?.message?.trim() - || data?.error?.message?.trim() - || (typeof data?.error === 'string' ? data.error.trim() : ''); - const err = new Error('Error reloading DHCP' + (serverMsg ? `: ${serverMsg}` : '')); - err.status = res.status; - throw err; - } +// ----------------------------- +// Delete Hosts +// ----------------------------- +export async function serviceDeleteHost(id) { + const data = await apiRequest( + `/api/hosts/${id}`, + { method: "DELETE" }, + "Error deleting host" + ); + + return data?.message ? { message: data.message } : true; +} - if (res.ok && (data.status === 'success' || data.code === 'DHCP_RELOAD_OK')) { - // Success - return data?.message ? { message: data.message } : true; - } else { - // Failed with JSON error message - return data?.message ? { message: data.message } : false; - } +// ----------------------------- +// Get Aliases +// ----------------------------- +export async function serviceGetAliases() { + return await apiGet("/api/aliases", "Error loading aliases"); } -// ------------------------------------------------------- -// Create a Backup -// ------------------------------------------------------- -export async function CreateBackup() { - let res; +// ----------------------------- +// Get a single alias +// ----------------------------- +export async function serviceGetAlias(id) { + return await apiRequest( + `/api/aliases/${id}`, + { method: "GET" }, + `Error loading alias ${id}` + ); +} - try { - // Fetch data - res = await fetch(`/api/backup/create`, { - method: 'POST', - headers: { 'Accept': 'application/json' }, - }); - - } catch (err) { - const msg = 'Network error while performing backup' + (err?.message ? `: ${err.message}` : ''); - throw new Error(msg, { cause: err }); - } +// ----------------------------- +// Create a new alias +// ----------------------------- +export async function serviceCreateAlias(aliasData) { + const data = await apiRequest( + "/api/aliases", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(aliasData) + }, + "Error creating alias" + ); + + return data?.message ? { message: data.message } : true; +} - // Success without JSON - if (res.status === 204) { - return true; - } +// ----------------------------- +// Update an alias +// ----------------------------- +export async function serviceUpdateAlias(id, aliasData) { + const data = await apiRequest( + `/api/aliases/${id}`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(aliasData) + }, + "Error updating alias" + ); + + return data?.message ? { message: data.message } : true; +} - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Error performing backup: ${res.status}: ${res.statusText || 'Unexpected response'}`); - err.status = res.status; - throw err; - } +// ----------------------------- +// Delete Alias +// ----------------------------- +export async function serviceDeleteAlias(id) { + const data = await apiRequest( + `/api/aliases/${id}`, + { method: "DELETE" }, + "Error deleting alias" + ); + + return data?.message ? { message: data.message } : true; +} - // Check JSON - let data = {}; - try { - data = await res.json(); - } catch { - throw new Error('Error performing backup: Invalid JSON payload'); - } +// ----------------------------- +// Get Devices +// ----------------------------- +export async function serviceGetDevices() { + return await apiGet("/api/devices", "Error loading Devices"); +} - // Check JSON errors - if (!res.ok) { - const serverMsg = - data?.detail?.message?.trim() - || (typeof data?.detail === 'string' ? data.detail.trim() : '') - || data?.message?.trim() - || data?.error?.message?.trim() - || (typeof data?.error === 'string' ? data.error.trim() : ''); - const err = new Error('Error performing backup' + (serverMsg ? `: ${serverMsg}` : '')); - err.status = res.status; - throw err; +// ------------------------------------------------------- +// Create a Backup +// ------------------------------------------------------- +export async function serviceBackupCreate() { + const data = await apiPost( + "/api/backup/create", + null, + "Error performing backup" + ); + + if (data.status === 'success') { + return data?.message ? { message: data.message } : true; } - if (res.ok && (data.status === 'success' || data.code === 'BACKUP_CREATE_OK')) { - // Success - return data?.message ? { message: data.message } : true; - } else if (res.ok && (data.status === 'partial' || data.code === 'BACKUP_PARTIAL')) { - // Partial success + if (data.status === 'partial') { return data?.message ? { message: data.message, partial: true } : { partial: true }; - } else { - // Failed with JSON error message - return data?.message ? { message: data.message } : false; } + + return false; } // ------------------------------------------------------- // Fetch Backups list // ------------------------------------------------------- -export async function fetchBackups() { - let res; - - try { - // Fetch data - res = await fetch(`/api/backup/list`, { - method: 'GET', - headers: { 'Accept': 'application/json' }, - }); - - } catch (err) { - const msg = 'Network error while fetching backups' + (err?.message ? `: ${err.message}` : ''); - throw new Error(msg, { cause: err }); - } - - // Success without JSON - if (res.status === 204) { - return []; - } - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Error fetching backups: ${res.status}: ${res.statusText || 'Unexpected response'}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data = {}; - try { - data = await res.json(); - } catch { - throw new Error('Error fetching backups: Invalid JSON payload'); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = - data?.detail?.message?.trim() - || (typeof data?.detail === 'string' ? data.detail.trim() : '') - || data?.message?.trim() - || data?.error?.message?.trim() - || (typeof data?.error === 'string' ? data.error.trim() : ''); - const err = new Error('Error fetching backups' + (serverMsg ? `: ${serverMsg}` : '')); - err.status = res.status; - throw err; - } - - return (data ?? []); +export async function serviceBackupList() { + return await apiGet("/api/backup/list", "Error fetching backups"); } // ------------------------------------------------------- // Restore a Backup // ------------------------------------------------------- -export async function RestoreBackup(id) { - let res; - - try { - // Fetch data - res = await fetch(`/api/backup/restore`, { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ backup_id: id }) - }); - - } catch (err) { - const msg = 'Network error while performing restore' + (err?.message ? `: ${err.message}` : ''); - throw new Error(msg, { cause: err }); - } - - // Success without JSON - if (res.status === 204) { - return true; - } - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Error performing restore: ${res.status}: ${res.statusText || 'Unexpected response'}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data = {}; - try { - data = await res.json(); - } catch { - throw new Error('Error performing restore: Invalid JSON payload'); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = - data?.detail?.message?.trim() - || (typeof data?.detail === 'string' ? data.detail.trim() : '') - || data?.message?.trim() - || data?.error?.message?.trim() - || (typeof data?.error === 'string' ? data.error.trim() : ''); - const err = new Error('Error performing restore' + (serverMsg ? `: ${serverMsg}` : '')); - err.status = res.status; - throw err; +export async function serviceBackupRestore(id) { + const data = await apiPost( + "/api/backup/restore", + { backup_id: id }, + "Error performing restore" + ); + + if (data.status === 'success') { + return data?.message ? { message: data.message } : true; } - if (res.ok && (data.status === 'success' || data.code === 'BACKUP_RESTORE_OK')) { - // Success - return data?.message ? { message: data.message } : true; - } else if (res.ok && (data.status === 'partial' || data.code === 'BACKUP_RESTORE_PARTIAL')) { - // Partial success + if (data.status === 'partial') { return data?.message ? { message: data.message, partial: true } : { partial: true }; - } else { - // Failed with JSON error message - return data?.message ? { message: data.message } : false; } + + return false; } // ------------------------------------------------------- // Delete a Backup // ------------------------------------------------------- export async function deleteBackup(id) { - let res; - - try { - // Fetch data - res = await fetch(`/api/backup/delete`, { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ backup_id: id }) - }); - - } catch (err) { - const msg = 'Network error while performing delete' + (err?.message ? `: ${err.message}` : ''); - throw new Error(msg, { cause: err }); - } - - // Success without JSON - if (res.status === 204) { - return true; - } - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Error performing delete: ${res.status}: ${res.statusText || 'Unexpected response'}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data = {}; - try { - data = await res.json(); - } catch { - throw new Error('Error performing delete: Invalid JSON payload'); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = - data?.detail?.message?.trim() - || (typeof data?.detail === 'string' ? data.detail.trim() : '') - || data?.message?.trim() - || data?.error?.message?.trim() - || (typeof data?.error === 'string' ? data.error.trim() : ''); - const err = new Error('Error performing delete' + (serverMsg ? `: ${serverMsg}` : '')); - err.status = res.status; - throw err; - } - - if (res.ok && (data.status === 'success' || data.code === 'BACKUP_DELETEE_OK')) { - // Success - return data?.message ? { message: data.message } : true; - } else { - // Failed with JSON error message - return data?.message ? { message: data.message } : false; - } -} - -// ------------------------------------------------------- -// Check Health -// ------------------------------------------------------- -export async function checkHealth() { - let res; - - try { - // Fetch data - res = await fetch(`/api/health`, { - method: 'GET', - headers: { 'Accept': 'application/json' } - }); - - } catch (err) { - const msg = 'Network error while performing health check' + (err?.message ? `: ${err.message}` : ''); - throw new Error(msg, { cause: err }); - } - - // Success without JSON - if (res.status === 204) { - return true; - } - - // Check content-type to avoid parsing errors - const contentType = res.headers.get("content-type") || ""; - if (!contentType.includes("application/json")) { - const err = new Error(`Error performing health check: ${res.status}: ${res.statusText || 'Unexpected response'}`); - err.status = res.status; - throw err; - } - - // Check JSON - let data = {}; - try { - data = await res.json(); - } catch { - throw new Error('Error performing health check: Invalid JSON payload'); - } - - // Check JSON errors - if (!res.ok) { - const serverMsg = - data?.detail?.message?.trim() - || (typeof data?.detail === 'string' ? data.detail.trim() : '') - || data?.message?.trim() - || data?.error?.message?.trim() - || (typeof data?.error === 'string' ? data.error.trim() : ''); - const err = new Error('Error performing health check' + (serverMsg ? `: ${serverMsg}` : '')); - err.status = res.status; - throw err; - } + const data = await apiPost( + "/api/backup/delete", + { backup_id: id }, + "Error performing delete" + ); - return (data ?? []); + return data?.message ? { message: data.message } : true; } diff --git a/frontend/modals.html b/frontend/modals.html index ccd3b16..0247a83 100644 --- a/frontend/modals.html +++ b/frontend/modals.html @@ -233,7 +233,7 @@