141 lines
4.9 KiB
JavaScript
141 lines
4.9 KiB
JavaScript
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) => {
|
||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
||
});
|
||
|
||
app.listen(PORT, async () => {
|
||
await convertImagesToWebp();
|
||
console.log(`✅ server running on http://localhost:${PORT}`);
|
||
console.log(`🔑 API Key: ${API_KEY}`);
|
||
}); |