Commit Graph

387 Commits

Author SHA1 Message Date
Davide Scaini 683b7d9c1b limit max number of users 2026-04-10 12:38:17 +02:00
Davide Scaini b4acf8c1a6 populate pages when a new user joins 2026-04-10 12:35:34 +02:00
Davide Scaini cbac82a2ba Fix 1 — new user pages 404 (server.py:228):
After registration creates the user's directories, it now calls _write_root_manifest(dd). This rewrites index.json to include the new handle's shard immediately. Since Astro dev re-evaluates getStaticPaths() on every request (reading that file), /u/pres/, /u/pres/stats/, and /u/pres/athlete/ will resolve correctly as soon as the new user navigates there.

  Fix 2 — invites link (athlete/index.astro:33):
  Added an Invites button (top-right, same style as "Edit profile") that starts hidden. When bincio:me fires and me === handle (you're on your own page), the subnav tabs are removed as before AND the invites button is revealed. Other visitors see neither.
2026-04-09 21:44:38 +02:00
Davide Scaini 084c652fdd fixing stuff after splitting jsons 2026-04-09 15:27:00 +02:00
Davide Scaini 8118f6f316 1 — Timeseries split
- writer.py: timeseries is now written to {id}.timeseries.json as a separate file. The detail JSON gets a timeseries_url field instead. finalize_pending and cleanup_pending handle the extra file.
  - merge.py (merge_one): symlinks the .timeseries.json file alongside the detail JSON. merge_all already handles it transparently (the .timeseries.json stem doesn't match any activity
  ID in to_merge, so it falls through to the symlink branch).
  - types.ts: timeseries is now timeseries?: Timeseries | null, and timeseries_url?: string | null added.
  - dataloader.ts: new loadTimeseries(url, detailUrl, base) function that resolves paths correctly in both single- and multi-user modes (uses the fetched detail URL's directory as the base).
  - ActivityDetail.svelte: loads timeseries separately after detail loads; uses detail.timeseries for IDB activities (embedded) or fetches via detail.timeseries_url for server activities. Charts show a pulse placeholder while loading.

 2 — GZip

  - GZipMiddleware (min 1 KB) added to both bincio/serve/server.py and bincio/edit/server.py — all API JSON responses are now gzip-compressed.
  - For static files (the big timeseries JSONs), nginx should be configured with gzip on; gzip_types application/json application/geo+json; — no code change needed on the server side.

  Net effect: opening an activity page now fetches ~1.4 KB (detail) instead of ~586 KB. The timeseries fetches ~60–150 KB gzip-compressed shortly after (it loads concurrently with the map rendering).
2026-04-09 14:01:02 +02:00
Davide Scaini 76b7b9ac7e swapping activities for a more diverse set 2026-04-09 13:44:59 +02:00
Davide Scaini bf95097d75 fix image 2026-04-09 13:39:44 +02:00
Davide Scaini 5a29259259 fix edit profile 2026-04-09 13:21:47 +02:00
Davide Scaini fb202b4edf Record / Convert tabs — now gated behind PUBLIC_MOBILE_APP=true. Hidden by default in VPS/dev mode; only show when explicitly opted into the mobile app build.
Edit/Upload UI — split into two concepts:
  - PUBLIC_EDIT_URL — the server base URL (empty = use proxy at /api/)
  - PUBLIC_EDIT_ENABLED=true — whether to show the edit/upload buttons at all

  bincio dev now sets PUBLIC_EDIT_ENABLED=true when instance.db exists (multi-user mode), so the upload button, edit button, and edit drawer all appear. The fetch calls already produce  correct relative URLs (${''}/api/upload = /api/upload) which the Vite proxy forwards to bincio serve.
2026-04-09 13:16:00 +02:00
Davide Scaini 2501c9e0f6 added some data to facilitate debugging... to be decided wether to publish this data or not in github 2026-04-09 13:03:19 +02:00
Davide Scaini 50cdeb3b6e ActivityFeed — replaced the <button> inside <a> (invalid HTML, unreliable) with the stretched-link pattern: the card is now a <div>, the title <a> carries a ::before pseudo-element
that covers the whole card making it clickable, and @handle is a proper <a> with z-index: 10 sitting above the stretched link. Clicking the handle navigates to /u/{handle}/; clicking
  anywhere else navigates to the activity.

  ActivityDetail — @handle link added in the date/time row of the header, linking to /u/{handle}/. Only shown when activity.handle is set (i.e. multi-user mode).
2026-04-09 13:02:07 +02:00
Davide Scaini 7dcb1e6dd0 refactor: extract/ingest facade, merge_one, deduplicate ops constants
- Add bincio/extract/ingest.py as a facade over the extract internals (ingest_parsed, strava_sync), reducing coupling from 6+ imports to one
  - Add merge_one() to merge.py — fast single-activity path for interactive edits (rewrites one file + index, skips full directory rebuild)
  - Rewrite edit/ops.py to delegate to the new facade; fix broken run_strava_sync return (was referencing undefined locals)
  - Remove duplicated SPORTS, STAT_PANELS, VALID_ACTIVITY_ID from edit/server.py — now imported from ops.py
2026-04-09 12:05:01 +02:00
Davide Scaini 0223d468c9 add tests: test_metrics.py (31 tests) — _haversine_m correctness and symmetry; compute() end-to-end for GPS distance, device distance preference, moving-time stop exclusion, elevation gain/loss,
HR, power, bbox, endpoints; MMP sliding-window constant and peak cases; _fastest_time_for_distance and compute_best_efforts for running targets; _best_climb including gap-reset
  behaviour.

  test_dedup.py (20 tests) — Exact hash lookup; near-duplicate thresholds at the ±5 min / ±5% edges; skipping already-marked duplicates; zero/null distance guard; pick_canonical source
  quality ranking; full save/reload round-trip including duplicate_of persistence.

  test_simplify.py (19 tests) — RDP mask collinear removal, corner retention, epsilon=0 keeps all; simplify_track with GPS and no-GPS input; preview_coords max-points cap and [lat, lon]
  format; build_geojson structure, coordinate order ([lon, lat, ele]), speeds parallel array, point counts.

  test_db.py (35 tests) — WAL mode, idempotent schema; user CRUD and bcrypt authenticate; session creation, lookup, expiry and auto-delete, purge; invite create/use/limit (admin
  unlimited, regular capped at 3); cascade delete of sessions when user is deleted.
2026-04-09 10:36:52 +02:00
Davide Scaini e662bb6426 missing file 2026-04-09 10:36:28 +02:00
Davide Scaini 509557ed6f fix: the user feed shows a page that nests again "feed stats athlete" that should be shown only if you are browsing a user that is not you 2026-04-09 10:35:58 +02:00
Davide Scaini cf7c71b8a3 (opus assessment) Fix auth wall flash, broken multi-user write API, and single-user redirect loop
Auth wall (Base.astro): set data-auth-pending on <body> at SSG time and hide
  it with inline CSS before any JS runs; remove the attribute after /api/me
  resolves. Eliminates the flash of protected content on private instances.

  Multi-user write API (serve/server.py): the previous _apply_sidecar_edit and
  strava_sync imports from bincio.edit.server were broken (those names don't
  exist as module-level exports) and the Strava sync mutated a global data_dir,
  making concurrent requests from different users racy. Fix: extract both
  operations into bincio/edit/ops.py as pure functions that take data_dir
  explicitly. Both edit/server.py and serve/server.py now import from there.

  Security: add rate limiting to POST /api/register (5 attempts / 15 min / IP,
  separate bucket from login). Add _check_id() activity ID validation to both
  GET and POST /api/activity/{id} in serve/server.py.

  Single-user mode: _write_root_manifest now forces instance.private=false when
  no instance.db exists, even if a previous run wrote true. Prevents the auth
  wall from firing and redirecting to /login/ when bincio serve isn't running.

  ActivityFeed: skip filterHandle when profileIndexUrl is set (per-user profile
  pages load the right shard directly; activities have no handle tag at that
  point, so the filter was producing an empty feed). Fix handle links to point
  to /u/{handle}/ instead of /{handle}/. Fix <a>-inside-<a> Svelte warning by
  converting the inner handle link to a <button>.
2026-04-09 09:19:48 +02:00
Davide Scaini 98c42dc443 unify single user and multi user behaviour 2026-04-09 08:59:40 +02:00
Davide Scaini 2007f53580 reorg documentation 2026-04-08 19:37:33 +02:00
Davide Scaini f76cc0ce7e towards multi-user 2026-04-08 19:37:10 +02:00
Davide Scaini 36a91362d9 Strava sync improvements
- Fix first sync finding 0 activities: remove last_sync_at stamp at
    connect time so the first sync checks all Strava history (existence
    check skips already-extracted files without fetching streams)
  - Add POST /api/strava/reset with soft/hard modes: soft sets last_sync_at
    to the most recent activity already on disk; hard clears it entirely
  - Surface error_count in sync response and status message
  - Add Reset / Hard reset buttons below Sync now in the upload modal
  - Reload on bfcache restore so client:only components re-mount after
    back navigation
2026-04-08 14:23:52 +02:00
Davide Scaini 083c67d018 local activity storage and convert page fixes
- Replace rdp dependency with inline pure-Python RDP implementation
    so the bincio wheel runs in Pyodide (no pure-Python wheel existed for rdp)
  - Fix convert page script: remove define:vars so Vite bundles it and
    TypeScript imports (localstore, format) work correctly
  - Rename wheel to proper PEP 427 filename (bincio-0.1.0-py3-none-any.whl)
  - Use en-GB date format on convert result, consistent with the feed
  - Add /activity/local/ page + LocalActivityDetail for IDB-only activities;
    feed links local activities there instead of the SSG route
  - Fix getStaticPaths: try public/data symlink as fallback, never crash on
    missing index.json
  - Fix ActivityDetail.onMount: load detail even when detail_url is absent
    so locally converted activities show map and charts
  - Derive track_url and detail_url from id in toSummary() since they are
    not present in the detail JSON
  - Reload on bfcache restore (pageshow) so client:only components re-mount
    after back navigation
2026-04-08 14:14:42 +02:00
Davide Scaini 5bf0f3636c local conversion 2026-04-06 22:25:57 +02:00
Davide Scaini b633d72258 iterating on the plan 2026-04-06 20:52:31 +02:00
Davide Scaini e940338816 planning 2026-04-06 19:31:52 +02:00
Davide Scaini 17f36889f3 sync strava data from web ui 2026-04-06 12:38:41 +02:00
Davide Scaini ad2710e759 fix ci 2026-04-06 12:14:39 +02:00
Davide Scaini b18024f7d3 adding ci 2026-04-01 22:51:46 +02:00
Davide Scaini ef3bf12eb8 update changelog 2026-04-01 21:55:42 +02:00
Davide Scaini bd5831c2fd second pass. low 2026-04-01 19:00:28 +02:00
Davide Scaini 3d364c3992 second pass. medium 2026-04-01 11:05:00 +02:00
Davide Scaini 94369606a4 second pass at issues. critical ones. 2026-04-01 10:58:45 +02:00
Davide Scaini 81438231b4 fix low level issues 2026-03-31 23:22:12 +02:00
Davide Scaini 8f91503cf7 fix mid level issues. updated changelog 2026-03-31 23:00:39 +02:00
Davide Scaini f8abab2c23 fix high priority issues 2026-03-31 22:53:50 +02:00
Davide Scaini e2870c3344 fixing issues 2026-03-31 22:40:35 +02:00
Davide Scaini 77c30150b0 fix ride types subclasses (?) to be tested 2026-03-30 22:55:53 +02:00
Davide Scaini 47568e9dda fix back button behaviour 2026-03-30 22:45:13 +02:00
Davide Scaini 9bc9a1d10f paginate stats page 2026-03-30 21:51:01 +02:00
Davide Scaini 5bbc3d07d2 fix: remove sensitive files, sync manifest 2026-03-30 20:38:00 +02:00
Davide Scaini 2ad18883f8 fix: update manifest with new files, remove sensitive files from remote 2026-03-30 20:37:47 +02:00
Davide Scaini 5b07c7670c feat: URL state persistence for all filters and tabs 2026-03-30 20:28:54 +02:00
Davide Scaini cb345c02a1 preserving navigation 2026-03-30 20:27:34 +02:00
Davide Scaini 0b9027b4a0 update changelog and cheatsheet for sub-sport and dev mode 2026-03-30 20:12:17 +02:00
Davide Scaini 877472e620 trying to get sub label showed properly 2026-03-30 20:09:01 +02:00
Davide Scaini c58bc8f7d5 fix default config loading 2026-03-30 15:39:58 +02:00
Davide Scaini 8fcd9f642b now strava sync works 2026-03-30 14:39:56 +02:00
Davide Scaini d806072546 improve configs, update docs 2026-03-30 13:30:43 +02:00
Davide Scaini a65923c3e0 fix unclosed tags 2026-03-30 13:06:31 +02:00
Davide Scaini 0865159cca upload external files 2026-03-30 13:06:20 +02:00
Davide Scaini fa08988471 updated changelog 2026-03-30 11:43:13 +02:00