Files
mg4/web
TCFDONMEZandClaude Opus 5 b45874c405 Stop the sign-out link from signing you out on render
Sign-out answered GET behind a <Link>, and Next prefetches links that are in the
viewport. The button sits in every page header, so the browser called it as soon
as a page rendered and cleared the session cookie before the reader had clicked
anything. The first page loaded; the next navigation landed back on the password
screen, which read as the session not persisting.

It is a POST form now, and the route answers POST only — a prefetch of it gets
405 and changes nothing. The rule this broke is the general one: an endpoint
with a side effect must be unreachable by a prefetch, a crawler, or a browser
preloading what it thinks you will click.

Both session cookies are now set on the returned response rather than through
the cookies() store, which a freshly constructed redirect does not reliably
carry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-08 14:35:58 +03:00
..

FatihsMG4 web

Next.js app on Vercel: the ingest API the car writes to, and a phone-shaped dashboard for reading the history back. Replaces the standalone Express service that used to live in server/.

The car never connects to MongoDB and never holds a MongoDB URI. The URI is a Vercel environment variable and is only ever read by a server-side route.

Two credentials, on purpose

Who holds it What it opens
INGEST_TOKEN the car, in the APK the PUT endpoints — write only
DASHBOARD_PASSWORD you, in a browser the dashboard and the GET history

The APK is published, so its token must be assumed extractable: anyone can unzip an APK and read its strings. That is survivable here because the write token cannot read anything back. The password is never compiled into anything.

Environment variables

Set all five in Vercel → Settings → Environment Variables, for Production and Preview.

Name Notes
MONGODB_URI Atlas connection string
MONGODB_DATABASE defaults to fatihsmg4
INGEST_TOKEN openssl rand -hex 32
DASHBOARD_PASSWORD at least 8 characters, or login is refused outright
SESSION_SECRET openssl rand -hex 32; rotating it signs every browser out

Atlas network access must allow Vercel's egress, which is not a fixed address on the Hobby plan — either allow 0.0.0.0/0 and rely on the database user's credentials, or use Atlas private networking on a paid plan.

Endpoints

Write, Authorization: Bearer <INGEST_TOKEN>:

  • PUT /v1/consumption/daily
  • PUT /v1/charging-sessions/:sessionId

Read, session cookie or Authorization: Bearer <DASHBOARD_PASSWORD>:

  • GET /v1/history/:installationId/consumption
  • GET /v1/history/:installationId/charging

GET /v1/health is unauthenticated and returns {"status":"ok"}.

The car builds its URLs as CONSUMPTION_API_URL + /v1/..., so the Android build variable is the bare origin — https://your-app.vercel.app, no trailing path.

Indexes

There is no boot step on Vercel, so ensureIndexes() runs on the first write of each warm lambda and is skipped afterwards. The unique indexes on (installationId, date) and (installationId, sessionId) are what make the upserts idempotent when the car retries a record it could not confirm.

Local development

cp .env.example .env.local   # Next loads .env.local automatically
npm install
npm run dev
npm test                     # pure validation and session-signing tests, no database