- Rust 92.7%
- Nix 5.4%
- Shell 1.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
require_athlete_access rejected any /sync/{athlete_id} request where
athlete_id didn't match the session's own Intervals.icu athlete id.
That's a local, unconditional block that has nothing to do with
whether the session's access token actually has access to that
athlete's data on Intervals.icu -- for example a coach account whose
API key can see multiple athletes' activities.
require_athlete_access is replaced by require_authenticated_session,
which only checks that the request carries a valid session; the
session's own access token is then used to call Intervals.icu for
whichever athlete_id was requested, and Intervals.icu's own API is
what actually authorizes (or 401s/403s) that per-athlete access.
Import status and the 'last synced' default were previously looked
up using the *session's* local athlete id even when browsing a
different athlete_id -- i.e. a coach browsing an athlete's
activities would see their own import history instead of that
athlete's. Both /sync/{athlete_id} and its import-visible handler
now resolve the local athlete row for the athlete_id being browsed
(local_athlete_id) and use that instead. If that athlete has never
logged in locally, everything is treated as not-yet-imported; actually
importing then fails with the existing 'no local athlete for
Intervals.icu athlete' error rather than silently doing the wrong
thing.
process_activity_with_client (session-derived client) is now used
instead of process_activity (which looks up the target athlete's own
stored token) for both single-activity and bulk import, since the
token doing the request is the session's, not necessarily the target
athlete's own.
|
||
| migrations | ||
| scripts | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| Cargo.toml | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
| rust-toolchain.toml | ||
County Sprints frontend/leaderboard update
Replace these files in the project:
src/main.rssrc/model.rssrc/db.rssrc/leaderboard.rssrc/web.rsmigrations/0001_initial.sql
No change is required to the working src/intervals.rs.
The existing src/webhook.rs can keep calling:
crate::process_activity(state_clone, athlete_id, activity_id.clone()).await
process_activity() now loads the athlete's OAuth access token itself. The development sync uses the explicit API-key client, so the personal development key is not written to the database.
Database
The migration is intentionally a complete replacement because the database can be reset during development.
The new schema adds:
leaderboard_groupsleaderboard_group_members- indexes for activity/crossing queries
Leaderboard location matching
Leaderboard rows are grouped by:
- 10-minute time bucket
- source county
- destination county
- spatial cluster with a 10 metre DBSCAN radius
The crossing geometry is transformed to EPSG:3857 before the 10 metre clustering calculation so the distance is measured in metres rather than degrees.
Frontend
The frontend now provides:
- Leaflet map for each leaderboard group
- small map for every individual crossing
- activity detail page at
/activity/{activity_id} - activity map containing all crossings
- browser-local timestamp formatting using
Intl.DateTimeFormat - leaderboard group management at
/groups - group selection on the main leaderboard
The map uses Leaflet and OpenStreetMap tiles. Leaflet's current stable 1.x documentation describes the same map/tile-layer APIs used here. citeturn0search2turn0search9