diff --git a/backend/app/api/garmin_sync.py b/backend/app/api/garmin_sync.py index e7cee88..967037b 100644 --- a/backend/app/api/garmin_sync.py +++ b/backend/app/api/garmin_sync.py @@ -14,7 +14,7 @@ router = APIRouter() class GarminConfigIn(BaseModel): email: str - password: str # plaintext; encrypted before storage + password: Optional[str] = None # plaintext; encrypted before storage. None = keep existing. sync_enabled: bool = True sync_activities: bool = True sync_wellness: bool = True @@ -69,47 +69,48 @@ async def save_config( current_user: User = Depends(get_current_user), ): """ - Save (or replace) Garmin Connect credentials. - Attempts a test login first so we can store the initial OAuth token and - fail fast on wrong credentials. + Save Garmin Connect settings. If a password is provided, re-authenticates and + refreshes the stored OAuth token. If no password is provided, only updates the + non-credential settings (toggles, lookback days) without re-logging in. """ from app.services.garmin_connect_sync import encrypt_password, authenticate_garmin - enc = encrypt_password(body.password) - - # Test login — raises HTTPException on auth failure - try: - garmin, token_store = authenticate_garmin(body.email, enc, None) - except Exception as exc: - raise HTTPException(status_code=400, detail=f"Garmin login failed: {exc}") - result = await db.execute( select(GarminConnectConfig).where(GarminConnectConfig.user_id == current_user.id) ) cfg = result.scalar_one_or_none() - if cfg: - cfg.email = body.email - cfg.password_enc = enc - cfg.token_store = token_store - cfg.sync_enabled = body.sync_enabled - cfg.sync_activities = body.sync_activities - cfg.sync_wellness = body.sync_wellness - cfg.sync_lookback_days = body.sync_lookback_days - cfg.last_sync_status = "Credentials updated" + if body.password: + # Credentials update — test-login before saving + enc = encrypt_password(body.password) + try: + garmin, token_store = authenticate_garmin(body.email, enc, None) + except Exception as exc: + raise HTTPException(status_code=400, detail=f"Garmin login failed: {exc}") + + if cfg: + cfg.email = body.email + cfg.password_enc = enc + cfg.token_store = token_store + cfg.last_sync_status = "Credentials updated" + else: + cfg = GarminConnectConfig( + user_id=current_user.id, + email=body.email, + password_enc=enc, + token_store=token_store, + last_sync_status="Connected", + ) + db.add(cfg) else: - cfg = GarminConnectConfig( - user_id=current_user.id, - email=body.email, - password_enc=enc, - token_store=token_store, - sync_enabled=body.sync_enabled, - sync_activities=body.sync_activities, - sync_wellness=body.sync_wellness, - sync_lookback_days=body.sync_lookback_days, - last_sync_status="Connected", - ) - db.add(cfg) + # Settings-only update — password unchanged + if not cfg: + raise HTTPException(status_code=400, detail="No Garmin account connected — password required for first-time setup") + + cfg.sync_enabled = body.sync_enabled + cfg.sync_activities = body.sync_activities + cfg.sync_wellness = body.sync_wellness + cfg.sync_lookback_days = body.sync_lookback_days await db.commit() await db.refresh(cfg)