From 4081b7f022f63d10aa6ef430a41b674ab2c922db Mon Sep 17 00:00:00 2001 From: Will Miao <13051207myq@gmail.com> Date: Tue, 29 Jul 2025 20:29:19 +0800 Subject: [PATCH] feat: implement settings synchronization with backend and migrate legacy settings --- py/routes/misc_routes.py | 3 + static/js/managers/MoveManager.js | 2 +- static/js/managers/SettingsManager.js | 140 ++++++++++----------- static/js/managers/import/FolderBrowser.js | 2 +- 4 files changed, 68 insertions(+), 79 deletions(-) diff --git a/py/routes/misc_routes.py b/py/routes/misc_routes.py index 4fdd9485..be07d105 100644 --- a/py/routes/misc_routes.py +++ b/py/routes/misc_routes.py @@ -167,6 +167,9 @@ class MiscRoutes: # Validate and update settings for key, value in data.items(): + if value == settings.get(key): + # No change, skip + continue # Special handling for example_images_path - verify path exists if key == 'example_images_path' and value: if not os.path.exists(value): diff --git a/static/js/managers/MoveManager.js b/static/js/managers/MoveManager.js index 5f14ea1e..b734c16a 100644 --- a/static/js/managers/MoveManager.js +++ b/static/js/managers/MoveManager.js @@ -90,7 +90,7 @@ class MoveManager { ).join(''); // Set default lora root if available - const defaultRoot = getStorageItem('settings', {}).default_loras_root; + const defaultRoot = getStorageItem('settings', {}).default_lora_root; if (defaultRoot && rootsData.roots.includes(defaultRoot)) { this.loraRootSelect.value = defaultRoot; } diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 8f3be9af..88c64038 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -15,29 +15,39 @@ export class SettingsManager { // Ensure settings are loaded from localStorage this.loadSettingsFromStorage(); - + + // Sync settings to backend if needed + this.syncSettingsToBackendIfNeeded(); + this.initialize(); } loadSettingsFromStorage() { // Get saved settings from localStorage const savedSettings = getStorageItem('settings'); - + + // Migrate legacy default_loras_root to default_lora_root if present + if (savedSettings && savedSettings.default_loras_root && !savedSettings.default_lora_root) { + savedSettings.default_lora_root = savedSettings.default_loras_root; + delete savedSettings.default_loras_root; + setStorageItem('settings', savedSettings); + } + // Apply saved settings to state if available if (savedSettings) { state.global.settings = { ...state.global.settings, ...savedSettings }; } - + // Initialize default values for new settings if they don't exist if (state.global.settings.compactMode === undefined) { state.global.settings.compactMode = false; } - + // Set default for optimizeExampleImages if undefined if (state.global.settings.optimizeExampleImages === undefined) { state.global.settings.optimizeExampleImages = true; } - + // Set default for cardInfoDisplay if undefined if (state.global.settings.cardInfoDisplay === undefined) { state.global.settings.cardInfoDisplay = 'always'; @@ -74,6 +84,50 @@ export class SettingsManager { } } + async syncSettingsToBackendIfNeeded() { + // Get local settings from storage + const localSettings = getStorageItem('settings') || {}; + + // Fields that need to be synced to backend + const fieldsToSync = [ + 'civitai_api_key', + 'default_lora_root', + 'default_checkpoint_root', + 'default_embedding_root', + 'base_model_path_mappings', + 'download_path_template' + ]; + + // Build payload for syncing + const payload = {}; + + fieldsToSync.forEach(key => { + if (localSettings[key] !== undefined) { + if (key === 'base_model_path_mappings') { + payload[key] = JSON.stringify(localSettings[key]); + } else { + payload[key] = localSettings[key]; + } + } + }); + + // Only send request if there is something to sync + if (Object.keys(payload).length > 0) { + try { + await fetch('/api/settings', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + // Log success to console + console.log('Settings synced to backend'); + } catch (e) { + // Log error to console + console.error('Failed to sync settings to backend:', e); + } + } + } + initialize() { if (this.initialized) return; @@ -159,8 +213,6 @@ export class SettingsManager { // Load default embedding root await this.loadEmbeddingRoots(); - - // Backend settings are loaded from the template directly } async loadLoraRoots() { @@ -193,7 +245,7 @@ export class SettingsManager { }); // Set selected value from settings - const defaultRoot = state.global.settings.default_loras_root || ''; + const defaultRoot = state.global.settings.default_lora_root || ''; defaultLoraRootSelect.value = defaultRoot; } catch (error) { @@ -507,7 +559,7 @@ export class SettingsManager { try { // For backend settings, make API call - if (['show_only_sfw', 'blur_mature_content', 'autoplay_on_hover', 'optimize_example_images', 'use_centralized_examples'].includes(settingKey)) { + if (['show_only_sfw'].includes(settingKey)) { const payload = {}; payload[settingKey] = value; @@ -552,7 +604,7 @@ export class SettingsManager { // Update frontend state if (settingKey === 'default_lora_root') { - state.global.settings.default_loras_root = value; + state.global.settings.default_lora_root = value; } else if (settingKey === 'default_checkpoint_root') { state.global.settings.default_checkpoint_root = value; } else if (settingKey === 'default_embedding_root') { @@ -632,10 +684,7 @@ export class SettingsManager { // Update state state.global.settings[settingKey] = value; - // Save to localStorage if appropriate - if (!settingKey.includes('api_key')) { // Don't store API keys in localStorage for security - setStorageItem('settings', state.global.settings); - } + setStorageItem('settings', state.global.settings); // For backend settings, make API call const payload = {}; @@ -717,69 +766,6 @@ export class SettingsManager { } } - async saveSettings() { - // Get frontend settings from UI - const blurMatureContent = document.getElementById('blurMatureContent').checked; - const showOnlySFW = document.getElementById('showOnlySFW').checked; - const defaultLoraRoot = document.getElementById('defaultLoraRoot').value; - const defaultCheckpointRoot = document.getElementById('defaultCheckpointRoot').value; - const autoplayOnHover = document.getElementById('autoplayOnHover').checked; - const optimizeExampleImages = document.getElementById('optimizeExampleImages').checked; - - // Get backend settings - const apiKey = document.getElementById('civitaiApiKey').value; - - // Update frontend state and save to localStorage - state.global.settings.blurMatureContent = blurMatureContent; - state.global.settings.show_only_sfw = showOnlySFW; - state.global.settings.default_loras_root = defaultLoraRoot; - state.global.settings.default_checkpoint_root = defaultCheckpointRoot; - state.global.settings.autoplayOnHover = autoplayOnHover; - state.global.settings.optimizeExampleImages = optimizeExampleImages; - - // Save settings to localStorage - setStorageItem('settings', state.global.settings); - - try { - // Save backend settings via API - const response = await fetch('/api/settings', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - civitai_api_key: apiKey, - show_only_sfw: showOnlySFW, - optimize_example_images: optimizeExampleImages, - default_checkpoint_root: defaultCheckpointRoot - }) - }); - - if (!response.ok) { - throw new Error('Failed to save settings'); - } - - showToast('Settings saved successfully', 'success'); - modalManager.closeModal('settingsModal'); - - // Apply frontend settings immediately - this.applyFrontendSettings(); - - if (this.currentPage === 'loras') { - // Reload the loras without updating folders - await resetAndReload(false); - } else if (this.currentPage === 'recipes') { - // Reload the recipes without updating folders - await window.recipeManager.loadRecipes(); - } else if (this.currentPage === 'checkpoints') { - // Reload the checkpoints without updating folders - await window.checkpointsManager.loadCheckpoints(); - } - } catch (error) { - showToast('Failed to save settings: ' + error.message, 'error'); - } - } - applyFrontendSettings() { // Apply blur setting to existing content const blurSetting = state.global.settings.blurMatureContent; diff --git a/static/js/managers/import/FolderBrowser.js b/static/js/managers/import/FolderBrowser.js index edd4d6a4..8eba907e 100644 --- a/static/js/managers/import/FolderBrowser.js +++ b/static/js/managers/import/FolderBrowser.js @@ -112,7 +112,7 @@ export class FolderBrowser { ).join(''); // Set default lora root if available - const defaultRoot = getStorageItem('settings', {}).default_loras_root; + const defaultRoot = getStorageItem('settings', {}).default_lora_root; if (defaultRoot && rootsData.roots.includes(defaultRoot)) { loraRoot.value = defaultRoot; }