From c3084830637013a07da4c02e3275d86ba2ff1fca Mon Sep 17 00:00:00 2001 From: Hongwei Date: Wed, 28 Oct 2026 19:39:53 +0100 Subject: [PATCH] fix: keep Opey's status on session refusal and stop retrying it The /backend/opey/auth route now passes on a 4xx answer from Opey instead of turning it into a 500, and names the cause (missing credentials, consent not ACCEPTED). The chat widget does not call the route for a logged-out visitor, does not retry a 4xx, and a consent that is still INITIATED is reported instead of being replaced by a new one. --- .../shared/src/lib/components/OpeyChat.svelte | 37 +--- .../lib/opey/controllers/SessionController.ts | 51 ++++- packages/shared/src/lib/opey/errors.ts | 40 ++++ .../src/lib/opey/opeyAuthStatus.test.ts | 196 ++++++++++++++++++ .../lib/opey/services/OpeySessionService.ts | 15 +- .../shared/src/lib/opey/state/SessionState.ts | 10 +- .../lib/server/obp/OBPIntegrationService.ts | 37 ++++ .../shared/src/lib/server/opey/handlers.ts | 25 ++- 8 files changed, 371 insertions(+), 40 deletions(-) create mode 100644 packages/shared/src/lib/opey/errors.ts create mode 100644 packages/shared/src/lib/opey/opeyAuthStatus.test.ts diff --git a/packages/shared/src/lib/components/OpeyChat.svelte b/packages/shared/src/lib/components/OpeyChat.svelte index fdc34667..53ca72b0 100644 --- a/packages/shared/src/lib/components/OpeyChat.svelte +++ b/packages/shared/src/lib/components/OpeyChat.svelte @@ -523,37 +523,10 @@ await sessionController.init(); } - // Add retry logic with exponential backoff + // Retry with backoff lives in the controller: a logged-out visitor makes no request, and a + // refusal such as 401 is reported once with its cause instead of being retried. async function initializeOpeySessionWithRetry(maxRetries = 3, baseDelay = 1000) { - let lastError: string | null = null; - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - await initializeOpeySession(); - if (session.status === 'ready') { - logger.debug(`Opey session initialized successfully on attempt ${attempt}`); - return; - } - // Init didn't throw but the session isn't ready — capture whatever - // session.error was set to internally so it isn't swallowed. - if (session.error) lastError = session.error; - } catch (error) { - lastError = error instanceof Error ? error.message : String(error); - logger.warn(`Session initialization attempt ${attempt} failed:`, error); - } - - if (attempt < maxRetries) { - const delay = baseDelay * Math.pow(2, attempt - 1); // Exponential backoff - logger.debug(`Retrying session initialization in ${delay}ms...`); - await new Promise((resolve) => setTimeout(resolve, delay)); - } - } - - // Surface the real underlying cause rather than just "after N attempts". - const finalMessage = lastError - ? `Failed to initialize after ${maxRetries} attempts: ${lastError}` - : `Failed to initialize after ${maxRetries} attempts`; - logger.error(finalMessage); - sessionState.setStatus('error', finalMessage); + await sessionController.initWithRetry({ userAuthenticated, maxRetries, baseDelay }); } /** @@ -803,7 +776,7 @@