mirror of
https://github.com/immich-app/immich.git
synced 2026-04-18 12:19:35 +00:00
feat: auth logout page (#27831)
* feat: auth logout page * feat: skip login if already logged in
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 }))
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import AuthPageLayout from '$lib/components/layouts/AuthPageLayout.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { updateMyUser } from '@immich/sdk';
|
||||
import { Alert, Button, Field, HelperText, PasswordInput, Stack, Text } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -23,7 +25,7 @@
|
||||
}
|
||||
|
||||
await updateMyUser({ userUpdateMeDto: { password } });
|
||||
await authManager.logout();
|
||||
await goto(Route.logout());
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
12
web/src/routes/auth/logout/+page.svelte
Normal file
12
web/src/routes/auth/logout/+page.svelte
Normal file
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { LoadingSpinner } from '@immich/ui';
|
||||
|
||||
void authManager.logout();
|
||||
</script>
|
||||
|
||||
<div class="h-screen w-screen overflow-hidden">
|
||||
<div class="m-auto">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user