import db from '../config/database.js'; class SiteSettingsHelper { static cache = new Map(); static lastUpdate = null; static cacheTimeout = 5 * 60 * 1000; // 5 минут /** * Получить все настройки сайта с кешированием */ static async getAllSettings() { const now = Date.now(); // Проверяем кеш if (this.lastUpdate && (now - this.lastUpdate) < this.cacheTimeout && this.cache.size > 0) { return Object.fromEntries(this.cache); } try { const result = await db.query('SELECT setting_key, setting_value, setting_type FROM site_settings ORDER BY category, setting_key'); // Очищаем кеш и заполняем новыми данными this.cache.clear(); for (const row of result.rows) { let value = row.setting_value; // Преобразуем значение в зависимости от типа switch (row.setting_type) { case 'number': value = parseFloat(value) || 0; break; case 'boolean': value = value === 'true' || value === '1'; break; case 'json': try { value = JSON.parse(value); } catch { value = {}; } break; } this.cache.set(row.setting_key, value); } this.lastUpdate = now; return Object.fromEntries(this.cache); } catch (error) { console.error('Error loading site settings:', error); return {}; } } /** * Получить настройку по ключу */ static async getSetting(key, defaultValue = null) { const settings = await this.getAllSettings(); return settings[key] !== undefined ? settings[key] : defaultValue; } /** * Установить настройку */ static async setSetting(key, value, type = 'text', description = '', category = 'general') { try { await db.query(` INSERT INTO site_settings (setting_key, setting_value, setting_type, description, category, updated_at) VALUES ($1, $2, $3, $4, $5, NOW()) ON CONFLICT (setting_key) DO UPDATE SET setting_value = EXCLUDED.setting_value, setting_type = EXCLUDED.setting_type, description = EXCLUDED.description, category = EXCLUDED.category, updated_at = NOW() `, [key, String(value), type, description, category]); // Обновляем кеш this.cache.set(key, value); return true; } catch (error) { console.error('Error setting site setting:', error); return false; } } /** * Получить настройки по категории */ static async getSettingsByCategory(category) { try { const result = await db.query( 'SELECT setting_key, setting_value, setting_type FROM site_settings WHERE category = $1 ORDER BY setting_key', [category] ); const settings = {}; for (const row of result.rows) { let value = row.setting_value; switch (row.setting_type) { case 'number': value = parseFloat(value) || 0; break; case 'boolean': value = value === 'true' || value === '1'; break; case 'json': try { value = JSON.parse(value); } catch { value = {}; } break; } settings[row.setting_key] = value; } return settings; } catch (error) { console.error('Error loading settings by category:', error); return {}; } } /** * Генерировать CSS переменные из настроек */ static async generateCSSVariables() { const settings = await this.getAllSettings(); let css = ':root {\n'; // Цвета if (settings.primary_color) css += ` --primary-color: ${settings.primary_color};\n`; if (settings.secondary_color) css += ` --secondary-color: ${settings.secondary_color};\n`; if (settings.accent_color) css += ` --accent-color: ${settings.accent_color};\n`; if (settings.text_color) css += ` --text-color: ${settings.text_color};\n`; if (settings.background_color) css += ` --background-color: ${settings.background_color};\n`; if (settings.card_background) css += ` --card-background: ${settings.card_background};\n`; // Типографика if (settings.font_family_primary) css += ` --font-family-primary: ${settings.font_family_primary};\n`; if (settings.font_family_display) css += ` --font-family-display: ${settings.font_family_display};\n`; if (settings.font_size_base) css += ` --font-size-base: ${settings.font_size_base}px;\n`; if (settings.line_height_base) css += ` --line-height-base: ${settings.line_height_base};\n`; // Эффекты if (settings.hero_overlay_opacity) css += ` --hero-overlay-opacity: ${settings.hero_overlay_opacity};\n`; if (settings.hero_overlay_color) css += ` --hero-overlay-color: ${settings.hero_overlay_color};\n`; if (settings.card_shadow) css += ` --card-shadow: ${settings.card_shadow};\n`; if (settings.border_radius) css += ` --border-radius: ${settings.border_radius}px;\n`; if (settings.blur_effect) css += ` --blur-effect: ${settings.blur_effect}px;\n`; // Макет if (settings.hero_height_desktop) css += ` --hero-height-desktop: ${settings.hero_height_desktop}vh;\n`; if (settings.hero_height_mobile) css += ` --hero-height-mobile: ${settings.hero_height_mobile}vh;\n`; if (settings.compact_hero_height) css += ` --compact-hero-height: ${settings.compact_hero_height}vh;\n`; if (settings.container_max_width) css += ` --container-max-width: ${settings.container_max_width}px;\n`; if (settings.navbar_height) css += ` --navbar-height: ${settings.navbar_height}px;\n`; // Анимации if (settings.animation_duration) css += ` --animation-duration: ${settings.animation_duration}s;\n`; css += '}\n'; // Добавляем пользовательский CSS if (settings.custom_css) { css += '\n/* Пользовательский CSS */\n' + settings.custom_css + '\n'; } return css; } /** * Очистить кеш настроек */ static clearCache() { this.cache.clear(); this.lastUpdate = null; } } export default SiteSettingsHelper;