const multer = require('multer'); const path = require('path'); const fs = require('fs'); const sharp = require('sharp'); let db; const siteConfig = require('../siteConfig'); function init(database) { db = database; } function setupRoutes(app, auth) { const { authenticateToken, requireAdmin } = auth; const imgDir = path.join(__dirname, '..', '..', 'public', 'img'); const favDir = path.join(__dirname, '..', '..', 'public'); if (!fs.existsSync(imgDir)) fs.mkdirSync(imgDir, { recursive: true }); const storage = multer.memoryStorage(); const uploadLogo = multer({ storage, limits: { fileSize: 5 * 1024 * 1024 }, fileFilter: (req, file, cb) => { const allowed = ['.jpg', '.jpeg', '.png', '.webp', '.svg', '.gif', '.bmp']; const ext = path.extname(file.originalname).toLowerCase(); if (!allowed.includes(ext)) return cb(new Error('Недопустимый формат файла')); cb(null, true); } }); app.post('/api/admin/settings/logo', authenticateToken, requireAdmin, uploadLogo.single('logo'), async (req, res) => { if (!req.file) return res.status(400).json({ error: 'Файл не загружен' }); try { const ext = path.extname(req.file.originalname).toLowerCase(); const logoWebp = path.join(imgDir, 'logo.webp'); const fav32 = path.join(favDir, 'favicon-32x32.png'); const fav16 = path.join(favDir, 'favicon-16x16.png'); const apple = path.join(favDir, 'apple-touch-icon.png'); const pipeline = sharp(req.file.buffer); if (ext !== '.svg') { await pipeline.clone().webp({ quality: 85 }).toFile(logoWebp); } else { fs.writeFileSync(logoWebp, req.file.buffer); } if (ext !== '.svg') { await pipeline.clone().resize(32, 32, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toFile(fav32); await pipeline.clone().resize(16, 16, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toFile(fav16); await pipeline.clone().resize(180, 180, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }).png().toFile(apple); } else { fs.writeFileSync(fav32, req.file.buffer); fs.writeFileSync(fav16, req.file.buffer); fs.writeFileSync(apple, req.file.buffer); } const logoPath = './img/logo.webp'; db.run(`INSERT INTO settings (key, value) VALUES ('logo_path', ?) ON CONFLICT(key) DO UPDATE SET value = ?`, [logoPath, logoPath]); res.json({ path: logoPath, url: logoPath }); } catch (e) { console.error('Ошибка при обработке логотипа:', e); res.status(500).json({ error: 'Ошибка обработки изображения' }); } }); app.get('/api/admin/settings', authenticateToken, requireAdmin, (req, res) => { db.all(`SELECT * FROM settings ORDER BY id`, (err, rows) => { if (err) return res.status(500).json({ error: 'Ошибка БД' }); const obj = {}; rows.forEach(r => { obj[r.key] = r.value; }); res.json(obj); }); }); app.put('/api/admin/settings', authenticateToken, requireAdmin, async (req, res) => { const entries = req.body; if (!entries || typeof entries !== 'object' || Array.isArray(entries)) return res.status(400).json({ error: 'Неверные данные' }); try { if (entries.logo_zoom !== undefined) { const z = parseFloat(entries.logo_zoom); if (!Number.isFinite(z) || z < 0.5 || z > 4) return res.status(400).json({ error: 'Масштаб логотипа должен быть от 0.5 до 4' }); entries.logo_zoom = String(z); } const saved = await siteConfig.setMany(entries); res.json({ message: 'Обновлено', saved }); } catch (err) { console.error('settings put error:', err); res.status(500).json({ error: 'Ошибка сохранения настроек' }); } }); } module.exports = { init, setupRoutes };