Cycle Star Dashboard
The Worker behind the app: it keeps the trips, owns the savings arithmetic, and serves a small dashboard.


What it does
- Overview tab: progress towards a savings goal, total saved, trip count, distance and the latest trips.
- Trips tab: every trip, with a detail page showing its stops and its route on a map. Trips can be edited, deleted with an undo, or added by hand for rides made before the app existed.
- Trends tab: average per trip, best week, litres of fuel not burnt, and weekly distance, weekly savings and cumulative-versus-goal charts.
- Looks up a posted fuel price for the app, either a station price or a median of the cheapest nearby stations, from the NSW Government Fuel API, which also covers Tasmania.
Under the hood
- A Cloudflare Worker serving a static, framework-free JavaScript dashboard and a JSON API, with D1 for trips, traces and settings. Charts are hand-built SVG, and the map uses a vendored copy of Leaflet.
- The server recalculates every saving from the inputs on each write and ignores client-sent figures. Savings are rounded to cents, and the same test vectors run in the Kotlin app and the Worker.
- Auth has two layers. The Worker verifies the Cloudflare Access token itself and refuses everything until it is configured. Then every trip belongs to a user, who must be on an allow-list. The app signs in with Google and receives an HMAC-signed 90-day token, compared in constant time. If a secret is missing, nothing is issued, and removing someone from the list locks them out at once.
- Browser writes from another origin are refused, and responses carry a strict content security policy with no inline script.
- An edit on the dashboard is not overwritten by an older copy from the app: the later update wins, a deleted trip answers 410, and a trip id owned by someone else is rejected.
- Input is validated and clamped (distance, fuel price, number of stops, trace length), and all SQL is parameterised and scoped to the user.
- Tests run with node:test and simulate D1 using SQLite and the real migration, with no network.
The live dashboard is private. The screenshots are from a local copy seeded with invented trips.