From 4ffa26c9695a5bdbf4d5b4d7d49841955efe9408 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Wed, 15 Apr 2026 16:33:52 -0400 Subject: [PATCH] feat: auth logout page (#27831) * feat: auth logout page * feat: skip login if already logged in --- .../navigation-bar/account-info-panel.svelte | 13 +++++----- .../navigation-bar/navigation-bar.svelte | 5 +--- web/src/lib/managers/auth-manager.svelte.ts | 26 ++++++++++--------- web/src/lib/managers/event-manager.svelte.ts | 1 + web/src/lib/route.ts | 1 + web/src/lib/stores/websocket.ts | 2 +- web/src/lib/utils/server.ts | 2 ++ .../routes/auth/change-password/+page.svelte | 4 ++- web/src/routes/auth/login/+page.ts | 8 +++++- web/src/routes/auth/logout/+page.svelte | 12 +++++++++ 10 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 web/src/routes/auth/logout/+page.svelte diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte index a2aa26efd8..21e093a272 100644 --- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte @@ -14,12 +14,11 @@ import { fade } from 'svelte/transition'; import UserAvatar from '../user-avatar.svelte'; - interface Props { - onLogout: () => void; + type Props = { onClose?: () => void; - } + }; - let { onLogout, onClose = () => {} }: Props = $props(); + let { onClose }: Props = $props(); let info: ServerAboutResponseDto | undefined = $state(); @@ -48,7 +47,7 @@ size="tiny" shape="round" onclick={async () => { - onClose(); + onClose?.(); await modalManager.show(AvatarEditModal); }} /> @@ -99,7 +98,7 @@
{ - onClose(); + onClose?.(); if (info) { await modalManager.show(HelpAndFeedbackModal, { info }); } diff --git a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte index 29f125097f..4ec153ad23 100644 --- a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte @@ -178,10 +178,7 @@ {#if shouldShowAccountInfoPanel} - authManager.logout()} - onClose={() => (shouldShowAccountInfoPanel = false)} - /> + (shouldShowAccountInfoPanel = false)} /> {/if}
diff --git a/web/src/lib/managers/auth-manager.svelte.ts b/web/src/lib/managers/auth-manager.svelte.ts index a6fe5bf6af..43de2221fb 100644 --- a/web/src/lib/managers/auth-manager.svelte.ts +++ b/web/src/lib/managers/auth-manager.svelte.ts @@ -41,6 +41,12 @@ class AuthManager { return this.#preferences; } + constructor() { + eventManager.on({ + SessionDelete: () => goto(Route.logout()), + }); + } + async load() { if (authManager.authenticated) { return; @@ -84,30 +90,26 @@ class AuthManager { } async logout() { - let redirectUri; + let redirectUri = Route.login(); try { const response = await logout(); if (response.redirectUri) { redirectUri = response.redirectUri; } - } catch (error) { - console.log('Error logging out:', error); + } catch { + // noop } - redirectUri = redirectUri ?? Route.login(); - - try { - if (redirectUri.startsWith('/')) { - await goto(redirectUri); - } else { - globalThis.location.href = redirectUri; - } - } finally { + if (redirectUri.startsWith('/')) { this.isPurchased = false; this.reset(); eventManager.emit('AuthLogout'); + + await goto(redirectUri); + } else { + globalThis.location.href = redirectUri; } } diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts index 323d6a8315..c5764c5af2 100644 --- a/web/src/lib/managers/event-manager.svelte.ts +++ b/web/src/lib/managers/event-manager.svelte.ts @@ -74,6 +74,7 @@ export type Events = { UserAdminDeleted: [{ id: string }]; SessionLocked: []; + SessionDelete: []; SystemConfigUpdate: [SystemConfigDto]; diff --git a/web/src/lib/route.ts b/web/src/lib/route.ts index 4cb1965122..2fdfb4a052 100644 --- a/web/src/lib/route.ts +++ b/web/src/lib/route.ts @@ -51,6 +51,7 @@ export const Docs = { export const Route = { // auth login: (params?: { continue?: string; autoLaunch?: 0 | 1 }) => '/auth/login' + asQueryString(params), + logout: (params?: { continue?: string }) => '/auth/logout' + asQueryString(params), register: () => '/auth/register', changePassword: () => '/auth/change-password', onboarding: (params?: { step?: string }) => '/auth/onboarding' + asQueryString(params), diff --git a/web/src/lib/stores/websocket.ts b/web/src/lib/stores/websocket.ts index 4c47c84d5d..fec6e1d178 100644 --- a/web/src/lib/stores/websocket.ts +++ b/web/src/lib/stores/websocket.ts @@ -78,7 +78,7 @@ websocket } }) .on('on_new_release', (event) => eventManager.emit('ReleaseEvent', event)) - .on('on_session_delete', () => authManager.logout()) + .on('on_session_delete', () => eventManager.emit('SessionDelete')) .on('on_user_delete', (id) => eventManager.emit('UserAdminDeleted', { id })) .on('on_asset_update', (asset) => eventManager.emit('AssetUpdate', asset)) .on('on_person_thumbnail', (id) => eventManager.emit('PersonThumbnailReady', { id })) diff --git a/web/src/lib/utils/server.ts b/web/src/lib/utils/server.ts index 50fe4d72fe..150260addf 100644 --- a/web/src/lib/utils/server.ts +++ b/web/src/lib/utils/server.ts @@ -1,3 +1,4 @@ +import { authManager } from '$lib/managers/auth-manager.svelte'; import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte'; import { serverConfigManager } from '$lib/managers/server-config-manager.svelte'; import { initLanguage } from '$lib/utils'; @@ -13,6 +14,7 @@ async function _init(fetch: Fetch) { defaults.fetch = fetch; await initLanguage(); await serverConfigManager.init(); + await authManager.load(); if (!serverConfigManager.value.maintenanceMode) { await featureFlagsManager.init(); diff --git a/web/src/routes/auth/change-password/+page.svelte b/web/src/routes/auth/change-password/+page.svelte index d8b4d9e00f..49578ff804 100644 --- a/web/src/routes/auth/change-password/+page.svelte +++ b/web/src/routes/auth/change-password/+page.svelte @@ -1,6 +1,8 @@ diff --git a/web/src/routes/auth/login/+page.ts b/web/src/routes/auth/login/+page.ts index 323f608754..0a2318abd7 100644 --- a/web/src/routes/auth/login/+page.ts +++ b/web/src/routes/auth/login/+page.ts @@ -1,3 +1,4 @@ +import { authManager } from '$lib/managers/auth-manager.svelte'; import { serverConfigManager } from '$lib/managers/server-config-manager.svelte'; import { Route } from '$lib/route'; import { getFormatter } from '$lib/utils/i18n'; @@ -7,6 +8,11 @@ import type { PageLoad } from './$types'; export const load = (async ({ parent, url }) => { await parent(); + const continueUrl = url.searchParams.get('continue') || Route.photos(); + if (authManager.authenticated) { + redirect(307, continueUrl); + } + if (!serverConfigManager.value.isInitialized) { // Admin not registered redirect(307, Route.register()); @@ -17,6 +23,6 @@ export const load = (async ({ parent, url }) => { meta: { title: $t('login'), }, - continueUrl: url.searchParams.get('continue') || Route.photos(), + continueUrl, }; }) satisfies PageLoad; diff --git a/web/src/routes/auth/logout/+page.svelte b/web/src/routes/auth/logout/+page.svelte new file mode 100644 index 0000000000..d7bd401681 --- /dev/null +++ b/web/src/routes/auth/logout/+page.svelte @@ -0,0 +1,12 @@ + + +
+
+ +
+