From 6db270546645892671292cc7f6cce1694b6ac943 Mon Sep 17 00:00:00 2001 From: OpenSquared Date: Sat, 18 Jul 2026 21:14:37 +0200 Subject: [PATCH] feat: saxo connector --- backend/services/saxo_auth.py | 9 ++++++++- backend/services/saxo_scheduler.py | 14 +++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/backend/services/saxo_auth.py b/backend/services/saxo_auth.py index 00638ea..1df846d 100644 --- a/backend/services/saxo_auth.py +++ b/backend/services/saxo_auth.py @@ -130,7 +130,14 @@ def get_valid_access_token() -> Optional[str]: if datetime.now(timezone.utc) < expires_at - timedelta(seconds=60): return stored["access_token"] - refreshed = refresh_tokens() + try: + refreshed = refresh_tokens() + except Exception as e: + # Refresh token rejected — clear the dead session instead of leaving a token that + # looks "connected" in /status but gets 401'd on the next real API call. + logger.error(f"[Saxo] Refresh failed on-demand, clearing dead session: {e}") + disconnect() + return None return refreshed["access_token"] if refreshed else None diff --git a/backend/services/saxo_scheduler.py b/backend/services/saxo_scheduler.py index 8f68065..850b9ff 100644 --- a/backend/services/saxo_scheduler.py +++ b/backend/services/saxo_scheduler.py @@ -44,10 +44,18 @@ def set_watchlist(symbols: list[str]): def _refresh_loop(stop: threading.Event): while not stop.wait(0): try: - from .saxo_auth import get_status, refresh_tokens + from .saxo_auth import get_status, refresh_tokens, disconnect if get_status().get("connected"): - refresh_tokens() - logger.info("[Saxo Scheduler] Token refreshed proactively") + try: + refresh_tokens() + logger.info("[Saxo Scheduler] Token refreshed proactively") + except Exception as e: + # Refresh token rejected (expired/rotated/invalid) — the stored access + # token is now dead even though its expires_at hasn't passed yet. Clear + # it so /api/saxo/status stops reporting a stale "connected" state, and + # the user gets a clear "reconnect" signal instead of silent 401s later. + logger.error(f"[Saxo Scheduler] Refresh failed, clearing dead session: {e}") + disconnect() except Exception as e: logger.error(f"[Saxo Scheduler] Refresh loop error: {e}") stop.wait(timeout=REFRESH_INTERVAL_SECONDS)