const express = require('express'); const path = require('path'); const fs = require('fs'); const sqlite3 = require('sqlite3').verbose(); const sharp = require('sharp'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 3000; const API_KEY = process.env.HOTEL777KEY; if (!API_KEY) { console.error('FATAL: HOTEL777KEY environment variable not set'); process.exit(1); } const imgDir = path.join(__dirname, 'public', 'img'); const supportedFormats = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff']; async function convertImagesToWebp() { if (!fs.existsSync(imgDir)) { console.log('📁 Папка img не найдена, пропускаем конвертацию'); return; } const files = fs.readdirSync(imgDir); const toConvert = files.filter(f => { const ext = path.extname(f).toLowerCase(); return supportedFormats.includes(ext) && !f.endsWith('.webp'); }); if (toConvert.length === 0) { console.log('✅ Все изображения уже в формате WebP'); return; } console.log(`🔄 Конвертация ${toConvert.length} изображений в WebP...`); for (const file of toConvert) { const inputPath = path.join(imgDir, file); const outputPath = path.join(imgDir, path.basename(file, path.extname(file)) + '.webp'); try { await sharp(inputPath) .webp({ quality: 85 }) .toFile(outputPath); fs.unlinkSync(inputPath); console.log(` ✅ ${file} → ${path.basename(outputPath)}`); } catch (err) { console.error(` ❌ Ошибка конвертации ${file}:`, err.message); } } console.log('✅ Конвертация завершена'); } // Middleware app.use(express.json()); app.use(express.static(path.join(__dirname, 'public'))); // Ensure data directory and database const dataDir = path.join(__dirname, 'data'); if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir); const dbPath = path.join(dataDir, 'bookings.db'); const db = new sqlite3.Database(dbPath); db.run(`CREATE TABLE IF NOT EXISTS leads ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, phone TEXT NOT NULL, message TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`); // API: POST /api/leads – сохранить новую заявку (публичный) app.post('/api/leads', (req, res) => { const { name, phone, message } = req.body; if (!name || !phone) { return res.status(400).json({ error: 'Missing required fields: name, phone' }); } const stmt = db.prepare(`INSERT INTO leads (name, phone, message) VALUES (?, ?, ?)`); stmt.run(name.trim(), phone.trim(), (message || '').trim(), function(err) { if (err) { console.error(err); return res.status(500).json({ error: 'Database error' }); } console.log(`✅ Новая заявка #${this.lastID}: ${name} | ${phone}`); res.status(201).json({ id: this.lastID, message: 'Lead saved' }); }); stmt.finalize(); }); // API: GET /api/leads – получить список всех заявок (требуется API-ключ) app.get('/api/leads', (req, res) => { const providedKey = req.headers['x-api-key']; if (!providedKey || providedKey !== API_KEY) { return res.status(401).json({ error: 'Invalid or missing API key' }); } db.all(`SELECT id, name, phone, message, created_at FROM leads ORDER BY created_at DESC`, (err, rows) => { if (err) { console.error(err); return res.status(500).json({ error: 'Database error' }); } res.json(rows); }); }); // API: DELETE /api/leads/:id – удалить заявку (требуется API-ключ) app.delete('/api/leads/:id', (req, res) => { const providedKey = req.headers['x-api-key']; if (!providedKey || providedKey !== API_KEY) { return res.status(401).json({ error: 'Invalid or missing API key' }); } const id = parseInt(req.params.id); if (isNaN(id)) { return res.status(400).json({ error: 'Invalid ID' }); } const stmt = db.prepare(`DELETE FROM leads WHERE id = ?`); stmt.run(id, function(err) { if (err) { console.error(err); return res.status(500).json({ error: 'Database error' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Lead not found' }); } console.log(`🗑️ Удалена заявка #${id}`); res.json({ message: 'Lead deleted' }); }); stmt.finalize(); }); // Serve frontend app.get('/', (req, res) => { const years = new Date().getFullYear() - 2011; const htmlPath = path.join(__dirname, 'public', 'index.html'); const html = fs.readFileSync(htmlPath, 'utf8'); res.send(html.replace(/\{years\}/g, String(years))); }); app.listen(PORT, async () => { await convertImagesToWebp(); console.log(`✅ server running on http://localhost:${PORT}`); console.log(`🔑 API Key: ${API_KEY}`); });