mirror of
https://github.com/immich-app/immich.git
synced 2026-05-24 00:22:29 -04:00
refactor: auth manager (#27638)
This commit is contained in:
@@ -3,7 +3,6 @@ import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { downloadRequest, withError } from '$lib/utils';
|
||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||
import { getFormatter } from '$lib/utils/i18n';
|
||||
@@ -26,7 +25,6 @@ import {
|
||||
type DownloadInfoDto,
|
||||
type ExifResponseDto,
|
||||
type StackResponseDto,
|
||||
type UserPreferencesResponseDto,
|
||||
type UserResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { toastManager } from '@immich/ui';
|
||||
@@ -102,9 +100,8 @@ export const downloadUrl = (url: string, filename: string) => {
|
||||
};
|
||||
|
||||
export const downloadArchive = async (fileName: string, options: Omit<DownloadInfoDto, 'archiveSize'>) => {
|
||||
const $preferences = get<UserPreferencesResponseDto | undefined>(preferences);
|
||||
const dto = { ...options, archiveSize: $preferences?.download.archiveSize };
|
||||
|
||||
const archiveSize = authManager.authenticated ? authManager.preferences.download.archiveSize : undefined;
|
||||
const dto = { ...options, archiveSize };
|
||||
const [error, downloadInfo] = await withError(() => getDownloadInfo({ ...authManager.params, downloadInfoDto: dto }));
|
||||
if (error) {
|
||||
const $t = get(t);
|
||||
|
||||
@@ -1,82 +1,45 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { preferences as preferences$, user as user$ } from '$lib/stores/user.store';
|
||||
import { userInteraction } from '$lib/stores/user.svelte';
|
||||
import { getMyPreferences, getMyUser, getStorage } from '@immich/sdk';
|
||||
import { getStorage } from '@immich/sdk';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { DateTime } from 'luxon';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export interface AuthOptions {
|
||||
admin?: true;
|
||||
public?: boolean;
|
||||
}
|
||||
|
||||
export const loadUser = async () => {
|
||||
try {
|
||||
let user = get(user$);
|
||||
let preferences = get(preferences$);
|
||||
|
||||
if ((!user || !preferences) && hasAuthCookie()) {
|
||||
[user, preferences] = await Promise.all([getMyUser(), getMyPreferences()]);
|
||||
user$.set(user);
|
||||
preferences$.set(preferences);
|
||||
eventManager.emit('AuthUserLoaded', user);
|
||||
}
|
||||
return user;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const hasAuthCookie = (): boolean => {
|
||||
if (!browser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const cookie of document.cookie.split('; ')) {
|
||||
const [name] = cookie.split('=');
|
||||
if (name === 'immich_is_authenticated') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const authenticate = async (url: URL, options?: AuthOptions) => {
|
||||
const { public: publicRoute, admin: adminRoute } = options || {};
|
||||
const user = await loadUser();
|
||||
await authManager.load();
|
||||
|
||||
if (publicRoute) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
if (!authManager.authenticated) {
|
||||
redirect(307, Route.login({ continue: url.pathname + url.search }));
|
||||
}
|
||||
|
||||
if (adminRoute && !user.isAdmin) {
|
||||
if (adminRoute && !authManager.user.isAdmin) {
|
||||
redirect(307, Route.photos());
|
||||
}
|
||||
};
|
||||
|
||||
export const requestServerInfo = async () => {
|
||||
if (get(user$)) {
|
||||
if (authManager.authenticated) {
|
||||
const data = await getStorage();
|
||||
userInteraction.serverInfo = data;
|
||||
}
|
||||
};
|
||||
|
||||
export const getAccountAge = (): number => {
|
||||
const user = get(user$);
|
||||
|
||||
if (!user) {
|
||||
if (!authManager.authenticated) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const createdDate = DateTime.fromISO(user.createdAt);
|
||||
const createdDate = DateTime.fromISO(authManager.user.createdAt);
|
||||
const now = DateTime.now();
|
||||
const accountAge = now.diff(createdDate, 'days').days.toFixed(0);
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { CastDestinationType, CastState, type ICastDestination } from '$lib/managers/cast-manager.svelte';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import 'chromecast-caf-sender';
|
||||
import { Duration } from 'luxon';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
const FRAMEWORK_LINK = 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1';
|
||||
|
||||
@@ -26,8 +25,7 @@ export class GCastDestination implements ICastDestination {
|
||||
private currentUrl: string | null = null;
|
||||
|
||||
async initialize(): Promise<boolean> {
|
||||
const preferencesStore = get(preferences);
|
||||
if (!preferencesStore || !preferencesStore.cast.gCastEnabled) {
|
||||
if (!authManager.authenticated || authManager.preferences.cast.gCastEnabled) {
|
||||
this.isAvailable = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { uploadManager } from '$lib/managers/upload-manager.svelte';
|
||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||
import { resetSavedUser, user } from '$lib/stores/user.store';
|
||||
import { UploadState } from '$lib/types';
|
||||
import * as utils from '$lib/utils';
|
||||
import { AssetMediaStatus, type AssetMediaResponseDto, type UserAdminResponseDto } from '@immich/sdk';
|
||||
import { preferencesFactory } from '@test-data/factories/preferences-factory';
|
||||
import { get } from 'svelte/store';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { fileUploadHandler } from './file-uploader';
|
||||
@@ -18,7 +19,7 @@ describe('fileUploader error handling', () => {
|
||||
vi.clearAllMocks();
|
||||
vi.spyOn(uploadManager, 'getExtensions').mockReturnValue(['.jpg']);
|
||||
uploadAssetsStore.reset();
|
||||
resetSavedUser();
|
||||
authManager.reset();
|
||||
|
||||
// Stub out crypto to avoid that branch
|
||||
vi.stubGlobal('crypto', undefined);
|
||||
@@ -31,7 +32,7 @@ describe('fileUploader error handling', () => {
|
||||
describe(`for ${name}`, () => {
|
||||
beforeEach(() => {
|
||||
if (mockUser) {
|
||||
user.set(mockUserObject);
|
||||
authManager.setUser(mockUserObject);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,9 +59,10 @@ describe('fileUploader error handling', () => {
|
||||
}
|
||||
|
||||
it('should suppress errors on logout', async () => {
|
||||
user.set(mockUserObject);
|
||||
authManager.setUser(mockUserObject);
|
||||
authManager.setPreferences(preferencesFactory.build());
|
||||
vi.spyOn(utils, 'uploadRequest').mockImplementationOnce(() => {
|
||||
resetSavedUser();
|
||||
authManager.reset();
|
||||
return Promise.reject(mockError);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { uploadManager } from '$lib/managers/upload-manager.svelte';
|
||||
import { addAssetsToAlbums } from '$lib/services/album.service';
|
||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { UploadState } from '$lib/types';
|
||||
import { uploadRequest } from '$lib/utils';
|
||||
import { ExecutorQueue } from '$lib/utils/executor-queue';
|
||||
@@ -145,7 +144,7 @@ async function fileUploader({
|
||||
}: FileUploaderParams): Promise<string | undefined> {
|
||||
const fileCreatedAt = new Date(assetFile.lastModified).toISOString();
|
||||
const $t = get(t);
|
||||
const wasInitiallyLoggedIn = !!get(user);
|
||||
const wasInitiallyLoggedIn = !!authManager.authenticated;
|
||||
|
||||
uploadAssetsStore.markStarted(deviceAssetId);
|
||||
|
||||
@@ -238,7 +237,7 @@ async function fileUploader({
|
||||
} catch (error) {
|
||||
// If the user store no longer holds a user, it means they have logged out
|
||||
// In this case don't bother reporting any errors.
|
||||
if (wasInitiallyLoggedIn && !get(user)) {
|
||||
if (wasInitiallyLoggedIn && !authManager.authenticated) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { PUBLIC_IMMICH_BUY_HOST, PUBLIC_IMMICH_PAY_HOST } from '$env/static/public';
|
||||
import type { ImmichProduct } from '$lib/constants';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
|
||||
import { setServerLicense, setUserLicense, type LicenseResponseDto } from '@immich/sdk';
|
||||
import { loadUser } from './auth';
|
||||
|
||||
export const activateProduct = async (licenseKey: string, activationKey: string): Promise<LicenseResponseDto> => {
|
||||
// Send server key to user activation if user is not admin
|
||||
const user = await loadUser();
|
||||
const isServerActivation = user?.isAdmin && licenseKey.search('IMSV') !== -1;
|
||||
// TODO is this needed?
|
||||
await authManager.load();
|
||||
|
||||
const isServerActivation = authManager.user.isAdmin && licenseKey.search('IMSV') !== -1;
|
||||
const licenseKeyDto = { licenseKey, activationKey };
|
||||
// Send server key to user activation if user is not admin
|
||||
return isServerActivation ? setServerLicense({ licenseKeyDto }) : setUserLicense({ licenseKeyDto });
|
||||
};
|
||||
|
||||
|
||||
@@ -1,32 +1,19 @@
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { updateMyPreferences } from '@immich/sdk';
|
||||
import { DateTime } from 'luxon';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export const getButtonVisibility = (): boolean => {
|
||||
const myPreferences = get(preferences);
|
||||
|
||||
if (!myPreferences) {
|
||||
if (!authManager.authenticated) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { purchase } = myPreferences;
|
||||
|
||||
const now = DateTime.now();
|
||||
const hideUntilDate = DateTime.fromISO(purchase.hideBuyButtonUntil);
|
||||
const hideUntilDate = DateTime.fromISO(authManager.preferences.purchase.hideBuyButtonUntil);
|
||||
const dayLeft = Number(now.diff(hideUntilDate, 'days').days.toFixed(0));
|
||||
|
||||
return dayLeft > 0;
|
||||
};
|
||||
|
||||
export const setSupportBadgeVisibility = async (value: boolean) => {
|
||||
const response = await updateMyPreferences({
|
||||
userPreferencesUpdateDto: {
|
||||
purchase: {
|
||||
showSupportBadge: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
preferences.set(response);
|
||||
const response = await updateMyPreferences({ userPreferencesUpdateDto: { purchase: { showSupportBadge: value } } });
|
||||
authManager.setPreferences(response);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user