Staging environment + FleetOps split #17
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/staging-fleetops-architecture"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Staging environment + FleetOps split.
Staging live: fleetapi/fleetops/fleetnow.fivetitude.com. Prod API cutover done (analytics + CORS).
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Adds the read-only /analytics/* surface the FleetOps SPA will consume, plus the migration that backs the fuel roll-up. All endpoints SELECT the indexed reporting.* / tracksolid.v_* views and never write, so the forthcoming staging instance can serve them against the prod DB as grafana_ro. dashboard_api_rev.py: - GET /analytics/fleet-summary per-vehicle + per-cost-centre roll-up - GET /analytics/utilisation per-vehicle utilisation + daily fleet trend - GET /analytics/driver-behaviour per-driver speeding / harsh index - GET /analytics/fuel actual vs estimated litres (data-gated flags) - GET /analytics/filters dropdown options (alias of GET /webhook/fleet-dashboard) - responses run through jsonable_encoder (Decimal->float, date->ISO) - VTRIPS_REFRESH_INTERVAL_S<=0 now DISABLES the v_trips refresher, so a read-only staging instance never attempts REFRESH (prod still owns it). migrations/17_fleetops_fuel_view.sql: - reporting.v_fuel_daily encapsulates the v_trips->devices join (so the read-only role needs SELECT only on the view) and grants it to grafana_ro. Registered 17 in run_migrations.py. Note: live migration head is 16, not 13 as CLAUDE.md implies. Endpoints are unit-compilable but untested live until the staging bridge (Phase 1) exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>deploy_dashboard_api_staging.sh — standalone bridge twin of the prod ~/deploy_dashboard_api.sh for the fivetitude.com staging umbrella: - container dashboard_api_staging on port 8891 - Traefik Host(fleetapi.fivetitude.com), router/service names suffixed -staging - CORS = fleetnow.fivetitude.com, fleetops.fivetitude.com - DATABASE_URL derived on-host as a READ-ONLY grafana_ro URL (never printed) - VTRIPS_REFRESH_INTERVAL_S=0 so the read-only instance never REFRESHes (prod owns the v_trips materialized-view refresh) Reuses the webhook_receiver image + app network + WIP bind-mount, exactly like prod. Not yet executed on the host — awaiting go-ahead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Replaces the grafana_ro reuse with a purpose-built least-privilege login role that can serve the FULL dashboard_api read surface — so it backs the staging instance now and can take over the live prod connection later (stage 2). scripts/dashboard_ro_role.sql (run as postgres, password-free in repo): - CREATE ROLE dashboard_ro LOGIN, read-only - SELECT on reporting.* + tracksolid.*; explicit SELECT on the reporting.v_trips MATERIALIZED VIEW (not covered by GRANT ON ALL TABLES) - EXECUTE on reporting.fn_* map functions - ALTER DEFAULT PRIVILEGES so future objects are auto-readable ("dynamic") scripts/bootstrap_dashboard_ro.sh: - generates the password into ~/.dashboard_ro.pw (0600), never printed - applies the DDL via docker exec psql -U postgres -v ro_pw=... deploy_dashboard_api_staging.sh: build DATABASE_URL from dashboard_ro + ~/.dashboard_ro.pw instead of grafana_ro. Migrations 17/18 (already applied) are left intact. Not yet executed on host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Thin passthrough over reporting.fn_inc_search (fleettickets migration 13): ad-hoc ticket lookup by ticket_id / owner / cluster / status / state / time for the FleetOps ticket explorer. Mirrors /webhook/inc-dashboard (read-only conn, jsonb passthrough); validates state in {open,closed,all} and ISO-8601 from/to. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Security: - .dockerignore + Dockerfile: stop baking .env / the 346MB OSM pbf into image layers; install pinned from uv.lock (reproducible builds) (SEC-04/05). - docker-compose: DB port binds ${DB_BIND_ADDR:-127.0.0.1} — loopback-only by default; remote tooling moves to an SSH tunnel (SEC-01). - webhook_receiver: CRITICAL startup warning + WEBHOOK_REQUIRE_TOKEN=1 fail-closed when JIMI_WEBHOOK_TOKEN is empty (SEC-02 / FIX-W01). Correctness: - FIX-M22/E07: capture cur.rowcount BEFORE RELEASE SAVEPOINT in poll_alarms/ poll_trips/poll_parking — the RELEASE reported -1, producing "Alarms: -4 new events inserted" logs and negative ingestion_log.rows_inserted. - FIX-W02: parse application/json push bodies (were silently dropped). - FIX-W03: move webhook DB work off the event loop via asyncio.to_thread. - FIX-M23: poll_trips phased so no txn/connection is held across Tracksolid + Nominatim (1 req/s) network calls. - FIX-M24: sync_devices disables devices absent from every target (guarded). - FIX-W04: reject device-clock-garbage alarm_time (2019 timestamps observed). - get_token(): don't relabel already-aware timestamptz expiries (BUG-P9). Observability/lifecycle: - migration 21: v_ingest_health restricted to active pipeline endpoints so one-shot tools stop wedging /health/ingest at 'stale' (dry-run verified). - FIX-M25: daily purge_audit_logs() trims ingestion_log (90d) + refresh_log (180d). - remove orphaned duplicate migrations/10_driver_clock_views.sql; ruff lint config. +5 webhook tests (82 pass). Report/plan/work-log in docs/reports/260702_*. Local only; not deployed. CLAUDE.md fix-history edits left uncommitted (that file also carries unrelated in-progress edits). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>The ingest-health handler returned reporting.v_ingest_health rows straight to JSONResponse, but last_run_at is a datetime — json.dumps raised TypeError and the endpoint fell into its except, always returning {"overall":"unknown","endpoints":[]}. Every other analytics endpoint already routes through jsonable_encoder; this one didn't. Surfaced when the prod bridge finally got the /health/ingest route. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.