diff --git a/app/app.py b/app/app.py index 693a90b..7af1b54 100644 --- a/app/app.py +++ b/app/app.py @@ -249,7 +249,7 @@ def collect_all(): def collector_loop(): - time.sleep(10) # Let the app start up + time.sleep(2) # Brief pause to let Flask start while True: try: collect_all() diff --git a/app/gather_info.sh b/app/gather_info.sh index 20286ba..13946b2 100755 --- a/app/gather_info.sh +++ b/app/gather_info.sh @@ -186,13 +186,18 @@ gather_container_stats() { echo "mem_percent=$mem_pct" fi - # Disk (rootfs) + # Disk (rootfs) - merge wrapped lines, find the / mount local disk_info - disk_info=$($exec_cmd df -B1 / 2>/dev/null | tail -1) + disk_info=$($exec_cmd df -B1 / 2>/dev/null | awk 'NR>1{line=line $0" "} END{print line}') if [ -n "$disk_info" ]; then - echo "disk_total=$(echo "$disk_info" | awk '{print $2}')" - echo "disk_used=$(echo "$disk_info" | awk '{print $3}')" - echo "disk_percent=$(echo "$disk_info" | awk '{gsub(/%/,""); print $5}')" + # Extract numbers - find the fields: total used avail percent + local d_total d_used d_pct + d_total=$(echo "$disk_info" | awk '{for(i=1;i<=NF;i++) if($i+0>0 && $i !~ /%/){print $i; exit}}') + d_used=$(echo "$disk_info" | awk '{for(i=1;i<=NF;i++) if($i+0>0 && $i !~ /%/){n++; if(n==2){print $i; exit}}}') + d_pct=$(echo "$disk_info" | grep -oE '[0-9]+%' | head -1 | tr -d '%') + [ -n "$d_total" ] && echo "disk_total=$d_total" + [ -n "$d_used" ] && echo "disk_used=$d_used" + [ -n "$d_pct" ] && echo "disk_percent=$d_pct" fi # IP @@ -200,6 +205,11 @@ gather_container_stats() { ip=$($exec_cmd hostname -I 2>/dev/null | awk '{print $1}') [ -n "$ip" ] && echo "ip=$ip" + # OS + local os_pretty + os_pretty=$($exec_cmd cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '"') + [ -n "$os_pretty" ] && echo "os=$os_pretty" + # Uptime local uptime_s uptime_s=$($exec_cmd cut -d' ' -f1 /proc/uptime 2>/dev/null | cut -d. -f1) @@ -217,8 +227,11 @@ _sudo() { # Proxmox LXC (pct) if command -v pct &>/dev/null; then - _sudo pct list 2>/dev/null | tail -n +2 | while read -r vmid status _ name _; do - [ -z "$vmid" ] && continue + _sudo pct list 2>/dev/null | tail -n +2 | while read -r line; do + [ -z "$line" ] && continue + vmid=$(echo "$line" | awk '{print $1}') + status=$(echo "$line" | awk '{print $2}') + name=$(echo "$line" | awk '{print $NF}') echo "[container:pct-${vmid}]" echo "type=lxc" echo "platform=proxmox" diff --git a/app/static/style.css b/app/static/style.css index c81021b..a476a24 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -327,6 +327,63 @@ main { color: #475569; } +/* --- Container Summary (on card face) --- */ + +.ct-summary-list { + margin-top: 10px; + padding-top: 8px; + border-top: 1px solid #334155; + display: flex; + flex-direction: column; + gap: 3px; +} + +.ct-summary-item { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.7rem; + color: #94a3b8; + overflow: hidden; +} + +.status-dot-sm { + width: 5px; + height: 5px; + border-radius: 50%; + flex-shrink: 0; +} + +.status-dot-sm.online { + background: #22c55e; +} + +.status-dot-sm.offline { + background: #ef4444; +} + +.ct-summary-name { + font-weight: 600; + color: #cbd5e1; + white-space: nowrap; +} + +.ct-summary-os { + color: #64748b; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.ct-summary-ip { + color: #64748b; + font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; + font-size: 0.65rem; + white-space: nowrap; + margin-left: auto; + flex-shrink: 0; +} + /* --- Container / VM Sub-cards --- */ .container-grid { diff --git a/app/templates/index.html b/app/templates/index.html index d189c8c..03b8c4a 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -63,6 +63,8 @@ {% endif %} + {% set containers = d.get('container', []) if d.get('container') else [] %} + {% if server.is_online %}
@@ -96,6 +98,20 @@
{% endif %}
+ {% if containers %} +
+ {% for ct in containers %} + {% set ct_up = ct.get('status', '')|lower in ['running', 'started'] %} +
+ + {{ ct.get('name', ct.get('id', '?')) }} + {% if ct.get('os') %}{{ ct.get('os') }}{% endif %} + {% if ct.get('ip') %}{{ ct.get('ip') }}{% endif %} +
+ {% endfor %} +
+ {% endif %} + {% else %}
Unreachable
{% endif %} @@ -254,7 +270,6 @@ - {% set containers = d.get('container', []) if d.get('container') else [] %} {% if containers %}

Containers & VMs

@@ -396,16 +411,10 @@ if (card) toggleDetails(card); } - // Auto-refresh without full page reload if a card is expanded, - // otherwise do a simple reload - setInterval(function() { - if (document.querySelector('.server-card.expanded')) { - // A card is open - reload page preserving hash - location.reload(); - } else { - location.reload(); - } - }, 60000); + // If no servers have data yet, refresh quickly; otherwise every 60s + const hasData = document.querySelectorAll('.server-card').length > 0; + const refreshMs = hasData ? 60000 : 5000; + setInterval(function() { location.reload(); }, refreshMs); // Restore state on load restoreExpanded(); diff --git a/infmap/docker-compose.yml b/infmap/docker-compose.yml index 76f7fcd..a06fb2a 100644 --- a/infmap/docker-compose.yml +++ b/infmap/docker-compose.yml @@ -10,6 +10,8 @@ services: - ${SSH_KEY_PATH}:/app/ssh_key:ro - ${CONFIG_PATH}/infrastructure.conf:/app/infrastructure.conf:ro - app_data:/app/data + stop_signal: SIGINT + stop_grace_period: 5s restart: unless-stopped healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/')"]