feat(ops): pipeline-health dashboard

- Add a /health ops page: snapshot freshness (last-capture-at, colour-coded), 24h vs
  total snapshots + anomalies, events tracked, sports covered, and the four worker
  on/off states. Nav entry under System; localized en+ru.
- New IPipelineHealthService + ISnapshotRepository.GetLatestCapturedAtAsync (max
  CapturedAt via indexed ORDER BY/LIMIT 1), with a real-SQLite round-trip test.
This commit is contained in:
2026-05-29 01:32:41 +03:00
parent b67030ae7f
commit 5eb3dec24b
10 changed files with 270 additions and 0 deletions
@@ -65,6 +65,7 @@
<data name="Nav.Results"><value>Results</value></data>
<data name="Nav.Settings"><value>Settings</value></data>
<data name="Nav.Export"><value>Export</value></data>
<data name="Nav.Health"><value>Health</value></data>
<data name="Home.Kicker"><value>Briefing</value></data>
<data name="Home.Title"><value>Hunting odds-flip anomalies</value></data>
@@ -272,6 +273,23 @@
<data name="Export.Hub.Lede"><value>Export captured odds snapshots to an Excel workbook for any date range — no need to open a specific event first.</value></data>
<data name="Export.Hub.Action"><value>Configure export</value></data>
<data name="Export.Hub.FilenameHint"><value>Saved as Marathon_&lt;from&gt;_to_&lt;to&gt;.xlsx in the configured export directory.</value></data>
<data name="Health.Kicker"><value>Operations</value></data>
<data name="Health.Title"><value>Pipeline health</value></data>
<data name="Health.Lede"><value>Capture freshness, recent volumes, and worker status at a glance.</value></data>
<data name="Health.LastCapture"><value>Last capture</value></data>
<data name="Health.LastCapture.Never"><value>no captures yet</value></data>
<data name="Health.MinutesAgo"><value>{0} min ago</value></data>
<data name="Health.Stat.Snapshots"><value>Snapshots (24h)</value></data>
<data name="Health.Stat.Anomalies"><value>Anomalies (24h)</value></data>
<data name="Health.Stat.Events"><value>Events tracked</value></data>
<data name="Health.Stat.Sports"><value>Sports covered</value></data>
<data name="Health.Total"><value>{0} total</value></data>
<data name="Health.Workers"><value>Workers</value></data>
<data name="Health.Worker.Schedule"><value>Schedule poller</value></data>
<data name="Health.Worker.Live"><value>Live poller</value></data>
<data name="Health.Worker.Detection"><value>Anomaly detection</value></data>
<data name="Health.Worker.Results"><value>Results poller</value></data>
<data name="Health.Empty"><value>No data captured yet — enable the pollers in Settings.</value></data>
<data name="Export.Error.MissingDates"><value>Pick a start and end date.</value></data>
<data name="Export.Error.InvalidRange"><value>End date must be on or after the start date.</value></data>
<data name="Export.Error.Failed"><value>Export failed.</value></data>