From 7202a59afc045f56b25fba893bd8e6f4ddf605d7 Mon Sep 17 00:00:00 2001 From: neon <18559658+neonsolstice@users.noreply.github.com> Date: Sun, 18 Oct 2026 18:07:08 +0000 Subject: [PATCH] fix(auth): wait for an unreachable server instead of signing out A page load during a server restart read the failed session refresh as signed out and stranded the reader on /login. Boot now holds the first navigation behind a reconnect screen and retries until the server answers, and api() keeps the session when a refresh gets a 5xx or no connection. --- client/src/App.vue | 4 + .../auth/components/ServerReconnectScreen.vue | 32 ++++++ .../__tests__/ServerReconnectScreen.spec.ts | 41 +++++++ .../composables/__tests__/useAuth.spec.ts | 51 ++++++++- .../__tests__/useSessionBootstrap.spec.ts | 106 ++++++++++++++++++ .../src/features/auth/composables/useAuth.ts | 32 ++++-- .../auth/composables/useSessionBootstrap.ts | 73 ++++++++++++ client/src/lib/__tests__/api.spec.ts | 47 ++++++++ client/src/lib/api.ts | 16 ++- client/src/locales/en.json | 6 + client/src/main.ts | 16 +-- .../src/router/__tests__/auth.guard.spec.ts | 28 ++++- client/src/router/guards/auth.guard.ts | 3 + 13 files changed, 429 insertions(+), 26 deletions(-) create mode 100644 client/src/features/auth/components/ServerReconnectScreen.vue create mode 100644 client/src/features/auth/components/__tests__/ServerReconnectScreen.spec.ts create mode 100644 client/src/features/auth/composables/__tests__/useSessionBootstrap.spec.ts create mode 100644 client/src/features/auth/composables/useSessionBootstrap.ts diff --git a/client/src/App.vue b/client/src/App.vue index 1afbd619a..42a4ad743 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -10,6 +10,8 @@ import ChangePasswordDialog from '@/features/auth/ChangePasswordDialog.vue' import WhatsNewDialog from '@/features/whats-new/WhatsNewDialog.vue' import { useWhatsNew } from '@/features/whats-new/composables/useWhatsNew' import { useAuth } from '@/features/auth/composables/useAuth' +import { useSessionBootstrap } from '@/features/auth/composables/useSessionBootstrap' +import ServerReconnectScreen from '@/features/auth/components/ServerReconnectScreen.vue' import TtsMiniPlayer from '@/features/tts/components/TtsMiniPlayer.vue' import MediaOverlayMiniPlayer from '@/features/reader/media-overlay/components/MediaOverlayMiniPlayer.vue' import PodcastMiniPlayer from '@/features/podcast/components/PodcastMiniPlayer.vue' @@ -44,6 +46,7 @@ const chartTokens = useChartTokens() const route = useRoute() const { user } = useAuth() +const { unreachable: serverUnreachable } = useSessionBootstrap() const { popupOpen, evaluate, syncPopup } = useWhatsNew() const podcastPlayer = APP_FEATURES.podcasts ? usePodcastPlayer() : null const podcastDownloads = APP_FEATURES.podcasts ? usePodcastDownloadBatches() : null @@ -88,6 +91,7 @@ provide(