Harden auth/upload, fix PR-delete cascade and sync backfill
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 4s
Build and push images / build-frontend (push) Successful in 8s

- OIDC: require signed short-lived state on login callback; reject
  missing userinfo sub (account-takeover guard); validate token
  exchange + userinfo responses
- Upload: safe zip extraction (path-traversal + zip-bomb cap),
  streamed size-capped writes, sanitised filenames
- Garmin: increasing lookback resets last_sync_at for one-time backfill
- Activities: delete/reprocess remove PersonalRecord rows (no FK cascade)
- Profile: validate /weight limit; sync lookback UI copy
- Dashboard: sleep shading uses same day as charted body battery

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 20:24:24 +01:00
parent 04689a29bd
commit bdd5f80c7e
8 changed files with 158 additions and 46 deletions
+21
View File
@@ -37,6 +37,17 @@ class GarminConfigOut(BaseModel):
from_attributes = True
def _wants_more_history(old: int, new: int) -> bool:
"""True if `new` lookback requests older data than `old` (-1 = all-time)."""
if new == old:
return False
if new == -1: # all-time requested where it wasn't before
return True
if old == -1: # was all-time, now finite → narrower, not more
return False
return new > old
@router.get("/config", response_model=GarminConfigOut)
async def get_config(
db: AsyncSession = Depends(get_db),
@@ -111,6 +122,16 @@ async def save_config(
if not cfg:
raise HTTPException(status_code=400, detail="No Garmin account connected — password required for first-time setup")
# If the user is now asking for MORE history than before, reset last_sync_at so
# the next sync treats it as a first sync and does a one-time backfill of the
# wider lookback window (then resumes cheap incremental syncs). Scheduled syncs
# otherwise only refresh the last day or two, so without this an increased
# lookback would never actually fetch the older data.
old_lookback = cfg.sync_lookback_days if cfg.sync_lookback_days is not None else 30
if _wants_more_history(old_lookback, body.sync_lookback_days):
cfg.last_sync_at = None
cfg.last_sync_status = "Lookback increased — backfill on next sync"
cfg.sync_enabled = body.sync_enabled
cfg.sync_activities = body.sync_activities
cfg.sync_wellness = body.sync_wellness