diff --git a/packages/builder/src/pages/builder/portal/settings/branding.svelte b/packages/builder/src/pages/builder/portal/settings/branding.svelte index ae22d310a1..ed858c2228 100644 --- a/packages/builder/src/pages/builder/portal/settings/branding.svelte +++ b/packages/builder/src/pages/builder/portal/settings/branding.svelte @@ -45,13 +45,13 @@ let updated = false $: onConfigUpdate(config, mounted) - $: init = Object.keys(config).length > 0 + $: initialised = Object.keys(config).length > 0 $: isCloud = $admin.cloud $: brandingEnabled = $licensing.brandingEnabled const onConfigUpdate = () => { - if (!mounted || updated || !init) { + if (!mounted || updated || !initialised) { return } updated = true @@ -122,34 +122,31 @@ return response } - async function saveConfig() { - saving = true - + async function saveFiles() { if (logoFile) { const logoResp = await uploadLogo(logoFile) if (logoResp.url) { - config = { - ...config, - logoUrl: logoResp.url, - } logoFile = null logoPreview = null } + config.logoUrl = undefined + } else { + config.logoUrl = "" } if (faviconFile) { const faviconResp = await uploadFavicon(faviconFile) if (faviconResp.url) { - config = { - ...config, - faviconUrl: faviconResp.url, - } faviconFile = null faviconPreview = null } + config.faviconUrl = undefined + } else { + config.faviconUrl = "" } + } - // Trim + function trimFields() { const userStrings = [ "metaTitle", "platformTitle", @@ -168,11 +165,18 @@ ...config, ...trimmed, } + } + + async function saveConfig() { + saving = true + + await saveFiles() + trimFields() try { // Update settings await organisation.save(config) - await organisation.init() + await init() notifications.success("Branding settings updated") } catch (e) { console.error("Branding updated failed", e) @@ -182,8 +186,10 @@ saving = false } - onMount(async () => { - await organisation.init() + async function init() { + if (!$organisation.loaded) { + await organisation.init() + } config = { faviconUrl: $organisation.faviconUrl, @@ -197,6 +203,10 @@ metaImageUrl: $organisation.metaImageUrl, metaTitle: $organisation.metaTitle, } + } + + onMount(async () => { + await init() mounted = true }) diff --git a/packages/builder/src/stores/portal/organisation.js b/packages/builder/src/stores/portal/organisation.js index ed7dd36636..bd8cf80fc1 100644 --- a/packages/builder/src/stores/portal/organisation.js +++ b/packages/builder/src/stores/portal/organisation.js @@ -23,6 +23,7 @@ const DEFAULT_CONFIG = { oidcCallbackUrl: "", googleCallbackUrl: "", isSSOEnforced: false, + loaded: false } export function createOrganisationStore() { @@ -43,6 +44,10 @@ export function createOrganisationStore() { delete storeConfig.googleDatasourceConfigured delete storeConfig.oidcCallbackUrl delete storeConfig.googleCallbackUrl + + // delete internal store field + delete storeConfig.loaded + await API.saveConfig({ type: "settings", config: { ...storeConfig, ...config }, @@ -54,7 +59,7 @@ export function createOrganisationStore() { subscribe, set, save, - init, + init } } diff --git a/packages/worker/src/api/controllers/global/configs.ts b/packages/worker/src/api/controllers/global/configs.ts index afbb7c931d..ec55d294f9 100644 --- a/packages/worker/src/api/controllers/global/configs.ts +++ b/packages/worker/src/api/controllers/global/configs.ts @@ -22,7 +22,7 @@ import { isOIDCConfig, isSettingsConfig, isSMTPConfig, - OIDCConfigs, + OIDCConfigs, SettingsBrandingConfig, SettingsInnerConfig, SSOConfig, SSOConfigType, @@ -142,13 +142,29 @@ async function hasActivatedConfig(ssoConfigs?: SSOConfigs) { return !!Object.values(ssoConfigs).find(c => c?.activated) } -async function verifySettingsConfig(config: SettingsInnerConfig) { +async function verifySettingsConfig( + config: SettingsInnerConfig & SettingsBrandingConfig, + existingConfig?: SettingsInnerConfig & SettingsBrandingConfig +) { if (config.isSSOEnforced) { const valid = await hasActivatedConfig() if (!valid) { throw new Error("Cannot enforce SSO without an activated configuration") } } + + // always preserve file attributes + // these should be set via upload instead + // only allow for deletion by checking empty string to bypass this behaviour + + if (existingConfig && config.logoUrl !== "") { + config.logoUrl = existingConfig.logoUrl + config.logoUrlEtag = existingConfig.logoUrlEtag + } + if (existingConfig && config.faviconUrl !== "") { + config.faviconUrl = existingConfig.faviconUrl + config.faviconUrlEtag = existingConfig.faviconUrlEtag + } } async function verifySSOConfig(type: SSOConfigType, config: SSOConfig) { @@ -198,7 +214,7 @@ export async function save(ctx: UserCtx) { await email.verifyConfig(config) break case ConfigType.SETTINGS: - await verifySettingsConfig(config) + await verifySettingsConfig(config, existingConfig?.config) break case ConfigType.GOOGLE: await verifyGoogleConfig(config) @@ -325,7 +341,7 @@ export async function publicSettings( config.faviconUrl = objectStore.getGlobalFileUrl( "settings", "faviconUrl", - branding.faviconUrl + branding.faviconUrlEtag ) }