Dashboards in Grafana; clean up after the Next.js app

- k8s/grafana.yaml: Grafana 13.2.3 with a 1Gi PVC, sign-up and
  anonymous access off. The InfluxDB datasource (URL and token from the
  environment) and three dashboards — Live, Consumption, Charging — are
  provisioned from k8s/grafana/ via kustomize-generated ConfigMaps.
- server.yaml for the Go server (port 8080, UID 65532, smaller limits),
  image tag 2.0.0. The NetworkPolicy now admits Grafana to InfluxDB.
- ingress.yaml: IngressRoutes for car.stinnesbeck.com and Grafana
  (placeholder host car-grafana.stinnesbeck.com) with tls: {}, using
  Traefik's default certificate; the cert-manager Certificate is gone.
- secrets.example.yaml: drops the dashboard secrets, adds grafana-admin.
- AGENTS.md loses its Next.js block; SPECS.md describes the new setup.

The removal of web/ (the Next.js app) and of the stale .next/ cache
upstream had committed already landed in 51e6f6f, which was staged
together with the Go server by mistake.

Checked end to end in podman (Go server + InfluxDB 2.7.12 + Grafana
13.2.3): all 25 dashboard queries return the expected data, and the
dashboards were rendered with headless Chromium. Manifests validated
offline with kustomize and kubeconform. Nothing applied to a cluster.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
2026-10-17 15:18:09 +02:00
co-authored by Claude Opus 5.5
parent 51e6f6fd48
commit 5473ab1c2b
14 changed files with 1842 additions and 139 deletions
+28 -61
View File
@@ -7,76 +7,40 @@ what has been changed, and what is still to do.
- Upstream: https://github.com/fatihdonmezdev/FatihsMG4 (itself a fork of malys / EVABRPUploader
and Leon Kernan / ABRP_Uploader). Credited in the app's About dialog.
## Plan: car server at car.stinnesbeck.com
## Architecture: car server at car.stinnesbeck.com
The car sends everything to our own server. The server stores it and forwards live telemetry to
ABRP, so the ABRP token lives only on the server, never in the APK.
The car sends everything to our own server. The server stores it in InfluxDB 2 and forwards live
telemetry to ABRP, so the ABRP token lives only on the server, never in the APK. Grafana reads
InfluxDB directly for the dashboards.
```
car ──live telemetry───▶ car.stinnesbeck.com ──▶ ABRP api.iternio.com/1/tlm/send
──daily totals─────▶ │
car ──live telemetry───▶ car.stinnesbeck.com (Go) ──▶ ABRP api.iternio.com/1/tlm/send
──daily chunks─────▶ │
──charging sessions▶ ▼
InfluxDB 2
InfluxDB 2 ◀── Grafana (car-grafana.stinnesbeck.com)
```
### Server — extend `web/` (Next.js)
- **Storage: MongoDB → InfluxDB 2.** Rewrite `web/src/lib/mongo.ts` / `data.ts` against one bucket
(tag `installationId` on every point):
| Measurement | Timestamp | Tags | Fields |
|---|---|---|---|
| `telemetry` | sample time | — | soc, power, speed, lat, lon, heading, elevation, is_charging, is_dcfc, is_parked, capacity, soe, odometer, est_battery_range, batt_temp, cabin_temp, ext_temp, hvac_setpoint, voltage, current, tire_pressure_fl/fr/rl/rr |
| `consumption_chunk` | recordedAt | date | km, kwh, hours, socDrop, sohPercent (days summed on read) |
| `charging_session` | startedAt | sessionId | endedAt, durationSeconds, energyKwh, gridEnergyKwh, pricePerKwh, totalCost, start/endSocPercent |
| `charging_curve` | point time | sessionId | powerKw, socPercent |
Same measurement + tags + timestamp overwrites, so the car's retries stay idempotent
(MongoDB used unique indexes for that).
- **New endpoint `POST /v1/telemetry`:** a batch of live samples, ingest-token auth, validated
and bounded like the existing endpoints.
- **ABRP forwarder:** after storing, forward the newest sample to `tlm/send` with `ABRP_API_KEY` and
`ABRP_USER_TOKEN` from the environment. Rate-limited; a failed forward is logged but not
retried, since stale live data is useless to ABRP. Off when the token is unset.
- **Existing endpoints stay:** `PUT /v1/consumption/daily`, `PUT /v1/charging-sessions/:id`,
`GET /v1/consumption/history/:id`, the dashboard and `/v1/health`.
- **Self-hosting:** `output: "standalone"`, a `Dockerfile`, and InfluxDB's own overwrite semantics
instead of `ensureIndexes()`.
### Car app
- Restore the live telemetry payload and upload cadence from before `ea8032a` (`TelemetryPayload`,
`UploadCadence`, location access), sending to the server instead of ABRP.
- Server URL, ingest token and installation id are entered in a new **Server** tab or imported from
a config file; nothing about the server is compiled into the APK. Each installation generates
its own id.
### Image and deployment
- Image: `git.stinnesbeck.com/nils/mg4-server:<version>`, pushed to the forge's registry
(needs a registry login on the build machine).
- `k8s/` with plain manifests: Namespace, InfluxDB StatefulSet + PVC + Service (cluster-internal),
server Deployment + Service + Ingress for `car.stinnesbeck.com` (TLS), and Secret *templates*
with placeholder values only.
- **Nothing is ever applied to the cluster by us.** Nils applies the manifests.
### Decisions (2026-10-17)
- InfluxDB 2 runs in the cluster from our manifests.
- TLS: cert-manager `ClusterIssuer` `letsencrypt-prod`; Traefik `IngressRoute` on `websecure`.
- Car config is entered in the app (Server tab / config file), not baked into the APK.
- The ABRP API key and user token are set by Nils in the `mg4-server` Secret.
- **Server:** `server/`, Go 1.26, API only — see `server/README.md` for endpoints, configuration
and the storage layout. Replaced the Next.js app from upstream (`web/`, removed 2026-10-17).
- **Dashboards:** Grafana, provisioned from `k8s/grafana/`: Live (stats, SoC/range, power/speed,
route map, temperatures, tyres), Consumption (period totals, lifetime, SoH, per-day energy,
distance and efficiency) and Charging (totals, session table, curves). Prices in euro.
- **Car app:** Server tab (URL, ingest token, installation id, config import); live telemetry
every 15 s while driving or charging; consumption chunks and charging sessions as before.
- **Deployment:** `k8s/`, plain manifests. **Nothing is ever applied to the cluster by us** —
Nils applies them (`k8s/README.md`). TLS comes from Traefik's default certificate.
### Status
- [x] Server on InfluxDB 2 with `/v1/telemetry` and the ABRP forwarder (`web/`), tested locally
against InfluxDB 2.7.12. Image builds.
- [x] Manifests in `k8s/`, validated offline. **Not applied** — that is Nils' step (`k8s/README.md`).
- [x] Car app 2.3.0: Server tab with config import, live telemetry upload, cadence controls.
Unit tests and lint pass; **not yet tried on the car**.
- [ ] Push the image (`mg4-server:1.1.2`) to git.stinnesbeck.com (needs a registry login on the build machine).
- [ ] Check the Traefik ingress class in `k8s/ingress.yaml` (placeholder `traefik-external`).
- [ ] First drive with 2.3.0: telemetry arrives in InfluxDB and at ABRP.
- [x] Go server 2.0.0: tests and govulncheck clean; image builds (about 11 MB).
- [x] End-to-end in podman (server + InfluxDB 2.7.12 + Grafana 13.2.3): every endpoint, retry
idempotency, all 25 dashboard queries return data, dashboards rendered and checked.
- [x] Manifests validated offline (including Traefik CRDs). **Not applied.**
- [x] Car app 2.3.0: unit tests and lint pass; **not yet tried on the car**.
- [ ] Push the image (`mg4-server:2.0.0`) to git.stinnesbeck.com (needs a registry login here).
- [ ] Check in `k8s/ingress.yaml`: the Traefik ingress class (placeholder `traefik-external`) and
Grafana's host (placeholder `car-grafana.stinnesbeck.com`).
- [ ] First drive with 2.3.0: telemetry arrives in InfluxDB, Grafana and ABRP.
## To do
@@ -116,6 +80,9 @@ car ──live telemetry───▶ car.stinnesbeck.com ──▶ ABRP api.ite
- 2026-10-17 — Accent colour: MG red instead of muted gold. Web app renamed to Nils' MG4.
- 2026-10-17 — Dashboard translated to English (en-GB formatting); euro instead of lira in the
dashboard and the car app's charging tab.
- 2026-10-17 — Server rewritten in Go (API only); dashboards moved to Grafana with provisioned
InfluxDB datasource and three dashboards; the Next.js app (`web/`) removed. Per-host
cert-manager certificates dropped in favour of Traefik's default certificate.
- 2026-10-17 — Temporary on-screen diagnostics (b2a5b32) to debug blank values without adb;
removed again (1002962) once values showed on the car. Revert 1002962 to bring them back.