"""HTTP client for diora's sync API (see the repo's CLAUDE.md, "Sync API for local clients"). Status: that API is provisional — expect endpoint/field changes as the server-side implementation settles. """ from __future__ import annotations import requests class ApiError(Exception): pass def _headers(token: str) -> dict[str, str]: return {"Authorization": f"Bearer {token}"} def fetch_sync_snapshot(server_url: str, token: str) -> dict: resp = requests.get(f"{server_url}/api/sync/", headers=_headers(token), timeout=30) if resp.status_code == 401: raise ApiError("Authentifizierung fehlgeschlagen — Token falsch oder abgelaufen?") resp.raise_for_status() return resp.json() def fetch_book_data(server_url: str, token: str, book_id: int) -> dict: resp = requests.get(f"{server_url}/books/{book_id}/data/", headers=_headers(token), timeout=120) if resp.status_code == 401: raise ApiError("Authentifizierung fehlgeschlagen — Token falsch oder abgelaufen?") if resp.status_code == 404: raise ApiError(f"Buch {book_id} nicht gefunden (falscher Owner oder gelöscht?)") resp.raise_for_status() return resp.json() def post_progress( server_url: str, token: str, book_id: int, *, scroll_fraction: float, position_anchor: str, force: bool = False ) -> None: body = {"scroll_fraction": scroll_fraction, "position_anchor": position_anchor, "force": force} resp = requests.post( f"{server_url}/books/{book_id}/progress/", headers=_headers(token), json=body, timeout=30 ) if resp.status_code == 401: raise ApiError("Authentifizierung fehlgeschlagen — Token falsch oder abgelaufen?") if resp.status_code == 404: raise ApiError(f"Buch {book_id} nicht gefunden (falscher Owner oder gelöscht?)") resp.raise_for_status()