From: Giorgio Ravera Date: Tue, 6 Jan 2026 20:56:13 +0000 (+0100) Subject: separated js functions X-Git-Tag: v0.0.1~38 X-Git-Url: http://git.giorgioravera.it/?a=commitdiff_plain;h=ec7d8c2133b6fbef78c1796610c480ac5af77e47;p=network-manager.git separated js functions --- diff --git a/backend/main.py b/backend/main.py index 1f4908f..2644991 100644 --- a/backend/main.py +++ b/backend/main.py @@ -82,8 +82,3 @@ async def session_middleware(request: Request, call_next): @app.get("/") def home(request: Request): return FileResponse(os.path.join(FRONTEND_DIR, "hosts.html")) - -# Serve app.js -@app.get("/app.js") -def js(): - return FileResponse(os.path.join(FRONTEND_DIR, "app.js")) diff --git a/backend/routes/hosts.py b/backend/routes/hosts.py index ecbebf6..caccc3a 100644 --- a/backend/routes/hosts.py +++ b/backend/routes/hosts.py @@ -33,6 +33,11 @@ def hosts(request: Request): def css_hosts(): return FileResponse(os.path.join(FRONTEND_DIR, "css/hosts.css")) +# Serve hosts.js +@router.get("/js/hosts.js") +def css_hosts(): + return FileResponse(os.path.join(FRONTEND_DIR, "js/hosts.js")) + # --------------------------------------------------------- # API ENDPOINTS # --------------------------------------------------------- diff --git a/backend/routes/login.py b/backend/routes/login.py index 7b6a6be..91a43db 100644 --- a/backend/routes/login.py +++ b/backend/routes/login.py @@ -43,6 +43,16 @@ def login_page(request: Request): def css_login(): return FileResponse(os.path.join(FRONTEND_DIR, "css/login.css")) +# Serve login.js +@router.get("/js/login.js") +def css_login(): + return FileResponse(os.path.join(FRONTEND_DIR, "js/login.js")) + +# Serve session.js +@router.get("/js/session.js") +def css_login(): + return FileResponse(os.path.join(FRONTEND_DIR, "js/session.js")) + # --------------------------------------------------------- # API ENDPOINTS # --------------------------------------------------------- diff --git a/frontend/app.js b/frontend/app.js deleted file mode 100644 index f8984fa..0000000 --- a/frontend/app.js +++ /dev/null @@ -1,327 +0,0 @@ -let editingHostId = null; -let sortDirection = {}; - -// ----------------------------- -// Validate the IP address format -// ----------------------------- -function isValidIP(ip) { - if (!ip || !ip.trim()) return true; // empty is allowed - - const ipv4 = /^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/; - const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::1)$/; - - return ipv4.test(ip) || ipv6.test(ip); -} - -// ----------------------------- -// LOAD ALL HOSTS INTO THE TABLE -// ----------------------------- -async function loadHosts() { - const res = await fetch("/api/hosts"); - const hosts = await res.json(); - - const tbody = document.querySelector("#hosts-table tbody"); - tbody.innerHTML = ""; - - hosts.forEach(h => { - const tr = document.createElement("tr"); - tr.innerHTML = ` - ${h.name} - ${h.ipv4 || ""} - ${h.ipv6 || ""} - ${h.mac || ""} - ${h.note || ""} - ${h.ssl_enabled ? "✔" : ""} - - - - - - - - - - - - - - `; - tbody.appendChild(tr); - }); -} - -// ----------------------------- -// OPEN POPUP IN EDIT MODE -// ----------------------------- -async function editHost(id) { - const res = await fetch(`/api/hosts/${id}`); - const host = await res.json(); - - // Store the ID of the host being edited - editingHostId = id; - - // Pre-fill the form fields - document.getElementById("hostName").value = host.name; - document.getElementById("hostIPv4").value = host.ipv4 || ""; - document.getElementById("hostIPv6").value = host.ipv6 || ""; - document.getElementById("hostMAC").value = host.mac || ""; - document.getElementById("hostNote").value = host.note || ""; - document.getElementById("hostSSL").checked = host.ssl_enabled === 1; - - document.getElementById("addHostModal").style.display = "flex"; -} - -// ----------------------------- -// OPEN POPUP IN CREATE MODE -// ----------------------------- -function openAddHostModal() { - editingHostId = null; // Reset edit mode - - // Clear all fields - document.getElementById("hostName").value = ""; - document.getElementById("hostIPv4").value = ""; - document.getElementById("hostIPv6").value = ""; - document.getElementById("hostMAC").value = ""; - document.getElementById("hostNote").value = ""; - document.getElementById("hostSSL").checked = false; - - document.getElementById("addHostModal").style.display = "flex"; -} - -// ----------------------------- -// CLOSE POPUP -// ----------------------------- -function closeAddHostModal() { - editingHostId = null; // Always reset edit mode - document.getElementById("addHostModal").style.display = "none"; -} - -// ----------------------------- -// SAVE HOST (CREATE OR UPDATE) -// ----------------------------- -async function saveHost() { - // Validate required fields - if (!document.getElementById("hostName").value.trim()) { - showToast("Name is required", false); - return; // stop here, do NOT send the request - } - // Validate IP format - if (!isValidIP(document.getElementById("hostIPv4").value)) { - showToast("Invalid IPv4 format", false); - return; - } - if (!isValidIP(document.getElementById("hostIPv6").value)) { - showToast("Invalid IPv6 format", false); - return; - } - - const payload = { - name: document.getElementById("hostName").value, - ipv4: document.getElementById("hostIPv4").value, - ipv6: document.getElementById("hostIPv6").value, - mac: document.getElementById("hostMAC").value, - note: document.getElementById("hostNote").value, - ssl_enabled: document.getElementById("hostSSL").checked ? 1 : 0 - }; - - try { - if (editingHostId !== null) { - // UPDATE EXISTING HOST - await fetch(`/api/hosts/${editingHostId}`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - }); - - showToast("Host updated successfully"); - } else { - // CREATE NEW HOST - await fetch("/api/hosts", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload) - }); - - showToast("Host added successfully"); - } - - closeAddHostModal(); - loadHosts(); - - } catch (err) { - console.error(err); - showToast("Error while saving host", false); - } -} - -// ----------------------------- -// DELETE HOST -// ----------------------------- -async function deleteHost(id) { - try { - const res = await fetch(`/api/hosts/${id}`, { method: "DELETE" }); - - if (!res.ok) { - throw new Error("Delete failed"); - } - - showToast("Host removed successfully"); - - } catch (err) { - console.error(err); - showToast("Error while removing host", false); - } - - loadHosts(); -} - -// ----------------------------- -// Display a temporary notification message -// ----------------------------- -function showToast(message, success = true) { - const toast = document.getElementById("toast"); - toast.textContent = message; - - toast.style.background = success ? "#28a745" : "#d9534f"; // green / red - - toast.classList.add("show"); - - setTimeout(() => { - toast.classList.remove("show"); - }, 2500); -} - -// ----------------------------- -// filter hosts in the table -// ----------------------------- -function filterHosts() { - const query = document.getElementById("searchInput").value.toLowerCase(); - const rows = document.querySelectorAll("#hosts-table tbody tr"); - - rows.forEach(row => { - const text = row.textContent.toLowerCase(); - row.style.display = text.includes(query) ? "" : "none"; - }); -} - -// ----------------------------- -// Clear search on ESC key -// ----------------------------- -function clearSearch() { - const input = document.getElementById("searchInput"); - input.value = ""; - input.blur(); - loadHosts(); -} - -// ----------------------------- -// Sort the table by column -// ----------------------------- -function sortTable(colIndex) { - const table = document.getElementById("hosts-table"); - const tbody = table.querySelector("tbody"); - const rows = Array.from(tbody.querySelectorAll("tr")); - const headers = table.querySelectorAll("th .sort-arrow"); - - // Toggle direction - sortDirection[colIndex] = !sortDirection[colIndex]; - const direction = sortDirection[colIndex] ? 1 : -1; - - // Reset all arrows - headers.forEach(h => h.textContent = ""); - - // Set arrow for current column - headers[colIndex].textContent = direction === 1 ? "▲" : "▼"; - - rows.sort((a, b) => { - const A = a.children[colIndex].innerText.toLowerCase(); - const B = b.children[colIndex].innerText.toLowerCase(); - - // Numeric sort if both values are numbers - const numA = parseFloat(A); - const numB = parseFloat(B); - - if (!isNaN(numA) && !isNaN(numB)) { - return (numA - numB) * direction; - } - - return A.localeCompare(B) * direction; - }); - - rows.forEach(row => tbody.appendChild(row)); -} - -// ----------------------------- -// Reset sorting arrows and directions -// ----------------------------- -function resetSorting() { - // Svuota tutte le direzioni salvate - sortDirection = {}; - - // Rimuove tutte le frecce dalle colonne - const arrows = document.querySelectorAll("th .sort-arrow"); - arrows.forEach(a => a.textContent = ""); -} - -// ----------------------------- -// Login function -// ----------------------------- -async function handleLogin(e) { - e.preventDefault(); - - const user = document.getElementById("username").value.trim(); - const pass = document.getElementById("password").value; - - const res = await fetch("/api/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - credentials: "include", - body: JSON.stringify({ - username: user, - password: pass - }) - }); - - const data = await res.json(); - - if (data.status === "ok") { - window.location.href = "/hosts"; - } else { - document.getElementById("loginError").textContent = data.error; - } -} - -// ----------------------------- -// Logout function -// ----------------------------- -async function handleLogout() { - await fetch("/api/logout", { - method: "POST", - credentials: "include" - }); - - window.location.href = "/login"; -} - -// ----------------------------- -// INITIAL TABLE LOAD -// ----------------------------- -loadHosts(); -document.getElementById("searchInput").value = ""; - -document.addEventListener("keydown", (e) => { - if (e.key === "Escape") { - resetSorting(); - clearSearch(); - } -}); - -document.addEventListener("DOMContentLoaded", () => { - const logoutBtn = document.getElementById("logoutBtn"); - if (logoutBtn) { - logoutBtn.addEventListener("click", handleLogout); - } -}); diff --git a/frontend/hosts.html b/frontend/hosts.html index b74b830..3a19e12 100644 --- a/frontend/hosts.html +++ b/frontend/hosts.html @@ -91,7 +91,8 @@ - + + diff --git a/frontend/js/hosts.js b/frontend/js/hosts.js new file mode 100644 index 0000000..9c2e47e --- /dev/null +++ b/frontend/js/hosts.js @@ -0,0 +1,281 @@ +let editingHostId = null; +let sortDirection = {}; + +// ----------------------------- +// Validate the IP address format +// ----------------------------- +function isValidIP(ip) { + if (!ip || !ip.trim()) return true; // empty is allowed + + const ipv4 = /^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/; + const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::1)$/; + + return ipv4.test(ip) || ipv6.test(ip); +} + +// ----------------------------- +// LOAD ALL HOSTS INTO THE TABLE +// ----------------------------- +async function loadHosts() { + const res = await fetch("/api/hosts"); + const hosts = await res.json(); + + const tbody = document.querySelector("#hosts-table tbody"); + tbody.innerHTML = ""; + + hosts.forEach(h => { + const tr = document.createElement("tr"); + tr.innerHTML = ` + ${h.name} + ${h.ipv4 || ""} + ${h.ipv6 || ""} + ${h.mac || ""} + ${h.note || ""} + ${h.ssl_enabled ? "✔" : ""} + + + + + + + + + + + + + + `; + tbody.appendChild(tr); + }); +} + +// ----------------------------- +// OPEN POPUP IN EDIT MODE +// ----------------------------- +async function editHost(id) { + const res = await fetch(`/api/hosts/${id}`); + const host = await res.json(); + + // Store the ID of the host being edited + editingHostId = id; + + // Pre-fill the form fields + document.getElementById("hostName").value = host.name; + document.getElementById("hostIPv4").value = host.ipv4 || ""; + document.getElementById("hostIPv6").value = host.ipv6 || ""; + document.getElementById("hostMAC").value = host.mac || ""; + document.getElementById("hostNote").value = host.note || ""; + document.getElementById("hostSSL").checked = host.ssl_enabled === 1; + + document.getElementById("addHostModal").style.display = "flex"; +} + +// ----------------------------- +// OPEN POPUP IN CREATE MODE +// ----------------------------- +function openAddHostModal() { + editingHostId = null; // Reset edit mode + + // Clear all fields + document.getElementById("hostName").value = ""; + document.getElementById("hostIPv4").value = ""; + document.getElementById("hostIPv6").value = ""; + document.getElementById("hostMAC").value = ""; + document.getElementById("hostNote").value = ""; + document.getElementById("hostSSL").checked = false; + + document.getElementById("addHostModal").style.display = "flex"; +} + +// ----------------------------- +// CLOSE POPUP +// ----------------------------- +function closeAddHostModal() { + editingHostId = null; // Always reset edit mode + document.getElementById("addHostModal").style.display = "none"; +} + +// ----------------------------- +// SAVE HOST (CREATE OR UPDATE) +// ----------------------------- +async function saveHost() { + // Validate required fields + if (!document.getElementById("hostName").value.trim()) { + showToast("Name is required", false); + return; // stop here, do NOT send the request + } + // Validate IP format + if (!isValidIP(document.getElementById("hostIPv4").value)) { + showToast("Invalid IPv4 format", false); + return; + } + if (!isValidIP(document.getElementById("hostIPv6").value)) { + showToast("Invalid IPv6 format", false); + return; + } + + const payload = { + name: document.getElementById("hostName").value, + ipv4: document.getElementById("hostIPv4").value, + ipv6: document.getElementById("hostIPv6").value, + mac: document.getElementById("hostMAC").value, + note: document.getElementById("hostNote").value, + ssl_enabled: document.getElementById("hostSSL").checked ? 1 : 0 + }; + + try { + if (editingHostId !== null) { + // UPDATE EXISTING HOST + await fetch(`/api/hosts/${editingHostId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload) + }); + + showToast("Host updated successfully"); + } else { + // CREATE NEW HOST + await fetch("/api/hosts", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload) + }); + + showToast("Host added successfully"); + } + + closeAddHostModal(); + loadHosts(); + + } catch (err) { + console.error(err); + showToast("Error while saving host", false); + } +} + +// ----------------------------- +// DELETE HOST +// ----------------------------- +async function deleteHost(id) { + try { + const res = await fetch(`/api/hosts/${id}`, { method: "DELETE" }); + + if (!res.ok) { + throw new Error("Delete failed"); + } + + showToast("Host removed successfully"); + + } catch (err) { + console.error(err); + showToast("Error while removing host", false); + } + + loadHosts(); +} + +// ----------------------------- +// Display a temporary notification message +// ----------------------------- +function showToast(message, success = true) { + const toast = document.getElementById("toast"); + toast.textContent = message; + + toast.style.background = success ? "#28a745" : "#d9534f"; // green / red + + toast.classList.add("show"); + + setTimeout(() => { + toast.classList.remove("show"); + }, 2500); +} + +// ----------------------------- +// filter hosts in the table +// ----------------------------- +function filterHosts() { + const query = document.getElementById("searchInput").value.toLowerCase(); + const rows = document.querySelectorAll("#hosts-table tbody tr"); + + rows.forEach(row => { + const text = row.textContent.toLowerCase(); + row.style.display = text.includes(query) ? "" : "none"; + }); +} + +// ----------------------------- +// Clear search on ESC key +// ----------------------------- +function clearSearch() { + const input = document.getElementById("searchInput"); + input.value = ""; + input.blur(); + loadHosts(); +} + +// ----------------------------- +// Sort the table by column +// ----------------------------- +function sortTable(colIndex) { + const table = document.getElementById("hosts-table"); + const tbody = table.querySelector("tbody"); + const rows = Array.from(tbody.querySelectorAll("tr")); + const headers = table.querySelectorAll("th .sort-arrow"); + + // Toggle direction + sortDirection[colIndex] = !sortDirection[colIndex]; + const direction = sortDirection[colIndex] ? 1 : -1; + + // Reset all arrows + headers.forEach(h => h.textContent = ""); + + // Set arrow for current column + headers[colIndex].textContent = direction === 1 ? "▲" : "▼"; + + rows.sort((a, b) => { + const A = a.children[colIndex].innerText.toLowerCase(); + const B = b.children[colIndex].innerText.toLowerCase(); + + // Numeric sort if both values are numbers + const numA = parseFloat(A); + const numB = parseFloat(B); + + if (!isNaN(numA) && !isNaN(numB)) { + return (numA - numB) * direction; + } + + return A.localeCompare(B) * direction; + }); + + rows.forEach(row => tbody.appendChild(row)); +} + +// ----------------------------- +// Reset sorting arrows and directions +// ----------------------------- +function resetSorting() { + // Svuota tutte le direzioni salvate + sortDirection = {}; + + // Rimuove tutte le frecce dalle colonne + const arrows = document.querySelectorAll("th .sort-arrow"); + arrows.forEach(a => a.textContent = ""); +} + +// ----------------------------- +// INITIAL TABLE LOAD +// ----------------------------- +loadHosts(); +document.getElementById("searchInput").value = ""; + +document.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + resetSorting(); + clearSearch(); + } +}); + diff --git a/frontend/js/login.js b/frontend/js/login.js new file mode 100644 index 0000000..3b13053 --- /dev/null +++ b/frontend/js/login.js @@ -0,0 +1,27 @@ +// ----------------------------- +// Login function +// ----------------------------- +async function handleLogin(e) { + e.preventDefault(); + + const user = document.getElementById("username").value.trim(); + const pass = document.getElementById("password").value; + + const res = await fetch("/api/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({ + username: user, + password: pass + }) + }); + + const data = await res.json(); + + if (data.status === "ok") { + window.location.href = "/hosts"; + } else { + document.getElementById("loginError").textContent = data.error; + } +} diff --git a/frontend/js/session.js b/frontend/js/session.js new file mode 100644 index 0000000..a140a4a --- /dev/null +++ b/frontend/js/session.js @@ -0,0 +1,21 @@ +// ----------------------------- +// Logout function +// ----------------------------- +async function handleLogout() { + await fetch("/api/logout", { + method: "POST", + credentials: "include" + }); + + window.location.href = "/login"; +} + +// ----------------------------- +// DOM Ready +// ----------------------------- +document.addEventListener("DOMContentLoaded", () => { + const logoutBtn = document.getElementById("logoutBtn"); + if (logoutBtn) { + logoutBtn.addEventListener("click", handleLogout); + } +}); diff --git a/frontend/login.html b/frontend/login.html index 4f91f95..9e725e3 100644 --- a/frontend/login.html +++ b/frontend/login.html @@ -37,7 +37,7 @@ - + \ No newline at end of file