mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor: auth manager (#27638)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import {
|
||||
@@ -157,7 +157,7 @@ class ActivityManager {
|
||||
const [liked] = await getActivities({
|
||||
albumId,
|
||||
assetId,
|
||||
userId: get(user).id,
|
||||
userId: authManager.user.id,
|
||||
$type: ReactionType.Like,
|
||||
level: assetId ? undefined : ReactionLevel.Album,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { AssetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
|
||||
import { resetSavedUser, user } from '$lib/stores/user.store';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { timelineAssetFactory } from '@test-data/factories/asset-factory';
|
||||
import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
||||
import { userAdminFactory } from '@test-data/factories/user-factory';
|
||||
|
||||
describe('AssetMultiSelectManager', () => {
|
||||
@@ -32,14 +33,15 @@ describe('AssetMultiSelectManager', () => {
|
||||
const cleanup = $effect.root(() => {
|
||||
expect(sut.isAllUserOwned).toBe(false);
|
||||
|
||||
user.set(user1);
|
||||
authManager.setUser(user1);
|
||||
authManager.setPreferences(preferencesFactory.build());
|
||||
expect(sut.isAllUserOwned).toBe(true);
|
||||
|
||||
user.set(user2);
|
||||
authManager.setUser(user2);
|
||||
expect(sut.isAllUserOwned).toBe(false);
|
||||
});
|
||||
|
||||
cleanup();
|
||||
resetSavedUser();
|
||||
authManager.reset();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { AssetVisibility, type UserAdminResponseDto } from '@immich/sdk';
|
||||
import { AssetVisibility } from '@immich/sdk';
|
||||
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||
import { fromStore } from 'svelte/store';
|
||||
|
||||
export type AssetMultiSelectOptions = {
|
||||
resetOnNavigate?: boolean;
|
||||
};
|
||||
export class AssetMultiSelectManager {
|
||||
#selectedMap = new SvelteMap<string, TimelineAsset>();
|
||||
#user = fromStore<UserAdminResponseDto | undefined>(user);
|
||||
#userId = $derived(this.#user.current?.id);
|
||||
|
||||
selectAll = $state(false);
|
||||
startAsset = $state<TimelineAsset | null>(null);
|
||||
@@ -23,12 +20,16 @@ export class AssetMultiSelectManager {
|
||||
selectionActive = $derived(this.#selectedMap.size > 0);
|
||||
|
||||
assets = $derived(Array.from(this.#selectedMap.values()));
|
||||
ownedAssets = $derived(this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets);
|
||||
ownedAssets = $derived(
|
||||
authManager.authenticated ? this.assets.filter((asset) => asset.ownerId === authManager.user.id) : this.assets,
|
||||
);
|
||||
|
||||
isAllTrashed = $derived(this.assets.every((asset) => asset.isTrashed));
|
||||
isAllArchived = $derived(this.assets.every((asset) => asset.visibility === AssetVisibility.Archive));
|
||||
isAllFavorite = $derived(this.assets.every((asset) => asset.isFavorite));
|
||||
isAllUserOwned = $derived(this.assets.every((asset) => asset.ownerId === this.#userId));
|
||||
isAllUserOwned = $derived(
|
||||
authManager.authenticated && this.assets.every((asset) => asset.ownerId === authManager.user.id),
|
||||
);
|
||||
|
||||
#unsubscribe?: () => void;
|
||||
|
||||
@@ -44,7 +45,9 @@ export class AssetMultiSelectManager {
|
||||
}
|
||||
|
||||
getOwnedAssets() {
|
||||
return this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets;
|
||||
return authManager.authenticated
|
||||
? this.assets.filter((asset) => asset.ownerId === authManager.user.id)
|
||||
: this.assets;
|
||||
}
|
||||
|
||||
hasSelectedAsset(assetId: string) {
|
||||
|
||||
@@ -1,31 +1,86 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { isSharedLinkRoute } from '$lib/utils/navigation';
|
||||
import { getAboutInfo, logout, type UserAdminResponseDto } from '@immich/sdk';
|
||||
import {
|
||||
getAboutInfo,
|
||||
getMyPreferences,
|
||||
getMyUser,
|
||||
logout,
|
||||
type UserAdminResponseDto,
|
||||
type UserPreferencesResponseDto,
|
||||
} from '@immich/sdk';
|
||||
|
||||
class AuthManager {
|
||||
isPurchased = $state(false);
|
||||
isSharedLink = $derived(isSharedLinkRoute(page.route?.id));
|
||||
params = $derived(this.isSharedLink ? { key: page.params.key, slug: page.params.slug } : {});
|
||||
|
||||
constructor() {
|
||||
eventManager.on({
|
||||
AuthUserLoaded: (user) => this.onAuthUserLoaded(user),
|
||||
});
|
||||
#user = $state<UserAdminResponseDto>();
|
||||
#preferences = $state<UserPreferencesResponseDto>();
|
||||
|
||||
get authenticated() {
|
||||
return !!(this.#user && this.#preferences);
|
||||
}
|
||||
|
||||
private async onAuthUserLoaded(user: UserAdminResponseDto) {
|
||||
if (user.license?.activatedAt) {
|
||||
authManager.isPurchased = true;
|
||||
get user() {
|
||||
if (!this.#user) {
|
||||
throw new TypeError('AuthManager.user is undefined');
|
||||
}
|
||||
|
||||
return this.#user;
|
||||
}
|
||||
|
||||
get preferences() {
|
||||
if (!this.#preferences) {
|
||||
throw new TypeError('AuthManager.preferences is undefined');
|
||||
}
|
||||
|
||||
return this.#preferences;
|
||||
}
|
||||
|
||||
async load() {
|
||||
if (authManager.authenticated) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serverInfo = await getAboutInfo().catch(() => undefined);
|
||||
if (serverInfo?.licensed) {
|
||||
authManager.isPurchased = true;
|
||||
if (!this.#hasAuthCookie()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.refresh();
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
try {
|
||||
const [user, preferences] = await Promise.all([getMyUser(), getMyPreferences()]);
|
||||
this.#preferences = preferences;
|
||||
this.#user = user;
|
||||
|
||||
if (user.license?.activatedAt) {
|
||||
this.isPurchased = true;
|
||||
} else {
|
||||
// check server status
|
||||
const serverInfo = await getAboutInfo().catch(() => {});
|
||||
if (serverInfo?.licensed) {
|
||||
this.isPurchased = true;
|
||||
}
|
||||
}
|
||||
|
||||
eventManager.emit('AuthUserLoaded', user);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
setUser(user: UserAdminResponseDto) {
|
||||
this.#user = user;
|
||||
}
|
||||
|
||||
setPreferences(preferences: UserPreferencesResponseDto) {
|
||||
this.#preferences = preferences;
|
||||
}
|
||||
|
||||
async logout() {
|
||||
@@ -50,9 +105,31 @@ class AuthManager {
|
||||
}
|
||||
} finally {
|
||||
this.isPurchased = false;
|
||||
|
||||
this.reset();
|
||||
eventManager.emit('AuthLogout');
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.#user = undefined;
|
||||
this.#preferences = undefined;
|
||||
}
|
||||
|
||||
#hasAuthCookie() {
|
||||
if (!browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const cookie of document.cookie.split('; ')) {
|
||||
const [name] = cookie.split('=');
|
||||
if (name === 'immich_is_authenticated') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const authManager = new AuthManager();
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { asLocalTimeISO } from '$lib/utils/date-time';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { deleteMemory, type MemoryResponseDto, removeMemoryAssets, searchMemories, updateMemory } from '@immich/sdk';
|
||||
import { DateTime } from 'luxon';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
type MemoryIndex = {
|
||||
memoryIndex: number;
|
||||
@@ -31,7 +30,7 @@ class MemoryManager {
|
||||
});
|
||||
|
||||
// loaded event might have already happened
|
||||
if (get(user)) {
|
||||
if (authManager.authenticated) {
|
||||
void this.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user