diff --git a/app/radio_sync.py b/app/radio_sync.py index 7fd44c5..cd57de8 100644 --- a/app/radio_sync.py +++ b/app/radio_sync.py @@ -834,7 +834,6 @@ async def stop_periodic_advert(): logger.info("Stopped periodic advertisement") - # Prevents reboot-loop: once we've rebooted to fix clock skew this session, # don't do it again (the hardware RTC case can't be fixed by reboot). _clock_reboot_attempted: bool = False diff --git a/app/repository/repeater_telemetry.py b/app/repository/repeater_telemetry.py index 405b3c5..a3dba86 100644 --- a/app/repository/repeater_telemetry.py +++ b/app/repository/repeater_telemetry.py @@ -1,10 +1,17 @@ import json import logging +import time from app.database import db logger = logging.getLogger(__name__) +# Maximum age for telemetry history entries (30 days) +_MAX_AGE_SECONDS = 30 * 86400 + +# Maximum entries to keep per repeater (sanity cap) +_MAX_ENTRIES_PER_REPEATER = 1000 + class RepeaterTelemetryRepository: @staticmethod @@ -13,7 +20,7 @@ class RepeaterTelemetryRepository: timestamp: int, data: dict, ) -> None: - """Insert a telemetry history row with the full status snapshot as JSON.""" + """Insert a telemetry history row and prune stale entries.""" await db.conn.execute( """ INSERT INTO repeater_telemetry_history @@ -22,6 +29,28 @@ class RepeaterTelemetryRepository: """, (public_key, timestamp, json.dumps(data)), ) + + # Prune entries older than 30 days + cutoff = int(time.time()) - _MAX_AGE_SECONDS + await db.conn.execute( + "DELETE FROM repeater_telemetry_history WHERE public_key = ? AND timestamp < ?", + (public_key, cutoff), + ) + + # Cap at _MAX_ENTRIES_PER_REPEATER (keep newest) + await db.conn.execute( + """ + DELETE FROM repeater_telemetry_history + WHERE public_key = ? AND id NOT IN ( + SELECT id FROM repeater_telemetry_history + WHERE public_key = ? + ORDER BY timestamp DESC + LIMIT ? + ) + """, + (public_key, public_key, _MAX_ENTRIES_PER_REPEATER), + ) + await db.conn.commit() @staticmethod diff --git a/app/routers/repeaters.py b/app/routers/repeaters.py index 29328a2..e9ee9c9 100644 --- a/app/routers/repeaters.py +++ b/app/routers/repeaters.py @@ -1,9 +1,7 @@ import logging import time -from typing import TYPE_CHECKING from fastapi import APIRouter, HTTPException -from meshcore import EventType from app.dependencies import require_connected from app.models import ( diff --git a/frontend/src/components/repeater/RepeaterTelemetryHistoryPane.tsx b/frontend/src/components/repeater/RepeaterTelemetryHistoryPane.tsx index ff39f4a..caf7952 100644 --- a/frontend/src/components/repeater/RepeaterTelemetryHistoryPane.tsx +++ b/frontend/src/components/repeater/RepeaterTelemetryHistoryPane.tsx @@ -123,9 +123,7 @@ export function TelemetryHistoryPane({ tick={{ fontSize: 10, fill: 'hsl(var(--muted-foreground))' }} tickLine={false} axisLine={false} - tickFormatter={(v) => - metric === 'uptime_seconds' ? formatUptime(v) : `${v}` - } + tickFormatter={(v) => (metric === 'uptime_seconds' ? formatUptime(v) : `${v}`)} /> { const numVal = typeof value === 'number' ? value : Number(value); const display = metric === 'uptime_seconds' ? formatUptime(numVal) : `${value}`; - const suffix = metric === 'uptime_seconds' ? '' : config.unit ? ` ${config.unit}` : ''; + const suffix = + metric === 'uptime_seconds' ? '' : config.unit ? ` ${config.unit}` : ''; const label = metric === 'packets' ? name === 'packets_received' diff --git a/frontend/src/test/setup.ts b/frontend/src/test/setup.ts index 05656d6..339b76d 100644 --- a/frontend/src/test/setup.ts +++ b/frontend/src/test/setup.ts @@ -8,7 +8,7 @@ class ResizeObserver { globalThis.ResizeObserver = ResizeObserver; -// uPlot calls matchMedia at import time for DPI detection +// Several components call matchMedia at import time for responsive detection if (typeof globalThis.matchMedia === 'undefined') { Object.defineProperty(globalThis, 'matchMedia', { value: (query: string) => ({ diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 523c156..26d77c6 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -473,7 +473,7 @@ export interface PaneState { export interface TelemetryHistoryEntry { timestamp: number; - data: RepeaterStatusResponse; + data: Record; } export interface TraceResponse { diff --git a/tests/test_repeater_telemetry.py b/tests/test_repeater_telemetry.py index 760566e..46c3ecc 100644 --- a/tests/test_repeater_telemetry.py +++ b/tests/test_repeater_telemetry.py @@ -1,6 +1,5 @@ """Tests for repeater telemetry history: repository CRUD and embedded status response.""" -import json import time import pytest