ю
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
.env
|
||||
data
|
||||
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
# Устанавливаем git
|
||||
RUN apk update --no-cache
|
||||
RUN apk add --no-cache git
|
||||
RUN git clone https://git.dadehard.ru/kalugin66/hotel777.git .
|
||||
# Копируем уже склонированный репозиторий с хоста
|
||||
#COPY ./hotel777 /app
|
||||
RUN npm i
|
||||
EXPOSE 3000
|
||||
CMD ["npm", "start"]
|
||||
31
docker-compose.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
hotell777:
|
||||
build: .
|
||||
image: kalugin66/hotell777
|
||||
container_name: hotell777
|
||||
# ports:
|
||||
# - "3000:3000"
|
||||
networks:
|
||||
- applications
|
||||
restart: always
|
||||
volumes:
|
||||
- /docker/hotell777/data:/app/data:rw
|
||||
environment:
|
||||
- TZ=Asia/Yekaterinburg
|
||||
- HOTEL777KEY="secretkey"
|
||||
- hotelName="Hotel 777"
|
||||
- hotelAddress="Абхазтя золотой берег"
|
||||
- hotelPhone="+79400000000"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks:
|
||||
applications:
|
||||
external: true
|
||||
# docker network create applications
|
||||
# docker compose up -d
|
||||
# docker compose up -d --build
|
||||
# docker compose build --no-cache && docker compose up -d
|
||||
12
package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
BIN
public/img/1006bc9be-64c6-43e3-9625-1a862d04930c.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/img/105d059e5-a204-499f-a01c-c540f6ae1e5d.webp
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
public/img/123f15ff3-55ae-428f-8af9-7d53c01d28a5.webp
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
public/img/16536f4b7-0c1c-4bbb-8537-96bbdd2e0886.webp
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
public/img/18abbb136-0e24-4c92-a568-fec8011e029b.webp
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
public/img/18c4bdbc7-3255-46ab-bbce-4964d4b3d3f9.webp
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
public/img/1e1ad70fe-ff01-4f94-9ca9-4926eba5bdd2.webp
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
public/img/1e4e22546-0208-4ba8-96fb-02052aecc8b5.webp
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
public/img/1eae46658-cfca-4b65-82f0-e5868af5541b.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/img/1f00325ba-df2c-4bb9-9ec6-28958e11f843.webp
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
public/img/1faae356b-9f79-489d-8165-c37a47b82040.webp
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/img/h777.webp
Normal file
|
After Width: | Height: | Size: 289 KiB |
1947
public/index.html
Normal file
109
server.js
Normal file
@@ -0,0 +1,109 @@
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 bookings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
phone TEXT NOT NULL,
|
||||
adults INTEGER NOT NULL,
|
||||
children INTEGER NOT NULL,
|
||||
checkin_date TEXT NOT NULL,
|
||||
checkout_date TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// Image conversion (automatically convert JPEG/PNG to WebP)
|
||||
async function convertImages() {
|
||||
const imgDir = path.join(__dirname, 'public', 'img');
|
||||
if (!fs.existsSync(imgDir)) {
|
||||
console.log('Папка img не найдена, пропускаем конвертацию.');
|
||||
return;
|
||||
}
|
||||
const files = fs.readdirSync(imgDir);
|
||||
for (const file of files) {
|
||||
const ext = path.extname(file).toLowerCase();
|
||||
if (ext === '.jpg' || ext === '.jpeg' || ext === '.png') {
|
||||
const name = path.parse(file).name;
|
||||
const webpPath = path.join(imgDir, `${name}.webp`);
|
||||
if (!fs.existsSync(webpPath)) {
|
||||
try {
|
||||
await sharp(path.join(imgDir, file))
|
||||
.webp({ quality: 85 })
|
||||
.toFile(webpPath);
|
||||
console.log(`✅ Сконвертировано: ${file} -> ${name}.webp`);
|
||||
} catch (err) {
|
||||
console.error(`❌ Ошибка при конвертации ${file}:`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// API: POST /api/bookings – сохранить новую заявку
|
||||
app.post('/api/bookings', (req, res) => {
|
||||
const { name, phone, adults, children, checkin, checkout } = req.body;
|
||||
if (!name || !phone || !adults || !checkin || !checkout) {
|
||||
return res.status(400).json({ error: 'Missing required fields' });
|
||||
}
|
||||
const stmt = db.prepare(`INSERT INTO bookings (name, phone, adults, children, checkin_date, checkout_date)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`);
|
||||
stmt.run(name, phone, adults, children || 0, checkin, checkout, function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return res.status(500).json({ error: 'Database error' });
|
||||
}
|
||||
res.status(201).json({ id: this.lastID, message: 'Booking saved' });
|
||||
});
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
// API: GET /api/bookings – получить список всех заявок (требуется API-ключ)
|
||||
app.get('/api/bookings', (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, adults, children, checkin_date, checkout_date, created_at
|
||||
FROM bookings ORDER BY created_at DESC`, (err, rows) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return res.status(500).json({ error: 'Database error' });
|
||||
}
|
||||
res.json(rows);
|
||||
});
|
||||
});
|
||||
|
||||
// Serve frontend
|
||||
app.get('/', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
||||
});
|
||||
|
||||
// Start server after image conversion
|
||||
convertImages().then(() => {
|
||||
app.listen(PORT, () => {
|
||||
console.log('✅ HOTEL777KEY is', API_KEY);
|
||||
console.log(`✅ Hotel 777 server running on http://localhost:${PORT}`);
|
||||
});
|
||||
});
|
||||