🎨 Добавлен полный редактор стилей и поле image_url для туров

 Новые функции:
- Поле image_url в модели туров для изменения изображений через админ-панель
- Расширенная модель настроек сайта с категориями: colors, typography, images, effects, layout
- Динамический CSS генератор на основе настроек (/dynamic-styles.css)
- API для управления настройками сайта (/api/site-settings)

🎯 Редактор стилей:
- Управление цветами (основные, акцентные, текст, фон)
- Настройка типографики (шрифты, размеры, межстрочный интервал)
- Управление изображениями (фоны, логотипы, фавикон)
- Эффекты (прозрачность, тени, размытие, скругления)
- Макет (высота секций, размеры контейнеров)
- Пользовательский CSS код

🛠️ Техническая реализация:
- SiteSettingsHelper с кешированием для производительности
- CSS переменные для динамического изменения стилей
- Автоматическая миграция базы данных
- Интеграция с AdminJS для удобного управления
- Загрузка настроек в шаблоны для доступности

📊 База данных:
- Расширена таблица site_settings (добавлено поле category)
- Новые типы настроек: color, file
- 27 предустановленных настроек для полного контроля над дизайном
- Автоматическое применение миграций при старте приложения
This commit is contained in:
2025-11-29 22:03:00 +09:00
parent a461fea9d9
commit ed871fc4d1
8 changed files with 528 additions and 28 deletions

86
src/routes/settings.js Normal file
View File

@@ -0,0 +1,86 @@
import express from 'express';
import SiteSettingsHelper from '../helpers/site-settings.js';
const router = express.Router();
/**
* Динамический CSS на основе настроек сайта
*/
router.get('/dynamic-styles.css', async (req, res) => {
try {
const css = await SiteSettingsHelper.generateCSSVariables();
res.setHeader('Content-Type', 'text/css');
res.setHeader('Cache-Control', 'public, max-age=300'); // Кеш на 5 минут
res.send(css);
} catch (error) {
console.error('Error generating dynamic CSS:', error);
res.status(500).send('/* Error generating dynamic CSS */');
}
});
/**
* API для получения настроек сайта
*/
router.get('/api/site-settings', async (req, res) => {
try {
const settings = await SiteSettingsHelper.getAllSettings();
res.json(settings);
} catch (error) {
console.error('Error loading site settings:', error);
res.status(500).json({ error: 'Failed to load site settings' });
}
});
/**
* API для получения настроек по категории
*/
router.get('/api/site-settings/:category', async (req, res) => {
try {
const { category } = req.params;
const settings = await SiteSettingsHelper.getSettingsByCategory(category);
res.json(settings);
} catch (error) {
console.error('Error loading site settings by category:', error);
res.status(500).json({ error: 'Failed to load site settings' });
}
});
/**
* API для обновления настройки
*/
router.post('/api/site-settings', async (req, res) => {
try {
const { key, value, type, description, category } = req.body;
if (!key || value === undefined) {
return res.status(400).json({ error: 'Key and value are required' });
}
const success = await SiteSettingsHelper.setSetting(key, value, type, description, category);
if (success) {
res.json({ success: true, message: 'Setting updated successfully' });
} else {
res.status(500).json({ error: 'Failed to update setting' });
}
} catch (error) {
console.error('Error updating site setting:', error);
res.status(500).json({ error: 'Failed to update setting' });
}
});
/**
* Очистка кеша настроек (для админов)
*/
router.post('/api/site-settings/clear-cache', async (req, res) => {
try {
SiteSettingsHelper.clearCache();
res.json({ success: true, message: 'Cache cleared successfully' });
} catch (error) {
console.error('Error clearing cache:', error);
res.status(500).json({ error: 'Failed to clear cache' });
}
});
export default router;