]> git.giorgioravera.it Git - network-manager.git/commitdiff
separated js functions
authorGiorgio Ravera <giorgio.ravera@gmail.com>
Tue, 6 Jan 2026 20:56:13 +0000 (21:56 +0100)
committerGiorgio Ravera <giorgio.ravera@gmail.com>
Tue, 6 Jan 2026 20:56:13 +0000 (21:56 +0100)
backend/main.py
backend/routes/hosts.py
backend/routes/login.py
frontend/app.js [deleted file]
frontend/hosts.html
frontend/js/hosts.js [new file with mode: 0644]
frontend/js/login.js [new file with mode: 0644]
frontend/js/session.js [new file with mode: 0644]
frontend/login.html

index 1f4908fcf758bceac52ad1636140823d4343ccc5..26449915f99e636a189aeb277bd331a60423ae55 100644 (file)
@@ -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"))
index ecbebf65bab998f52ad3a7ed606d17b1e61b910f..caccc3ac3acaa75a4bfe79451efc4316d403fab4 100644 (file)
@@ -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
 # ---------------------------------------------------------
index 7b6a6be7304ab478c3786ce72473b9a1778085c6..91a43dbd4dfd609da5a0a4cbc2d7ba18a0325a8b 100644 (file)
@@ -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 (file)
index f8984fa..0000000
+++ /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 = `
-            <td>${h.name}</td>
-            <td>${h.ipv4 || ""}</td>
-            <td>${h.ipv6 || ""}</td>
-            <td>${h.mac || ""}</td>
-            <td>${h.note || ""}</td>
-            <td>${h.ssl_enabled ? "&#10004;" : ""}</td>
-            <td class="actions">
-                <span class="edit-btn" onclick="editHost(${h.id})">
-                    <svg width="18" height="18" viewBox="0 0 24 24" fill="#007BFF">
-                        <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 
-                        7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 
-                        0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
-                    </svg>
-                </span>
-
-                <span class="delete-btn" onclick="deleteHost(${h.id})">
-                    <svg width="18" height="18" viewBox="0 0 24 24" fill="#0099FF">
-                        <path d="M3 6h18v2H3V6zm2 3h14l-1.5 
-                        12.5h-11L5 9zm5-6h4l1 1h5v2H4V4h5l1-1z"/>
-                    </svg>
-                </span>
-            </td>
-        `;
-        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);
-    }
-});
index b74b830dab8286a1d079c3488e12f8413fcaf3d5..3a19e129a9d588d29d77b27744a01e5643a5ab33 100644 (file)
@@ -91,7 +91,8 @@
     </div>
 </div>
 
-<script src="app.js"></script>
+<script src="js/hosts.js"></script>
+<script src="js/session.js"></script>
 
 </body>
 </html>
diff --git a/frontend/js/hosts.js b/frontend/js/hosts.js
new file mode 100644 (file)
index 0000000..9c2e47e
--- /dev/null
@@ -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 = `
+            <td>${h.name}</td>
+            <td>${h.ipv4 || ""}</td>
+            <td>${h.ipv6 || ""}</td>
+            <td>${h.mac || ""}</td>
+            <td>${h.note || ""}</td>
+            <td>${h.ssl_enabled ? "&#10004;" : ""}</td>
+            <td class="actions">
+                <span class="edit-btn" onclick="editHost(${h.id})">
+                    <svg width="18" height="18" viewBox="0 0 24 24" fill="#007BFF">
+                        <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 
+                        7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 
+                        0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
+                    </svg>
+                </span>
+
+                <span class="delete-btn" onclick="deleteHost(${h.id})">
+                    <svg width="18" height="18" viewBox="0 0 24 24" fill="#0099FF">
+                        <path d="M3 6h18v2H3V6zm2 3h14l-1.5 
+                        12.5h-11L5 9zm5-6h4l1 1h5v2H4V4h5l1-1z"/>
+                    </svg>
+                </span>
+            </td>
+        `;
+        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 (file)
index 0000000..3b13053
--- /dev/null
@@ -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 (file)
index 0000000..a140a4a
--- /dev/null
@@ -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);
+    }
+});
index 4f91f9528e16539a35c4b907a84ecfe132453104..9e725e3ca6ecedc0f7384d9664614dd8cfe511ae 100644 (file)
@@ -37,7 +37,7 @@
     </div>
 </div>
 
-<script src="app.js"></script>
+<script src="js/login.js"></script>
 
 </body>
 </html>
\ No newline at end of file