diff --git a/app/utils/settings_sync.py b/app/utils/settings_sync.py index 893e14ce..3298c189 100644 --- a/app/utils/settings_sync.py +++ b/app/utils/settings_sync.py @@ -38,7 +38,9 @@ _last_seen_version: str = "" def notify_settings_updated() -> None: """ - Publish a settings-updated signal by updating the Redis version key. + Publish a settings-updated signal by updating the Redis version key, and + immediately reload the in-process ``settings`` singleton so the API + process serves fresh values without a restart. Call this after every successful settings write so that all worker processes know they need to reload their in-memory configuration. @@ -56,6 +58,19 @@ def notify_settings_updated() -> None: except Exception as exc: logger.warning(f"Could not publish settings update to Redis: {exc}") + # Reload the in-process settings singleton immediately so the API node + # returns updated values (e.g. oauth_provider_name on the login page) + # without needing a restart. Workers use the task_prerun signal handler + # instead, so this only affects the API/web process. + try: + from app.config import settings + from app.utils.config_loader import reload_settings_from_db + + reload_settings_from_db(settings) + logger.debug("In-process settings reloaded after settings update") + except Exception as exc: + logger.warning(f"Could not reload in-process settings: {exc}") + def register_settings_reload_signal() -> None: """ diff --git a/frontend/templates/settings.html b/frontend/templates/settings.html index 36afbd72..b35ccefe 100644 --- a/frontend/templates/settings.html +++ b/frontend/templates/settings.html @@ -185,7 +185,7 @@ {% endif %} - +
+
@@ -233,9 +245,11 @@ function settingsApp() { return { formData: {}, originalData: {}, + isDbOverride: {}, showPassword: {}, saving: false, savingKey: null, + revertingKey: null, showAlert: false, alertType: 'success', alertTitle: '', @@ -247,6 +261,7 @@ function settingsApp() { {% for setting in settings_list %} this.formData['{{ setting.key }}'] = {{ setting.display_value|tojson }}; this.originalData['{{ setting.key }}'] = {{ setting.display_value|tojson }}; + this.isDbOverride['{{ setting.key }}'] = {{ (setting.source == 'database')|tojson }}; this.showPassword['{{ setting.key }}'] = false; {% endfor %} {% endfor %} @@ -297,6 +312,7 @@ function settingsApp() { if (response.ok && result.success) { this.originalData[key] = value; + this.isDbOverride[key] = true; let message = `Setting '${key}' saved successfully.`; if (result.restart_required) { message += ' Please restart the application for this change to take effect.'; @@ -313,6 +329,30 @@ function settingsApp() { } }, + async revertSetting(key) { + this.revertingKey = key; + this.hideAlert(); + + try { + const response = await fetch(`/api/settings/${key}`, { + method: 'DELETE', + }); + + if (response.ok) { + // Reload the page so the ENV/default value and source badge refresh + window.location.reload(); + } else { + const result = await response.json(); + this.showErrorAlert('Revert Failed', result.detail || 'Unknown error'); + this.revertingKey = null; + } + } catch (error) { + console.error('Error reverting setting:', error); + this.showErrorAlert('Error', 'Failed to revert setting. Please try again.'); + this.revertingKey = null; + } + }, + async saveSettings() { this.saving = true; this.hideAlert(); @@ -345,7 +385,10 @@ function settingsApp() { const result = await response.json(); if (response.ok && result.success) { - this.originalData = { ...this.formData }; + for (const updated of result.updated) { + this.originalData[updated.key] = updated.value; + this.isDbOverride[updated.key] = true; + } let message = `${result.updated.length} setting(s) updated successfully.`; if (result.restart_required) { message += ' Please restart the application for changes to take effect.';