feat(stats): resource metrics dashboard + sites logs/stats
Build / build (push) Successful in 10m50s
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:
@@ -21,6 +21,8 @@
|
||||
|
||||
let staleThresholdDays = $state('7');
|
||||
let imagePruneThresholdMb = $state('1024');
|
||||
let statsIntervalSeconds = $state('15');
|
||||
let statsRetentionHours = $state('2');
|
||||
|
||||
async function load() {
|
||||
loading = true;
|
||||
@@ -28,6 +30,8 @@
|
||||
const s = await getSettings();
|
||||
staleThresholdDays = String(s.stale_threshold_days ?? 7);
|
||||
imagePruneThresholdMb = String(s.image_prune_threshold_mb ?? 1024);
|
||||
statsIntervalSeconds = String(s.stats_interval_seconds ?? 15);
|
||||
statsRetentionHours = String(s.stats_retention_hours ?? 2);
|
||||
} catch (err) {
|
||||
toasts.error(err instanceof Error ? err.message : $t('settingsGeneral.loadFailed'));
|
||||
} finally {
|
||||
@@ -38,9 +42,20 @@
|
||||
async function handleSave() {
|
||||
saving = true;
|
||||
try {
|
||||
const intervalParsed = parseInt(statsIntervalSeconds, 10);
|
||||
const retentionParsed = parseInt(statsRetentionHours, 10);
|
||||
// Interval 0 disables collection; otherwise clamp to [5, 300].
|
||||
const interval = isNaN(intervalParsed)
|
||||
? 15
|
||||
: intervalParsed === 0
|
||||
? 0
|
||||
: Math.max(5, Math.min(300, intervalParsed));
|
||||
const retention = isNaN(retentionParsed) ? 2 : Math.max(0, Math.min(24, retentionParsed));
|
||||
await updateSettings({
|
||||
stale_threshold_days: Math.max(1, parseInt(staleThresholdDays, 10) || 7),
|
||||
image_prune_threshold_mb: Math.max(0, parseInt(imagePruneThresholdMb, 10) || 0)
|
||||
image_prune_threshold_mb: Math.max(0, parseInt(imagePruneThresholdMb, 10) || 0),
|
||||
stats_interval_seconds: interval,
|
||||
stats_retention_hours: retention
|
||||
} as any);
|
||||
toasts.success($t('settingsGeneral.saved'));
|
||||
} catch (err) {
|
||||
@@ -99,6 +114,22 @@
|
||||
placeholder="1024"
|
||||
helpText={$t('settings.pruneThresholdHelp')}
|
||||
/>
|
||||
<FormField
|
||||
label={$t('statsSettings.intervalLabel')}
|
||||
name="statsIntervalSeconds"
|
||||
type="number"
|
||||
bind:value={statsIntervalSeconds}
|
||||
placeholder="15"
|
||||
helpText={$t('statsSettings.intervalHelp')}
|
||||
/>
|
||||
<FormField
|
||||
label={$t('statsSettings.retentionLabel')}
|
||||
name="statsRetentionHours"
|
||||
type="number"
|
||||
bind:value={statsRetentionHours}
|
||||
placeholder="2"
|
||||
helpText={$t('statsSettings.retentionHelp')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
|
||||
Reference in New Issue
Block a user