From c98081d3607ba9981d08b6dc9e8ca180d0b7cf00 Mon Sep 17 00:00:00 2001 From: Kalzu Rekku Date: Sat, 14 Jun 2025 01:19:57 +0300 Subject: [PATCH] Types, types, types... --- Dockerfile | 1 + app/main.py | 1 + app/web/static/logs.js | 68 +++++++++++++++++++++++++++++------- app/web/static/script.js | 26 ++++++++++---- app/web/templates/index.html | 2 +- app/web/templates/logs.html | 3 +- docker-compose.yml | 13 +++---- 7 files changed, 87 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index f7e1b46..484a05d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -76,3 +76,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ # Run the application using the virtual environment's python interpreter CMD ["/opt/venv/bin/python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips", "*"] +#CMD ["/opt/venv/bin/python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/app/main.py b/app/main.py index 3ab3c3c..8848459 100644 --- a/app/main.py +++ b/app/main.py @@ -346,6 +346,7 @@ async def read_root(request: Request): "uptime_seconds": data.get("uptime_seconds"), "formatted_uptime": format_uptime(data.get("uptime_seconds")), # Pre-format uptime for HTML "load_avg": data.get("load_avg"), + "load_avg_fmt": [f"{x:.2f}" for x in data.get("load_avg", [])] if data.get("load_avg") else None, "memory_usage_percent": data.get("memory_usage_percent"), "connections": connections, } diff --git a/app/web/static/logs.js b/app/web/static/logs.js index a9e474f..3710bf9 100644 --- a/app/web/static/logs.js +++ b/app/web/static/logs.js @@ -4,12 +4,47 @@ document.addEventListener('DOMContentLoaded', () => { const levelRadios = document.querySelectorAll('input[name="log-level"]'); const sinceFilter = document.getElementById('since-filter'); const applyFiltersButton = document.getElementById('apply-filters'); + const togglePollingButton = document.getElementById('toggle-polling'); + const pollingStatusSpan = document.getElementById('polling-status'); const POLLING_INTERVAL_MS = 5000; const serviceUuid = logTableContainer.dataset.serviceUuid; // Get service UUID from data attribute let currentLevel = ''; let currentSince = ''; + let pollingIntervalId = null; + let isPollingActive = true; // Start active by default + + // Function to update the UI elements related to polling status + function updatePollingStatusUI() { + if (isPollingActive) { + togglePollingButton.textContent = 'Pause Auto-Refresh'; + pollingStatusSpan.textContent = `Auto-refresh: ON (every ${POLLING_INTERVAL_MS / 1000} seconds)`; + } else { + togglePollingButton.textContent = 'Resume Auto-Refresh'; + pollingStatusSpan.textContent = 'Auto-refresh: OFF (paused)'; + } + } + + // Function to start polling + function startPolling() { + if (pollingIntervalId) { + clearInterval(pollingIntervalId); // Clear any existing interval to prevent duplicates + } + isPollingActive = true; + fetchLogs(); // Fetch immediately when starting/resuming + pollingIntervalId = setInterval(fetchLogs, POLLING_INTERVAL_MS); + updatePollingStatusUI(); + } + + // Function to stop polling + function stopPolling() { + isPollingActive = false; + clearInterval(pollingIntervalId); + pollingIntervalId = null; + updatePollingStatusUI(); + } + async function fetchLogs() { console.log('Fetching logs with params:', { level: currentLevel, since: currentSince }); try { @@ -24,13 +59,11 @@ document.addEventListener('DOMContentLoaded', () => { console.log('Fetch URL:', url); const response = await fetch(url); console.log('Response status:', response.status); - console.log('Response Content-Type:', response.headers.get('Content-Type')); // NEW: Log Content-Type + console.log('Response Content-Type:', response.headers.get('Content-Type')); if (!response.ok) { - const errorText = await response.text(); // Try to get response body as text - console.error('Raw response text on error:', errorText.substring(0, 500) + (errorText.length > 500 ? '...' : '')); // Log first 500 chars - // If the server returns a 404, it might be due to a stale UUID. - // Log a more specific message. + const errorText = await response.text(); + console.error('Raw response text on error:', errorText.substring(0, 500) + (errorText.length > 500 ? '...' : '')); if (response.status === 404) { console.error(`404 Not Found: Service UUID mismatch. Current page UUID: ${serviceUuid}. Server UUID might have changed. Please hard refresh the page.`); logTableContainer.innerHTML = `

Error: Service UUID mismatch. Please hard refresh your browser tab (Ctrl+F5 or Cmd+Shift+R).

`; @@ -39,7 +72,8 @@ document.addEventListener('DOMContentLoaded', () => { console.error(`HTTP error! status: ${response.status} - ${errorText}`); logTableContainer.innerHTML = `

Error loading logs. HTTP Status: ${response.status}. Please check server connection or hard refresh.

`; } - return; // Stop further processing if error + stopPolling(); // Stop polling on error + return; } // Attempt to parse JSON. This is where the error would occur if the content is HTML. @@ -50,6 +84,7 @@ document.addEventListener('DOMContentLoaded', () => { } catch (error) { console.error("Error fetching logs (JSON parsing or other):", error); logTableContainer.innerHTML = '

Error loading logs. Failed to parse response or other network issue. See console for details.

'; + stopPolling(); // Stop polling on error } } @@ -151,19 +186,26 @@ document.addEventListener('DOMContentLoaded', () => { return div.innerHTML; } + // Event listener for the "Apply Filters" button applyFiltersButton.addEventListener('click', () => { const selectedRadio = document.querySelector('input[name="log-level"]:checked'); currentLevel = selectedRadio ? selectedRadio.value : ''; - // Send the datetime-local value directly. It's already in YYYY-MM-DDTHH:mm (24-hour) format. - // The backend will interpret this as UTC. currentSince = sinceFilter.value; console.log('Applying filters:', { level: currentLevel, since: currentSince }); - fetchLogs(); + // When filters are applied, always restart polling to fetch new data + startPolling(); + }); + + // Event listener for the new "Toggle Polling" button + togglePollingButton.addEventListener('click', () => { + if (isPollingActive) { + stopPolling(); + } else { + startPolling(); + } }); console.log('Initializing logs page'); - // Call fetchLogs immediately on page load to populate the table with fresh data - // and handle the initial refresh logic. - fetchLogs(); - setInterval(fetchLogs, POLLING_INTERVAL_MS); + // Initial call to start polling when the page loads + startPolling(); }); diff --git a/app/web/static/script.js b/app/web/static/script.js index 670205f..c0bed9a 100644 --- a/app/web/static/script.js +++ b/app/web/static/script.js @@ -72,16 +72,30 @@ document.addEventListener('DOMContentLoaded', () => {

UUID: ${rowNode.uuid}

IP: ${rowNode.ip}

Last Seen: ${new Date(rowNode.last_seen).toLocaleTimeString()}

-

Uptime: ${rowNode.uptime_seconds ? formatUptime(rowNode.uptime_seconds) : 'N/A'}

-

Load Avg (1m, 5m, 15m): ${rowNode.load_avg ? rowNode.load_avg.map(l => l.toFixed(2)).join(', ') : 'N/A'}

-

Memory Usage: ${rowNode.memory_usage_percent ? rowNode.memory_usage_percent.toFixed(2) + '%' : 'N/A'}

+

Uptime: ${rowNode.uptime_seconds =! null ? formatUptime(rowNode.uptime_seconds) : 'N/A'}

+

Load Avg (1m, 5m, 15m): ${ + Array.isArray(rowNode.load_avg) + ? rowNode.load_avg.map(l => l.toFixed(2)).join(', ') + : 'N/A' + }

+

Memory Usage: ${ + rowNode.memory_usage_percent != null + ? rowNode.memory_usage_percent.toFixed(2) + '%' + : 'N/A' + }

`; } else { // Off-diagonal: show ping latency - const latency = rowNode.connections && colNode.uuid in rowNode.connections && rowNode.connections[colNode.uuid] !== null ? rowNode.connections[colNode.uuid] : null; - const displayLatency = latency !== null && !isNaN(latency) ? `${latency.toFixed(1)} ms` : 'N/A'; - const latencyClass = latency !== null && !isNaN(latency) ? getLatencyClass(latency) : 'latency-unavailable'; + const latency = ( + rowNode.connections && + colNode.uuid in rowNode.connections && + rowNode.connections[colNode.uuid] !== null + ) ? rowNode.connections[colNode.uuid] : null; + + const displayLatency = (latency != null && !isNaN(latency)) ? `${latency.toFixed(1)} ms` : 'N/A'; + const latencyClass = (latency != null && !isNaN(latency)) ? getLatencyClass(latency) : 'latency-unavailable'; + cell.classList.add(latencyClass); cell.innerHTML = `
Ping: ${displayLatency}
diff --git a/app/web/templates/index.html b/app/web/templates/index.html index b131538..41b8108 100644 --- a/app/web/templates/index.html +++ b/app/web/templates/index.html @@ -58,7 +58,7 @@

IP: {{ row_node.ip }}

Last Seen: {{ row_node.formatted_last_seen }}

Uptime: {{ row_node.formatted_uptime }}

-

Load Avg (1m, 5m, 15m): {{ row_node.load_avg | map('%.2f' | format) | join(', ') if row_node.load_avg else 'N/A' }}

+

Load Avg (1m, 5m, 15m): {{ row_node.load_avg_fmt | join(', ') if row_node.load_avg_fmt else 'N/A' }}

Memory Usage: {{ '%.2f' | format(row_node.memory_usage_percent) + '%' if row_node.memory_usage_percent is not none else 'N/A' }}

{% else %} diff --git a/app/web/templates/logs.html b/app/web/templates/logs.html index 7b6296b..dd684e7 100644 --- a/app/web/templates/logs.html +++ b/app/web/templates/logs.html @@ -9,7 +9,7 @@

Node Monitor Logs

Service UUID: {{ service_uuid }}

-

Total Logs: {{ log_count }} (Auto-refreshing every 5 seconds)

+

Total Logs: {{ log_count }} ()

Log Level @@ -21,6 +21,7 @@ + {# Added toggle-polling button #}
diff --git a/docker-compose.yml b/docker-compose.yml index f929701..a248143 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,7 @@ services: environment: # Set a fixed SERVICE_UUID here. Replace this with your desired UUID. # This UUID will be used by the FastAPI app and passed to the frontend. + # !! David, change this !! SERVICE_UUID: "ab73d00a-8169-46bb-997d-f13e5f760973" DATA_DIR: "/data" # Inform the application where its data volume is mounted @@ -27,9 +28,9 @@ services: restart: unless-stopped # Healthcheck to ensure the container is running and responsive - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8000/health"] - interval: 30s - timeout: 10s - start_period: 5s - retries: 3 + #healthcheck: + # test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8000/health"] + # interval: 30s + # timeout: 10s + # start_period: 5s + # retries: 3