From a05c990718025ea72a2963bae25c68608f1821ac Mon Sep 17 00:00:00 2001 From: Jason Rasmussen <jrasm91@gmail.com> Date: Mon, 13 May 2024 16:40:33 -0400 Subject: [PATCH] feat(web): combine auth settings (#9427) --- .../settings/auth/auth-settings.svelte | 242 ++++++++++++++++++ .../settings/confirm-disable-login.svelte | 25 -- .../settings/oauth/oauth-settings.svelte | 213 --------------- .../password-login-settings.svelte | 68 ----- .../routes/admin/system-settings/+page.svelte | 36 +-- 5 files changed, 256 insertions(+), 328 deletions(-) create mode 100644 web/src/lib/components/admin-page/settings/auth/auth-settings.svelte delete mode 100644 web/src/lib/components/admin-page/settings/confirm-disable-login.svelte delete mode 100644 web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte delete mode 100644 web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte new file mode 100644 index 0000000000..d9c879faff --- /dev/null +++ b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte @@ -0,0 +1,242 @@ +<script lang="ts"> + import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; + import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; + import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; + import SettingInputField, { + SettingInputFieldType, + } from '$lib/components/shared-components/settings/setting-input-field.svelte'; + import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; + import { type SystemConfigDto } from '@immich/sdk'; + import { isEqual } from 'lodash-es'; + import { createEventDispatcher } from 'svelte'; + import { fade } from 'svelte/transition'; + import type { SettingsEventType } from '../admin-settings'; + + export let savedConfig: SystemConfigDto; + export let defaultConfig: SystemConfigDto; + export let config: SystemConfigDto; // this is the config that is being edited + export let disabled = false; + + const dispatch = createEventDispatcher<SettingsEventType>(); + + let isConfirmOpen = false; + + const handleToggleOverride = () => { + // click runs before bind + const previouslyEnabled = config.oauth.mobileOverrideEnabled; + if (!previouslyEnabled && !config.oauth.mobileRedirectUri) { + config.oauth.mobileRedirectUri = window.location.origin + '/api/oauth/mobile-redirect'; + } + }; + + const handleSave = (skipConfirm: boolean) => { + const allMethodsDisabled = !config.oauth.enabled && !config.passwordLogin.enabled; + if (allMethodsDisabled && !skipConfirm) { + isConfirmOpen = true; + return; + } + + isConfirmOpen = false; + dispatch('save', { passwordLogin: config.passwordLogin, oauth: config.oauth }); + }; +</script> + +{#if isConfirmOpen} + <ConfirmDialogue + id="disable-login-modal" + title="Disable login" + onClose={() => (isConfirmOpen = false)} + onConfirm={() => handleSave(true)} + > + <svelte:fragment slot="prompt"> + <div class="flex flex-col gap-4"> + <p>Are you sure you want to disable all login methods? Login will be completely disabled.</p> + <p> + To re-enable, use a + <a + href="https://immich.app/docs/administration/server-commands" + rel="noreferrer" + target="_blank" + class="underline" + > + Server Command</a + >. + </p> + </div> + </svelte:fragment> + </ConfirmDialogue> +{/if} + +<div> + <div in:fade={{ duration: 500 }}> + <form autocomplete="off" on:submit|preventDefault> + <div class="ml-4 mt-4 flex flex-col gap-4"> + <SettingAccordion key="oauth" title="OAuth" subtitle="Manage OAuth login settings"> + <div class="ml-4 mt-4 flex flex-col gap-4"> + <p class="text-sm dark:text-immich-dark-fg"> + For more details about this feature, refer to the <a + href="https://immich.app/docs/administration/oauth" + class="underline" + target="_blank" + rel="noreferrer">docs</a + >. + </p> + + <SettingSwitch + id="login-with-oauth" + {disabled} + title="ENABLE" + subtitle="Login with OAuth" + bind:checked={config.oauth.enabled} + /> + + {#if config.oauth.enabled} + <hr /> + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="ISSUER URL" + bind:value={config.oauth.issuerUrl} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.issuerUrl == savedConfig.oauth.issuerUrl)} + /> + + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="CLIENT ID" + bind:value={config.oauth.clientId} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.clientId == savedConfig.oauth.clientId)} + /> + + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="CLIENT SECRET" + bind:value={config.oauth.clientSecret} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.clientSecret == savedConfig.oauth.clientSecret)} + /> + + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="SCOPE" + bind:value={config.oauth.scope} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.scope == savedConfig.oauth.scope)} + /> + + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="SIGNING ALGORITHM" + bind:value={config.oauth.signingAlgorithm} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.signingAlgorithm == savedConfig.oauth.signingAlgorithm)} + /> + + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="STORAGE LABEL CLAIM" + desc="Automatically set the user's storage label to the value of this claim." + bind:value={config.oauth.storageLabelClaim} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.storageLabelClaim == savedConfig.oauth.storageLabelClaim)} + /> + + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="STORAGE QUOTA CLAIM" + desc="Automatically set the user's storage quota to the value of this claim." + bind:value={config.oauth.storageQuotaClaim} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.storageQuotaClaim == savedConfig.oauth.storageQuotaClaim)} + /> + + <SettingInputField + inputType={SettingInputFieldType.NUMBER} + label="DEFAULT STORAGE QUOTA (GiB)" + desc="Quota in GiB to be used when no claim is provided (Enter 0 for unlimited quota)." + bind:value={config.oauth.defaultStorageQuota} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.defaultStorageQuota == savedConfig.oauth.defaultStorageQuota)} + /> + + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="BUTTON TEXT" + bind:value={config.oauth.buttonText} + required={false} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.buttonText == savedConfig.oauth.buttonText)} + /> + + <SettingSwitch + id="auto-register-new-users" + title="AUTO REGISTER" + subtitle="Automatically register new users after signing in with OAuth" + bind:checked={config.oauth.autoRegister} + disabled={disabled || !config.oauth.enabled} + /> + + <SettingSwitch + id="auto-launch-oauth" + title="AUTO LAUNCH" + subtitle="Start the OAuth login flow automatically upon navigating to the login page" + disabled={disabled || !config.oauth.enabled} + bind:checked={config.oauth.autoLaunch} + /> + + <SettingSwitch + id="mobile-redirect-uri-override" + title="MOBILE REDIRECT URI OVERRIDE" + subtitle="Enable when 'app.immich:/' is an invalid redirect URI." + disabled={disabled || !config.oauth.enabled} + on:click={() => handleToggleOverride()} + bind:checked={config.oauth.mobileOverrideEnabled} + /> + + {#if config.oauth.mobileOverrideEnabled} + <SettingInputField + inputType={SettingInputFieldType.TEXT} + label="MOBILE REDIRECT URI" + bind:value={config.oauth.mobileRedirectUri} + required={true} + disabled={disabled || !config.oauth.enabled} + isEdited={!(config.oauth.mobileRedirectUri == savedConfig.oauth.mobileRedirectUri)} + /> + {/if} + {/if} + </div> + </SettingAccordion> + + <SettingAccordion key="password" title="Password" subtitle="Manage password login settings"> + <div class="ml-4 mt-4 flex flex-col gap-4"> + <div class="ml-4 mt-4 flex flex-col"> + <SettingSwitch + id="enable-password-login" + title="ENABLED" + {disabled} + subtitle="Login with email and password" + bind:checked={config.passwordLogin.enabled} + /> + </div> + </div> + </SettingAccordion> + + <SettingButtonsRow + showResetToDefault={!isEqual(savedConfig.passwordLogin, defaultConfig.passwordLogin) || + !isEqual(savedConfig.oauth, defaultConfig.oauth)} + {disabled} + on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['passwordLogin', 'oauth'] })} + on:save={() => handleSave(false)} + /> + </div> + </form> + </div> +</div> diff --git a/web/src/lib/components/admin-page/settings/confirm-disable-login.svelte b/web/src/lib/components/admin-page/settings/confirm-disable-login.svelte deleted file mode 100644 index 621f51de9f..0000000000 --- a/web/src/lib/components/admin-page/settings/confirm-disable-login.svelte +++ /dev/null @@ -1,25 +0,0 @@ -<script lang="ts"> - import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; - - export let onCancel: () => void; - export let onConfirm: () => void; -</script> - -<ConfirmDialogue id="disable-login-modal" title="Disable login" onClose={onCancel} {onConfirm}> - <svelte:fragment slot="prompt"> - <div class="flex flex-col gap-4"> - <p>Are you sure you want to disable all login methods? Login will be completely disabled.</p> - <p> - To re-enable, use a - <a - href="https://immich.app/docs/administration/server-commands" - rel="noreferrer" - target="_blank" - class="underline" - > - Server Command</a - >. - </p> - </div> - </svelte:fragment> -</ConfirmDialogue> diff --git a/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte b/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte deleted file mode 100644 index 8173c353eb..0000000000 --- a/web/src/lib/components/admin-page/settings/oauth/oauth-settings.svelte +++ /dev/null @@ -1,213 +0,0 @@ -<script lang="ts"> - import type { SystemConfigDto } from '@immich/sdk'; - import { isEqual } from 'lodash-es'; - import { createEventDispatcher } from 'svelte'; - import { fade } from 'svelte/transition'; - import type { SettingsEventType } from '../admin-settings'; - import ConfirmDisableLogin from '../confirm-disable-login.svelte'; - import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; - import SettingInputField, { - SettingInputFieldType, - } from '$lib/components/shared-components/settings/setting-input-field.svelte'; - import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; - - export let savedConfig: SystemConfigDto; - export let defaultConfig: SystemConfigDto; - export let config: SystemConfigDto; // this is the config that is being edited - export let disabled = false; - - const dispatch = createEventDispatcher<SettingsEventType>(); - - const handleToggleOverride = () => { - // click runs before bind - const previouslyEnabled = config.oauth.mobileOverrideEnabled; - if (!previouslyEnabled && !config.oauth.mobileRedirectUri) { - config.oauth.mobileRedirectUri = window.location.origin + '/api/oauth/mobile-redirect'; - } - }; - - let isConfirmOpen = false; - let handleConfirm: (value: boolean) => void; - - const openConfirmModal = () => { - return new Promise((resolve) => { - handleConfirm = (value: boolean) => { - isConfirmOpen = false; - resolve(value); - }; - isConfirmOpen = true; - }); - }; - - const handleSave = async () => { - if (!savedConfig.passwordLogin.enabled && savedConfig.oauth.enabled && !config.oauth.enabled) { - const confirmed = await openConfirmModal(); - if (!confirmed) { - return; - } - } - - if (!config.oauth.mobileOverrideEnabled) { - config.oauth.mobileRedirectUri = ''; - } - - dispatch('save', { oauth: config.oauth }); - }; -</script> - -{#if isConfirmOpen} - <ConfirmDisableLogin onCancel={() => handleConfirm(false)} onConfirm={() => handleConfirm(true)} /> -{/if} - -<div class="mt-2"> - <div in:fade={{ duration: 500 }}> - <form autocomplete="off" on:submit|preventDefault class="mx-4 flex flex-col gap-4 py-4"> - <p class="text-sm dark:text-immich-dark-fg"> - For more details about this feature, refer to the <a - href="https://immich.app/docs/administration/oauth" - class="underline" - target="_blank" - rel="noreferrer">docs</a - >. - </p> - - <SettingSwitch - id="login-with-oauth" - {disabled} - title="ENABLE" - subtitle="Login with OAuth" - bind:checked={config.oauth.enabled} - /> - - {#if config.oauth.enabled} - <hr /> - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="ISSUER URL" - bind:value={config.oauth.issuerUrl} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.issuerUrl == savedConfig.oauth.issuerUrl)} - /> - - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="CLIENT ID" - bind:value={config.oauth.clientId} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.clientId == savedConfig.oauth.clientId)} - /> - - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="CLIENT SECRET" - bind:value={config.oauth.clientSecret} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.clientSecret == savedConfig.oauth.clientSecret)} - /> - - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="SCOPE" - bind:value={config.oauth.scope} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.scope == savedConfig.oauth.scope)} - /> - - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="SIGNING ALGORITHM" - bind:value={config.oauth.signingAlgorithm} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.signingAlgorithm == savedConfig.oauth.signingAlgorithm)} - /> - - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="STORAGE LABEL CLAIM" - desc="Automatically set the user's storage label to the value of this claim." - bind:value={config.oauth.storageLabelClaim} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.storageLabelClaim == savedConfig.oauth.storageLabelClaim)} - /> - - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="STORAGE QUOTA CLAIM" - desc="Automatically set the user's storage quota to the value of this claim." - bind:value={config.oauth.storageQuotaClaim} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.storageQuotaClaim == savedConfig.oauth.storageQuotaClaim)} - /> - - <SettingInputField - inputType={SettingInputFieldType.NUMBER} - label="DEFAULT STORAGE QUOTA (GiB)" - desc="Quota in GiB to be used when no claim is provided (Enter 0 for unlimited quota)." - bind:value={config.oauth.defaultStorageQuota} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.defaultStorageQuota == savedConfig.oauth.defaultStorageQuota)} - /> - - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="BUTTON TEXT" - bind:value={config.oauth.buttonText} - required={false} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.buttonText == savedConfig.oauth.buttonText)} - /> - - <SettingSwitch - id="auto-register-new-users" - title="AUTO REGISTER" - subtitle="Automatically register new users after signing in with OAuth" - bind:checked={config.oauth.autoRegister} - disabled={disabled || !config.oauth.enabled} - /> - - <SettingSwitch - id="auto-launch-oauth" - title="AUTO LAUNCH" - subtitle="Start the OAuth login flow automatically upon navigating to the login page" - disabled={disabled || !config.oauth.enabled} - bind:checked={config.oauth.autoLaunch} - /> - - <SettingSwitch - id="mobile-redirect-uri-override" - title="MOBILE REDIRECT URI OVERRIDE" - subtitle="Enable when 'app.immich:/' is an invalid redirect URI." - disabled={disabled || !config.oauth.enabled} - on:click={() => handleToggleOverride()} - bind:checked={config.oauth.mobileOverrideEnabled} - /> - - {#if config.oauth.mobileOverrideEnabled} - <SettingInputField - inputType={SettingInputFieldType.TEXT} - label="MOBILE REDIRECT URI" - bind:value={config.oauth.mobileRedirectUri} - required={true} - disabled={disabled || !config.oauth.enabled} - isEdited={!(config.oauth.mobileRedirectUri == savedConfig.oauth.mobileRedirectUri)} - /> - {/if} - {/if} - - <SettingButtonsRow - on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['oauth'] })} - on:save={() => handleSave()} - showResetToDefault={!isEqual(savedConfig.oauth, defaultConfig.oauth)} - {disabled} - /> - </form> - </div> -</div> diff --git a/web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte b/web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte deleted file mode 100644 index a022583527..0000000000 --- a/web/src/lib/components/admin-page/settings/password-login/password-login-settings.svelte +++ /dev/null @@ -1,68 +0,0 @@ -<script lang="ts"> - import type { SystemConfigDto } from '@immich/sdk'; - import { isEqual } from 'lodash-es'; - import { createEventDispatcher } from 'svelte'; - import { fade } from 'svelte/transition'; - import type { SettingsEventType } from '../admin-settings'; - import ConfirmDisableLogin from '../confirm-disable-login.svelte'; - import SettingButtonsRow from '$lib/components/shared-components/settings/setting-buttons-row.svelte'; - import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; - - export let savedConfig: SystemConfigDto; - export let defaultConfig: SystemConfigDto; - export let config: SystemConfigDto; // this is the config that is being edited - export let disabled = false; - - const dispatch = createEventDispatcher<SettingsEventType>(); - - let isConfirmOpen = false; - let handleConfirm: (value: boolean) => void; - - const openConfirmModal = () => { - return new Promise((resolve) => { - handleConfirm = (value: boolean) => { - isConfirmOpen = false; - resolve(value); - }; - isConfirmOpen = true; - }); - }; - - async function handleSave() { - if (!savedConfig.oauth.enabled && savedConfig.passwordLogin.enabled && !config.passwordLogin.enabled) { - const confirmed = await openConfirmModal(); - if (!confirmed) { - return; - } - } - - dispatch('save', { passwordLogin: config.passwordLogin }); - } -</script> - -{#if isConfirmOpen} - <ConfirmDisableLogin onCancel={() => handleConfirm(false)} onConfirm={() => handleConfirm(true)} /> -{/if} - -<div> - <div in:fade={{ duration: 500 }}> - <form autocomplete="off" on:submit|preventDefault> - <div class="ml-4 mt-4 flex flex-col"> - <SettingSwitch - id="enable-password-login" - title="ENABLED" - {disabled} - subtitle="Login with email and password" - bind:checked={config.passwordLogin.enabled} - /> - - <SettingButtonsRow - on:reset={({ detail }) => dispatch('reset', { ...detail, configKeys: ['passwordLogin'] })} - on:save={() => handleSave()} - showResetToDefault={!isEqual(savedConfig.passwordLogin, defaultConfig.passwordLogin)} - {disabled} - /> - </div> - </form> - </div> -</div> diff --git a/web/src/routes/admin/system-settings/+page.svelte b/web/src/routes/admin/system-settings/+page.svelte index c57a6b1697..20a9557c7f 100644 --- a/web/src/routes/admin/system-settings/+page.svelte +++ b/web/src/routes/admin/system-settings/+page.svelte @@ -1,34 +1,33 @@ <script lang="ts"> import AdminSettings from '$lib/components/admin-page/settings/admin-settings.svelte'; + import AuthSettings from '$lib/components/admin-page/settings/auth/auth-settings.svelte'; import FFmpegSettings from '$lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte'; + import ImageSettings from '$lib/components/admin-page/settings/image/image-settings.svelte'; import JobSettings from '$lib/components/admin-page/settings/job-settings/job-settings.svelte'; import LibrarySettings from '$lib/components/admin-page/settings/library-settings/library-settings.svelte'; import LoggingSettings from '$lib/components/admin-page/settings/logging-settings/logging-settings.svelte'; import MachineLearningSettings from '$lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte'; import MapSettings from '$lib/components/admin-page/settings/map-settings/map-settings.svelte'; import NewVersionCheckSettings from '$lib/components/admin-page/settings/new-version-check-settings/new-version-check-settings.svelte'; - import OAuthSettings from '$lib/components/admin-page/settings/oauth/oauth-settings.svelte'; - import PasswordLoginSettings from '$lib/components/admin-page/settings/password-login/password-login-settings.svelte'; - import ServerSettings from '$lib/components/admin-page/settings/server/server-settings.svelte'; import NotificationSettings from '$lib/components/admin-page/settings/notification-settings/notification-settings.svelte'; - import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; + import ServerSettings from '$lib/components/admin-page/settings/server/server-settings.svelte'; import StorageTemplateSettings from '$lib/components/admin-page/settings/storage-template/storage-template-settings.svelte'; import ThemeSettings from '$lib/components/admin-page/settings/theme/theme-settings.svelte'; - import ImageSettings from '$lib/components/admin-page/settings/image/image-settings.svelte'; import TrashSettings from '$lib/components/admin-page/settings/trash-settings/trash-settings.svelte'; import UserSettings from '$lib/components/admin-page/settings/user-settings/user-settings.svelte'; import LinkButton from '$lib/components/elements/buttons/link-button.svelte'; import Icon from '$lib/components/elements/icon.svelte'; import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; + import SettingAccordionState from '$lib/components/shared-components/settings/setting-accordion-state.svelte'; + import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; + import { QueryParameter } from '$lib/constants'; import { downloadManager } from '$lib/stores/download'; import { featureFlags } from '$lib/stores/server-config.store'; import { copyToClipboard } from '$lib/utils'; import { downloadBlob } from '$lib/utils/asset-utils'; + import type { SystemConfigDto } from '@immich/sdk'; import { mdiAlert, mdiContentCopy, mdiDownload, mdiUpload } from '@mdi/js'; import type { PageData } from './$types'; - import SettingAccordionState from '$lib/components/shared-components/settings/setting-accordion-state.svelte'; - import { QueryParameter } from '$lib/constants'; - import type { SystemConfigDto } from '@immich/sdk'; export let data: PageData; @@ -36,13 +35,12 @@ let handleSave: (update: Partial<SystemConfigDto>) => Promise<void>; type Settings = + | typeof AuthSettings | typeof JobSettings | typeof LibrarySettings | typeof LoggingSettings | typeof MachineLearningSettings | typeof MapSettings - | typeof OAuthSettings - | typeof PasswordLoginSettings | typeof ServerSettings | typeof StorageTemplateSettings | typeof ThemeSettings @@ -82,6 +80,12 @@ subtitle: string; key: string; }> = [ + { + item: AuthSettings, + title: 'Authentication Settings', + subtitle: 'Manage password, OAuth, and other authentication settings', + key: 'image', + }, { item: ImageSettings, title: 'Image Settings', @@ -124,18 +128,6 @@ subtitle: 'Manage notification settings, including email', key: 'notifications', }, - { - item: OAuthSettings, - title: 'OAuth Authentication', - subtitle: 'Manage the login with OAuth settings', - key: 'oauth', - }, - { - item: PasswordLoginSettings, - title: 'Password Authentication', - subtitle: 'Manage the login with password settings', - key: 'password', - }, { item: ServerSettings, title: 'Server Settings',