This commit is contained in:
2026-05-13 02:16:02 +05:00
commit f0eb7fb1ea
21 changed files with 1487 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
node_modules
package-lock.json
.env
data
promt

12
package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"dependencies": {
"dotenv": "^17.4.2",
"express": "^5.2.1",
"sharp": "^0.34.5",
"sqlite3": "^6.0.1"
},
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
public/img/geo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

BIN
public/img/hero-bg.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
public/img/howo-sitrak.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
public/img/john-deere.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
public/img/kamaz-6520.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
public/img/ks-45721.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
public/img/license-lom.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

BIN
public/img/logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
public/img/secret.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
public/img/tral-1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
public/img/tral-2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

1329
public/index.html Normal file

File diff suppressed because it is too large Load Diff

141
server.js Normal file
View File

@@ -0,0 +1,141 @@
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}`);
});