feat(stats): resource metrics dashboard + sites logs/stats
Build / build (push) Successful in 10m50s

Background collector samples CPU/memory/network/block I/O for every
instance and site on a configurable interval (default 15s, range
5-300s), persists samples to SQLite with a configurable retention
window (default 2h, range 0-24h), and skips ticks gracefully when
the Docker daemon is unreachable. Settings are reloadable without
a restart — each tick re-reads them.

New API endpoints:
- GET /api/system/stats (host snapshot: info + df)
- GET /api/system/stats/history
- GET /api/system/stats/top?by=cpu|memory
- GET /api/projects/{id}/stages/{s}/instances/{iid}/stats/history
- GET /api/sites/{id}/stats[/history]
- GET /api/sites/{id}/logs (SSE + JSON, reuses instance log streamer)

Frontend:
- ECharts added with tree-shaken imports (~180KB gzip) for
  future-proof time-series/gantt/graph visualizations
- CollapsibleSection wraps all dashboard sections (system health,
  daemons, system resources, static sites, projects) with
  localStorage-persisted open state
- SystemResourcesCard shows capacity tiles, workload utilization
  chart with 30m/2h/6h/24h window picker, disk breakdown with
  reclaimable callouts, and top 5 consumers
- ContainerStats and ContainerLogs take a source discriminated union
  so sites reuse the same components as instances; sites detail page
  embeds both for Deno backend debugging
- Settings › Maintenance exposes collection interval + retention
- Docker-unavailable state returns 503 and renders an amber banner
  instead of a generic 500

Full i18n coverage (en + ru) for all new strings.
This commit is contained in:
2026-04-24 15:02:43 +03:00
parent 0632f512e6
commit 05440a5f92
27 changed files with 1897 additions and 112 deletions
+41 -1
View File
@@ -41,7 +41,47 @@
"failedSites": "failed",
"noSites": "No static sites yet.",
"addFirstSite": "Deploy your first site",
"viewAllSites": "View all sites"
"viewAllSites": "View all sites",
"systemHealth": "System health",
"daemons": "Daemons",
"systemResources": "System resources",
"systemResourcesSubtitle": "CPU, memory, disk, and top consumers"
},
"resources": {
"cpuCores": "CPU Cores",
"memory": "Memory",
"running": "Running",
"dockerDisk": "Docker Disk",
"workloadUtilization": "Workload utilization",
"windowMinutes": "{n} minutes",
"windowHours": "{n} hours",
"noSamples": "No samples yet — the collector samples every {interval}s.",
"diskImages": "Images",
"diskContainers": "Containers",
"diskVolumes": "Volumes",
"diskBuildCache": "Build cache",
"reclaimable": "{size} reclaimable",
"topConsumers": "Top consumers",
"byCpu": "by cpu",
"byMemory": "by memory",
"noRunning": "No running containers.",
"instance": "instance",
"site": "site",
"showHistory": "Show history",
"hideHistory": "Hide history",
"cpuSeries": "CPU %",
"memorySeries": "Memory %",
"loading": "Loading…",
"sectionTitle": "Resources",
"showLogs": "Show logs",
"hideLogs": "Hide logs",
"dockerUnavailable": "Docker is unavailable. Check that the daemon is running."
},
"statsSettings": {
"intervalLabel": "Stats collection interval (s)",
"intervalHelp": "How often resource samples are collected. 0 disables collection. Range: 5300s.",
"retentionLabel": "Stats retention (hours)",
"retentionHelp": "How long resource samples are kept. 0 disables collection. Range: 024h."
},
"projects": {
"title": "Projects",