From 9fd088c693fcea67a7f110bffecca264c564678f Mon Sep 17 00:00:00 2001 From: Davide Scaini Date: Fri, 10 Apr 2026 18:34:53 +0200 Subject: [PATCH] =?UTF-8?q?=20=20-=20"Last=20sync:=20never":=20The=20old?= =?UTF-8?q?=20blocking=20sync=20was=20killed=20by=20nginx=20at=20120s=20be?= =?UTF-8?q?fore=20save=5Ftoken=20was=20reached.=20The=20activities=20made?= =?UTF-8?q?=20it=20to=20disk=20(ingestion=20happens=20per-activity=20as=20?= =?UTF-8?q?it=20goes),=20but=20the=20token's=20=20=20last=5Fsync=5Fat=20ti?= =?UTF-8?q?mestamp=20was=20never=20written.=20After=20deploying,=20do=20a?= =?UTF-8?q?=20soft=20reset=20=E2=80=94=20it'll=20set=20last=5Fsync=5Fat=20?= =?UTF-8?q?to=20your=20most=20recent=20activity's=20timestamp=20so=20the?= =?UTF-8?q?=20next=20sync=20only=20fetches=20newer=20ones.=20=20=20-=20Res?= =?UTF-8?q?et=20404:=20Added=20POST=20/api/strava/reset=20to=20serve/serve?= =?UTF-8?q?r.py.=20The=20soft=20reset=20now=20looks=20in=20=5Fmerged/index?= =?UTF-8?q?.json=20first=20(multi-user=20path),=20falling=20back=20to=20in?= =?UTF-8?q?dex.json.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bincio/serve/server.py | 65 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/bincio/serve/server.py b/bincio/serve/server.py index 1b07965..83051fa 100644 --- a/bincio/serve/server.py +++ b/bincio/serve/server.py @@ -663,16 +663,65 @@ async def strava_status(bincio_session: Optional[str] = Cookie(default=None)) -> if not strava_client_id: return JSONResponse({"configured": False, "connected": False, "last_sync": None}) dd = _get_data_dir() / user.handle - token_path = dd / "strava_token.json" - connected = token_path.exists() - last_sync = None - if connected: + from bincio.extract.strava_api import load_token + token = load_token(dd) + return JSONResponse({ + "configured": True, + "connected": token is not None, + "last_sync": token.get("last_sync_at") if token else None, + }) + + +@app.post("/api/strava/reset") +async def strava_reset(request: Request, bincio_session: Optional[str] = Cookie(default=None)) -> JSONResponse: + """Reset last_sync_at so the next sync re-fetches from a chosen point. + + mode=soft — set to the started_at of the most recent activity on disk + (next sync only fetches activities newer than the last known one) + mode=hard — clear last_sync_at entirely + (next sync re-downloads full Strava history, skipping existing files) + """ + user = _require_user(bincio_session) + dd = _get_data_dir() / user.handle + from bincio.extract.strava_api import load_token, save_token + token = load_token(dd) + if token is None: + raise HTTPException(400, "Not connected to Strava") + + body = await request.json() + mode = body.get("mode", "soft") + + if mode == "hard": + token.pop("last_sync_at", None) + save_token(dd, token) + return JSONResponse({"ok": True, "mode": "hard", "last_sync_at": None}) + + # soft: find the most recent started_at across the user's merged index + from datetime import datetime, timezone + last_ts: int | None = None + for index_path in [dd / "_merged" / "index.json", dd / "index.json"]: + if not index_path.exists(): + continue try: - token = json.loads(token_path.read_text()) - last_sync = token.get("last_sync_at") + index_data = json.loads(index_path.read_text(encoding="utf-8")) + started_ats = [ + a.get("started_at") for a in index_data.get("activities", []) + if a.get("started_at") + ] + if started_ats: + latest = max(started_ats) + dt = datetime.fromisoformat(latest.replace("Z", "+00:00")) + last_ts = int(dt.astimezone(timezone.utc).timestamp()) + break except Exception: - pass - return JSONResponse({"configured": True, "connected": connected, "last_sync": last_sync}) + continue + + if last_ts is None: + token.pop("last_sync_at", None) + else: + token["last_sync_at"] = last_ts + save_token(dd, token) + return JSONResponse({"ok": True, "mode": "soft", "last_sync_at": last_ts}) @app.get("/api/strava/auth-url")