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")