feat: auth logout page (#27831)

* feat: auth logout page

* feat: skip login if already logged in
This commit is contained in:
Jason Rasmussen
2026-04-15 16:33:52 -04:00
committed by GitHub
parent ac06514db5
commit 4ffa26c969
10 changed files with 48 additions and 26 deletions
@@ -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 @@
<div class="mb-4 flex flex-col">
<Button
class="m-1 mx-4 rounded-none rounded-b-3xl bg-white p-3 dark:bg-immich-dark-primary/10"
onclick={onLogout}
href={Route.logout()}
leadingIcon={mdiLogout}
variant="ghost"
color="secondary">{$t('sign_out')}</Button
@@ -109,7 +108,7 @@
type="button"
class="text-center mt-4 underline text-xs text-primary"
onclick={async () => {
onClose();
onClose?.();
if (info) {
await modalManager.show(HelpAndFeedbackModal, { info });
}
@@ -178,10 +178,7 @@
</button>
{#if shouldShowAccountInfoPanel}
<AccountInfoPanel
onLogout={() => authManager.logout()}
onClose={() => (shouldShowAccountInfoPanel = false)}
/>
<AccountInfoPanel onClose={() => (shouldShowAccountInfoPanel = false)} />
{/if}
</div>
</section>
+14 -12
View File
@@ -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;
}
}
@@ -74,6 +74,7 @@ export type Events = {
UserAdminDeleted: [{ id: string }];
SessionLocked: [];
SessionDelete: [];
SystemConfigUpdate: [SystemConfigDto];
+1
View File
@@ -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),
+1 -1
View File
@@ -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 }))
+2
View File
@@ -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();