diff --git a/e2e/src/ui/mock-network/base-network.ts b/e2e/src/ui/mock-network/base-network.ts
index 0b361bd72..3c761ba37 100644
--- a/e2e/src/ui/mock-network/base-network.ts
+++ b/e2e/src/ui/mock-network/base-network.ts
@@ -1,5 +1,6 @@
import { BrowserContext } from '@playwright/test';
import { playwrightHost } from 'src/../playwright.config.js';
+import { adminConfig, publicConfig, userConfig } from 'src/ui/mock-network/config-fixtures.js';
export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserId: string) => {
await context.addCookies([
@@ -113,6 +114,27 @@ export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserI
},
});
});
+ await context.route('**/api/public/config', async (route) => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ json: publicConfig,
+ });
+ });
+ await context.route('**/api/config', async (route) => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ json: userConfig,
+ });
+ });
+ await context.route('**/api/admin/config', async (route) => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ json: adminConfig,
+ });
+ });
await context.route('**/api/server/features', async (route) => {
return route.fulfill({
status: 200,
diff --git a/e2e/src/ui/mock-network/config-fixtures.ts b/e2e/src/ui/mock-network/config-fixtures.ts
new file mode 100644
index 000000000..a9b2a42b5
--- /dev/null
+++ b/e2e/src/ui/mock-network/config-fixtures.ts
@@ -0,0 +1,171 @@
+export const publicConfig = {
+ ffmpeg: { realtime: { enabled: false } },
+ map: {
+ enabled: true,
+ lightStyle: 'https://tiles.immich.cloud/v1/style/light.json',
+ darkStyle: 'https://tiles.immich.cloud/v1/style/dark.json',
+ },
+ oauth: { autoLaunch: false, buttonText: 'Login with OAuth', enabled: false },
+ passkey: { enabled: true },
+ passwordLogin: { enabled: true },
+ server: { loginPageMessage: '' },
+};
+
+export const userConfig = {
+ ffmpeg: { realtime: { enabled: false, videoCodecs: ['h264', 'hevc'], resolutions: [480, 720, 1080] } },
+ machineLearning: {
+ enabled: false,
+ clip: { enabled: true },
+ duplicateDetection: { enabled: true },
+ facialRecognition: { enabled: true, minFaces: 3 },
+ ocr: { enabled: true },
+ },
+ map: {
+ enabled: true,
+ lightStyle: 'https://tiles.immich.cloud/v1/style/light.json',
+ darkStyle: 'https://tiles.immich.cloud/v1/style/dark.json',
+ },
+ reverseGeocoding: { enabled: true },
+ oauth: { autoLaunch: false, buttonText: 'Login with OAuth', enabled: false, accountManagementUrl: '' },
+ passkey: { enabled: true },
+ passwordLogin: { enabled: true },
+ image: { thumbnail: { size: 250 }, preview: { size: 1440 }, fullsize: { enabled: false } },
+ trash: { enabled: true, days: 30 },
+ notifications: { smtp: { enabled: false } },
+ server: { externalDomain: '', loginPageMessage: '', publicUsers: true },
+ user: { deleteDelay: 7 },
+};
+
+export const adminConfig = {
+ backup: { database: { enabled: true, cronExpression: '0 02 * * *', keepLastAmount: 14 } },
+ ffmpeg: {
+ crf: 23,
+ threads: 0,
+ preset: 'ultrafast',
+ targetVideoCodec: 'h264',
+ acceptedVideoCodecs: ['h264'],
+ targetAudioCodec: 'aac',
+ acceptedAudioCodecs: ['aac', 'mp3', 'opus'],
+ acceptedContainers: ['mov', 'ogg', 'webm'],
+ targetResolution: '720',
+ maxBitrate: '0',
+ bframes: -1,
+ refs: 0,
+ gopSize: 0,
+ temporalAQ: false,
+ cqMode: 'auto',
+ twoPass: false,
+ preferredHwDevice: 'auto',
+ transcode: 'required',
+ tonemap: 'hable',
+ accel: 'disabled',
+ accelDecode: true,
+ realtime: { enabled: false, videoCodecs: ['h264', 'hevc'], resolutions: [480, 720, 1080] },
+ },
+ integrityChecks: {
+ missingFiles: { enabled: true, cronExpression: '0 03 * * *' },
+ untrackedFiles: { enabled: true, cronExpression: '0 03 * * *' },
+ checksumFiles: { enabled: true, cronExpression: '0 03 * * *', timeLimit: 3_600_000, percentageLimit: 1 },
+ },
+ job: {
+ thumbnailGeneration: { concurrency: 3 },
+ metadataExtraction: { concurrency: 5 },
+ videoConversion: { concurrency: 1 },
+ faceDetection: { concurrency: 2 },
+ smartSearch: { concurrency: 2 },
+ backgroundTask: { concurrency: 5 },
+ migration: { concurrency: 5 },
+ search: { concurrency: 5 },
+ sidecar: { concurrency: 5 },
+ library: { concurrency: 5 },
+ notifications: { concurrency: 5 },
+ ocr: { concurrency: 1 },
+ workflow: { concurrency: 5 },
+ editor: { concurrency: 2 },
+ integrityCheck: { concurrency: 1 },
+ },
+ logging: { enabled: true, level: 'log' },
+ machineLearning: {
+ enabled: false,
+ urls: ['http://immich-machine-learning:3003'],
+ availabilityChecks: { enabled: true, timeout: 2000, interval: 30_000 },
+ clip: { enabled: true, modelName: 'ViT-B-32__openai' },
+ duplicateDetection: { enabled: true, maxDistance: 0.01 },
+ facialRecognition: { enabled: true, modelName: 'buffalo_l', minScore: 0.7, maxDistance: 0.5, minFaces: 3 },
+ ocr: {
+ enabled: true,
+ modelName: 'PP-OCRv5_mobile',
+ minDetectionScore: 0.5,
+ minRecognitionScore: 0.8,
+ maxResolution: 736,
+ },
+ },
+ map: {
+ enabled: true,
+ lightStyle: 'https://tiles.immich.cloud/v1/style/light.json',
+ darkStyle: 'https://tiles.immich.cloud/v1/style/dark.json',
+ },
+ reverseGeocoding: { enabled: true },
+ metadata: { faces: { import: false } },
+ oauth: {
+ autoLaunch: false,
+ autoRegister: true,
+ buttonText: 'Login with OAuth',
+ clientId: '',
+ clientSecret: '',
+ defaultStorageQuota: null,
+ enabled: false,
+ issuerUrl: '',
+ accountManagementUrl: '',
+ endSessionEndpoint: '',
+ mobileOverrideEnabled: false,
+ mobileRedirectUri: '',
+ prompt: '',
+ scope: 'openid email profile',
+ signingAlgorithm: 'RS256',
+ profileSigningAlgorithm: 'none',
+ storageLabelClaim: 'preferred_username',
+ storageQuotaClaim: 'immich_quota',
+ roleClaim: 'immich_role',
+ tokenEndpointAuthMethod: 'client_secret_post',
+ timeout: 30_000,
+ allowInsecureRequests: false,
+ },
+ passkey: { enabled: true, domain: null, additionalDomains: [] },
+ passwordLogin: { enabled: true },
+ storageTemplate: {
+ enabled: false,
+ hashVerificationEnabled: true,
+ template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
+ },
+ image: {
+ thumbnail: { format: 'webp', size: 250, quality: 80, progressive: false },
+ preview: { format: 'jpeg', size: 1440, quality: 80, progressive: false },
+ colorspace: 'p3',
+ extractEmbedded: false,
+ fullsize: { enabled: false, format: 'jpeg', quality: 80, progressive: false },
+ },
+ newVersionCheck: { enabled: true, channel: 'stable' },
+ nightlyTasks: {
+ startTime: '00:00',
+ databaseCleanup: true,
+ generateMemories: true,
+ syncQuotaUsage: true,
+ missingThumbnails: true,
+ clusterNewFaces: true,
+ },
+ trash: { enabled: true, days: 30 },
+ theme: { customCss: '' },
+ library: { scan: { enabled: true, cronExpression: '0 0 * * *' }, watch: { enabled: false } },
+ server: { externalDomain: '', loginPageMessage: '', publicUsers: true },
+ notifications: {
+ smtp: {
+ enabled: false,
+ from: '',
+ replyTo: '',
+ transport: { ignoreCert: false, host: '', port: 587, secure: false, username: '', password: '' },
+ },
+ },
+ templates: { email: { welcomeTemplate: '', albumInviteTemplate: '', albumUpdateTemplate: '' } },
+ user: { deleteDelay: 7 },
+};
diff --git a/web/src/lib/commands.ts b/web/src/lib/commands.ts
index 3945674e0..4001db81d 100644
--- a/web/src/lib/commands.ts
+++ b/web/src/lib/commands.ts
@@ -31,7 +31,8 @@ import type { MessageFormatter } from 'svelte-i18n';
import { goto } from '$app/navigation';
import { page } from '$app/state';
import { authManager } from '$lib/managers/auth-manager.svelte';
-import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
+import { readPreloadedQuery } from '$lib/queries/client';
+import { getUserConfigQuery } from '$lib/queries/config-user.query';
import { Route } from '$lib/route';
import { copyToClipboard } from '$lib/utils';
@@ -92,7 +93,7 @@ export const getPagesProvider = ($t: MessageFormatter) => {
title: $t('map'),
icon: mdiMapOutline,
onAction: () => goto(Route.map()),
- $if: () => authManager.authenticated && featureFlagsManager.value.map,
+ $if: () => authManager.authenticated && readPreloadedQuery(getUserConfigQuery()).map.enabled,
},
{
title: $t('people'),
@@ -173,7 +174,7 @@ export const getPagesProvider = ($t: MessageFormatter) => {
title: $t('trash'),
icon: mdiTrashCanOutline,
onAction: () => goto(Route.trash()),
- $if: () => authManager.authenticated && featureFlagsManager.value.trash,
+ $if: () => authManager.authenticated && readPreloadedQuery(getUserConfigQuery()).trash.enabled,
},
{
title: $t('admin.user_settings'),
diff --git a/web/src/lib/components/admin-settings/StorageTemplateSettings.svelte b/web/src/lib/components/admin-settings/StorageTemplateSettings.svelte
index 05657654c..98832ac25 100644
--- a/web/src/lib/components/admin-settings/StorageTemplateSettings.svelte
+++ b/web/src/lib/components/admin-settings/StorageTemplateSettings.svelte
@@ -7,7 +7,7 @@
import { authManager } from '$lib/managers/auth-manager.svelte';
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { createPreloadedQuery, executeMutation } from '$lib/queries/client';
- import { getConfigQuery, updateConfigMutation } from '$lib/queries/system-config.query';
+ import { getAdminConfigQuery, updateAdminConfigMutation } from '$lib/queries/config-admin.query';
import StorageTemplateVariablesModal from '$lib/modals/StorageTemplateVariablesModal.svelte';
import { Route } from '$lib/route';
import { getStorageTemplateOptions, type SystemConfigTemplateStorageOptionDto } from '@immich/sdk';
@@ -40,7 +40,7 @@
type StorageTemplateOption = (typeof StorageTemplateOption)[keyof typeof StorageTemplateOption];
const disabled = $derived(featureFlagsManager.value.configFile);
- const query = createPreloadedQuery(getConfigQuery);
+ const query = createPreloadedQuery(getAdminConfigQuery);
const config = $derived(query.data);
let configToEdit = $state(cloneDeep(config));
@@ -155,7 +155,7 @@
onDestroy(() => {
if (saveOnClose) {
- void executeMutation(updateConfigMutation, { storageTemplate: configToEdit.storageTemplate });
+ void executeMutation(updateAdminConfigMutation, { storageTemplate: configToEdit.storageTemplate });
}
});
diff --git a/web/src/lib/components/album-page/AlbumViewer.svelte b/web/src/lib/components/album-page/AlbumViewer.svelte
index 2f1b43aee..056854b5e 100644
--- a/web/src/lib/components/album-page/AlbumViewer.svelte
+++ b/web/src/lib/components/album-page/AlbumViewer.svelte
@@ -7,7 +7,6 @@
import Timeline from '$lib/components/timeline/Timeline.svelte';
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
- import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import { handleDownloadAlbum } from '$lib/services/album.service';
import { getGlobalActions } from '$lib/services/app.service';
@@ -23,6 +22,11 @@
import ControlAppBar from '../shared-components/ControlAppBar.svelte';
import ThemeButton from '../shared-components/ThemeButton.svelte';
import AlbumSummary from './AlbumSummary.svelte';
+ import { createPreloadedQuery } from '$lib/queries/client';
+ import { getPublicConfigQuery } from '$lib/queries/config-public.query';
+
+ const query = createPreloadedQuery(getPublicConfigQuery);
+ const publicConfig = $derived(query.data);
interface Props {
sharedLink: SharedLinkResponseDto;
@@ -144,7 +148,7 @@
icon={mdiDownload}
/>
{/if}
- {#if sharedLink.showMetadata && featureFlagsManager.value.map}
+ {#if sharedLink.showMetadata && publicConfig.map.enabled}
{/if}
diff --git a/web/src/lib/components/asset-viewer/AssetViewerNavBar.spec.ts b/web/src/lib/components/asset-viewer/AssetViewerNavBar.spec.ts
index 2c118f347..3d89971ab 100644
--- a/web/src/lib/components/asset-viewer/AssetViewerNavBar.spec.ts
+++ b/web/src/lib/components/asset-viewer/AssetViewerNavBar.spec.ts
@@ -1,6 +1,9 @@
+import type { UserConfigDto } from '@immich/sdk';
import '@testing-library/jest-dom';
import { getResizeObserverMock } from '$lib/__mocks__/resize-observer.mock';
import { authManager } from '$lib/managers/auth-manager.svelte';
+import { queryClient } from '$lib/queries/client';
+import { getUserConfigQuery } from '$lib/queries/config-user.query';
import { renderWithTooltips } from '$tests/helpers';
import { assetFactory } from '@test-data/factories/asset-factory';
import { preferencesFactory } from '@test-data/factories/preferences-factory';
@@ -60,6 +63,16 @@ describe('AssetViewerNavBar component', () => {
const user = userAdminFactory.build({ id: ownerId });
const asset = assetFactory.build({ ownerId, isTrashed: false });
authManager.setUser(user);
+ queryClient.setQueryData(getUserConfigQuery().queryKey, {
+ machineLearning: {
+ enabled: true,
+ clip: { enabled: true },
+ duplicateDetection: { enabled: true },
+ facialRecognition: { enabled: true },
+ ocr: { enabled: true },
+ },
+ trash: { enabled: true, days: 30 },
+ } as UserConfigDto);
const preferences = preferencesFactory.build({ cast: { gCastEnabled: false } });
authManager.setPreferences(preferences);
diff --git a/web/src/lib/components/asset-viewer/DetailPanel.svelte b/web/src/lib/components/asset-viewer/DetailPanel.svelte
index 47791a94a..9f210419a 100644
--- a/web/src/lib/components/asset-viewer/DetailPanel.svelte
+++ b/web/src/lib/components/asset-viewer/DetailPanel.svelte
@@ -8,7 +8,6 @@
import { timeToLoadTheMap } from '$lib/constants';
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
import { authManager } from '$lib/managers/auth-manager.svelte';
- import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { Route } from '$lib/route';
import { locale } from '$lib/stores/preferences.store';
import { getAssetMediaUrl } from '$lib/utils';
@@ -34,6 +33,11 @@
import AlbumListItemDetails from './AlbumListItemDetails.svelte';
import DetailPanelPeople from '$lib/components/asset-viewer/DetailPanelPeople.svelte';
import { faceManager } from '$lib/stores/face.svelte';
+ import { createPreloadedQuery } from '$lib/queries/client';
+ import { getPublicConfigQuery } from '$lib/queries/config-public.query';
+
+ const query = createPreloadedQuery(getPublicConfigQuery);
+ const publicConfig = $derived(query.data);
interface Props {
asset: AssetResponseDto;
@@ -278,7 +282,7 @@
- {#if latlng && featureFlagsManager.value.map}
+ {#if latlng && publicConfig.map.enabled}
{#await import('$lib/components/shared-components/map/Map.svelte')}
{#await delay(timeToLoadTheMap) then}
diff --git a/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte b/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte
index ce7486bc1..ad82ff6f0 100644
--- a/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte
+++ b/web/src/lib/components/asset-viewer/VideoNativeViewer.svelte
@@ -4,7 +4,6 @@
import { assetViewerFadeDuration } from '$lib/constants';
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
import { castManager } from '$lib/managers/cast-manager.svelte';
- import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { mediaCapabilitiesManager } from '$lib/managers/media-capabilities-manager.svelte';
import { autoPlayVideo, lang, loopVideo as loopVideoPreference } from '$lib/stores/preferences.store';
@@ -45,6 +44,11 @@
import { t } from 'svelte-i18n';
import { fade } from 'svelte/transition';
import './immich-time-range';
+ import { createPreloadedQuery } from '$lib/queries/client';
+ import { getPublicConfigQuery } from '$lib/queries/config-public.query';
+
+ const query = createPreloadedQuery(getPublicConfigQuery);
+ const publicConfig = $derived(query.data);
interface Props {
asset: AssetResponseDto;
@@ -78,7 +82,7 @@
let isLoading = $state(true);
let hasLoadedMetadata = $state(false);
let assetFileUrl = $derived.by(() => {
- if (featureFlagsManager.value.realtimeTranscoding) {
+ if (publicConfig.ffmpeg.realtime.enabled) {
return getAssetHlsUrl(assetId);
}
@@ -376,7 +380,7 @@
style:aspect-ratio={aspectRatio}
defaultduration={asset.duration! / 1000}
>
- {#if featureFlagsManager.value.realtimeTranscoding}
+ {#if publicConfig.ffmpeg.realtime.enabled}
{$t('media_chrome.playback_rate')}
- {#if featureFlagsManager.value.realtimeTranscoding}
+ {#if publicConfig.ffmpeg.realtime.enabled}
{$t('video_quality')}
diff --git a/web/src/lib/components/shared-components/map/Map.svelte b/web/src/lib/components/shared-components/map/Map.svelte
index a3fda214c..dc69366f8 100644
--- a/web/src/lib/components/shared-components/map/Map.svelte
+++ b/web/src/lib/components/shared-components/map/Map.svelte
@@ -12,7 +12,6 @@
import { afterNavigate } from '$app/navigation';
import OnEvents from '$lib/components/OnEvents.svelte';
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
- import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
import MapSettingsModal from '$lib/modals/MapSettingsModal.svelte';
import { mapSettings } from '$lib/stores/preferences.store';
import { getAssetMediaUrl, handlePromiseError } from '$lib/utils';
@@ -48,6 +47,11 @@
ScaleControl,
} from 'svelte-maplibre';
import type { SelectionBBox } from './types';
+ import { createPreloadedQuery } from '$lib/queries/client';
+ import { getPublicConfigQuery } from '$lib/queries/config-public.query';
+
+ const query = createPreloadedQuery(getPublicConfigQuery);
+ const publicConfig = $derived(query.data);
interface Props {
mapMarkers?: MapMarkerResponseDto[];
@@ -111,9 +115,7 @@
let abortController: AbortController;
const mapTheme = $derived($mapSettings.allowDarkMode ? themeManager.value : Theme.Light);
- const styleUrl = $derived(
- mapTheme === Theme.Dark ? serverConfigManager.value.mapDarkStyleUrl : serverConfigManager.value.mapLightStyleUrl,
- );
+ const styleUrl = $derived(mapTheme === Theme.Dark ? publicConfig.map.darkStyle : publicConfig.map.lightStyle);
export function addClipMapMarker(lng: number, lat: number) {
if (!map) {
diff --git a/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte b/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte
index 22c87d9a3..8166f4a4b 100644
--- a/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte
+++ b/web/src/lib/components/shared-components/search-bar/SearchTextSection.svelte
@@ -1,10 +1,14 @@
@@ -44,7 +48,7 @@
- {#if featureFlagsManager.value.map}
+ {#if config.map.enabled}
{/if}
@@ -111,7 +115,7 @@
- {#if featureFlagsManager.value.trash}
+ {#if config.trash.enabled}
{/if}
diff --git a/web/src/lib/managers/auth-manager.svelte.ts b/web/src/lib/managers/auth-manager.svelte.ts
index 5e10d8b11..e53d5d909 100644
--- a/web/src/lib/managers/auth-manager.svelte.ts
+++ b/web/src/lib/managers/auth-manager.svelte.ts
@@ -10,6 +10,8 @@ import { goto } from '$app/navigation';
import { page } from '$app/state';
import { eventManager } from '$lib/managers/event-manager.svelte';
import { queryClient } from '$lib/queries/client';
+import { getAdminConfigQuery } from '$lib/queries/config-admin.query';
+import { getUserConfigQuery } from '$lib/queries/config-user.query';
import { getAboutInfoQuery } from '$lib/queries/server.query';
import { Route } from '$lib/route';
import { isSharedLinkRoute } from '$lib/utils/navigation';
@@ -62,7 +64,14 @@ class AuthManager {
async refresh() {
try {
- const [user, preferences] = await Promise.all([getMyUser(), getMyPreferences()]);
+ const [user, preferences] = await Promise.all([
+ getMyUser(),
+ getMyPreferences(),
+ queryClient.query(getUserConfigQuery()),
+ ]);
+ if (user.isAdmin) {
+ await queryClient.query(getAdminConfigQuery());
+ }
this.#preferences = preferences;
this.#user = user;
diff --git a/web/src/lib/managers/server-config-manager.svelte.ts b/web/src/lib/managers/server-config-manager.svelte.ts
deleted file mode 100644
index 45f54122e..000000000
--- a/web/src/lib/managers/server-config-manager.svelte.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { getServerConfig, type ServerConfigDto } from '@immich/sdk';
-import { eventManager } from '$lib/managers/event-manager.svelte';
-
-class ServerConfigManager {
- #value?: ServerConfigDto = $state();
-
- constructor() {
- eventManager.on({
- SystemConfigUpdate: () => this.loadServerConfig(),
- });
- }
-
- async init() {
- await this.loadServerConfig();
- }
-
- get value() {
- if (!this.#value) {
- throw new Error('Server config manager must be initialized first');
- }
-
- return this.#value;
- }
-
- async loadServerConfig() {
- this.#value = await getServerConfig();
- }
-}
-
-export const serverConfigManager = new ServerConfigManager();
diff --git a/web/src/lib/modals/PinCodeResetModal.svelte b/web/src/lib/modals/PinCodeResetModal.svelte
index a51e0c358..6ea7b98f2 100644
--- a/web/src/lib/modals/PinCodeResetModal.svelte
+++ b/web/src/lib/modals/PinCodeResetModal.svelte
@@ -1,9 +1,13 @@
-{#if featureFlagsManager.value.passwordLogin}
+{#if config.passwordLogin.enabled}
{$t('reset_pin_code_description')}
diff --git a/web/src/lib/modals/UserDeleteConfirmModal.svelte b/web/src/lib/modals/UserDeleteConfirmModal.svelte
index 03818b550..fe7cd6255 100644
--- a/web/src/lib/modals/UserDeleteConfirmModal.svelte
+++ b/web/src/lib/modals/UserDeleteConfirmModal.svelte
@@ -1,12 +1,15 @@
-{#if featureFlagsManager.value.map}
+{#if config.map.enabled}
;
let searchQuery = $derived(page.url.searchParams.get(QueryParameter.QUERY));
- let smartSearchEnabled = $derived(featureFlagsManager.value.smartSearch);
let terms = $derived
(searchQuery ? JSON.parse(searchQuery) : {});
let searchTermKeys = $derived(getObjectKeys(terms));
@@ -139,7 +142,7 @@
try {
const { albums, assets } =
- ('query' in searchDto || 'queryAssetId' in searchDto) && smartSearchEnabled
+ ('query' in searchDto || 'queryAssetId' in searchDto) && config.machineLearning.clip.enabled
? await searchSmart({
smartSearchDto: { visibility: AssetVisibility.Timeline, ...searchDto, language: $lang },
})
diff --git a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte
index 46e8b46be..95bed86a3 100644
--- a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -9,14 +9,17 @@
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
import Timeline from '$lib/components/timeline/Timeline.svelte';
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
- import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
- import { serverConfigManager } from '$lib/managers/server-config-manager.svelte';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import { Route } from '$lib/route';
import { getTrashActions } from '$lib/services/trash.service';
import { handlePromiseError } from '$lib/utils';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
+ import { createPreloadedQuery } from '$lib/queries/client';
+ import { getUserConfigQuery } from '$lib/queries/config-user.query';
+
+ const query = createPreloadedQuery(getUserConfigQuery);
+ const config = $derived(query.data);
type Props = {
data: PageData;
@@ -27,7 +30,7 @@
let timelineManager = $state() as TimelineManager;
const options = { isTrashed: true };
- if (!featureFlagsManager.value.trash) {
+ if (!config.trash.enabled) {
handlePromiseError(goto(Route.photos()));
}
@@ -43,7 +46,7 @@
const { Empty, RestoreAll } = $derived(getTrashActions($t, timelineManager?.assetCount ?? 0));
-{#if featureFlagsManager.value.trash}
+{#if config.trash.enabled}
{$t('trashed_items_will_be_permanently_deleted_after', {
- values: { days: serverConfigManager.value.trashDays },
+ values: { days: config.trash.days },
})}
{#snippet empty()}
diff --git a/web/src/routes/(user)/user-settings/FeatureSettings.svelte b/web/src/routes/(user)/user-settings/FeatureSettings.svelte
index 640a2425a..133066cdf 100644
--- a/web/src/routes/(user)/user-settings/FeatureSettings.svelte
+++ b/web/src/routes/(user)/user-settings/FeatureSettings.svelte
@@ -1,5 +1,4 @@
diff --git a/web/src/routes/admin/system-settings/+page.ts b/web/src/routes/admin/system-settings/+page.ts
index 32d7f4298..a2d62e66e 100644
--- a/web/src/routes/admin/system-settings/+page.ts
+++ b/web/src/routes/admin/system-settings/+page.ts
@@ -1,12 +1,12 @@
import { queryClient } from '$lib/queries/client';
-import { getConfigDefaultsQuery, getConfigQuery } from '$lib/queries/system-config.query';
+import { getAdminConfigDefaultsQuery } from '$lib/queries/config-admin.query';
import { authenticate } from '$lib/utils/auth';
import { getFormatter } from '$lib/utils/i18n';
import type { PageLoad } from './$types';
export const load = (async ({ url }) => {
await authenticate(url, { admin: true });
- await Promise.all([queryClient.query(getConfigQuery()), queryClient.query(getConfigDefaultsQuery())]);
+ await queryClient.query(getAdminConfigDefaultsQuery());
const $t = await getFormatter();
return {
diff --git a/web/src/routes/admin/system-settings/AuthSettings.svelte b/web/src/routes/admin/system-settings/AuthSettings.svelte
index 086b96747..2a9b34424 100644
--- a/web/src/routes/admin/system-settings/AuthSettings.svelte
+++ b/web/src/routes/admin/system-settings/AuthSettings.svelte
@@ -8,7 +8,7 @@
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import AuthDisableLoginConfirmModal from '$lib/modals/AuthDisableLoginConfirmModal.svelte';
import { createPreloadedQuery } from '$lib/queries/client';
- import { getConfigQuery } from '$lib/queries/system-config.query';
+ import { getAdminConfigQuery } from '$lib/queries/config-admin.query';
import { handleError } from '$lib/utils/handle-error';
import { OAuthTokenEndpointAuthMethod, unlinkAllOAuthAccountsAdmin } from '@immich/sdk';
import { Button, IconButton, Link, modalManager, Text, toastManager } from '@immich/ui';
@@ -18,7 +18,7 @@
import { fade } from 'svelte/transition';
import SettingSelect from './SettingSelect.svelte';
- const query = createPreloadedQuery(getConfigQuery);
+ const query = createPreloadedQuery(getAdminConfigQuery);
const config = $derived(query.data);
const disabled = $derived(featureFlagsManager.value.configFile);
diff --git a/web/src/routes/admin/system-settings/BackupSettings.svelte b/web/src/routes/admin/system-settings/BackupSettings.svelte
index b9da87d6d..6dcb1c2ae 100644
--- a/web/src/routes/admin/system-settings/BackupSettings.svelte
+++ b/web/src/routes/admin/system-settings/BackupSettings.svelte
@@ -1,6 +1,6 @@
diff --git a/web/src/routes/auth/onboarding/OnboardingHello.svelte b/web/src/routes/auth/onboarding/OnboardingHello.svelte
index 1b5b0d2e8..deadf8bc0 100644
--- a/web/src/routes/auth/onboarding/OnboardingHello.svelte
+++ b/web/src/routes/auth/onboarding/OnboardingHello.svelte
@@ -1,12 +1,16 @@
diff --git a/web/src/routes/auth/onboarding/OnboardingServerPrivacy.svelte b/web/src/routes/auth/onboarding/OnboardingServerPrivacy.svelte
index 46be0b922..e96b1c8f3 100644
--- a/web/src/routes/auth/onboarding/OnboardingServerPrivacy.svelte
+++ b/web/src/routes/auth/onboarding/OnboardingServerPrivacy.svelte
@@ -1,16 +1,16 @@