From 38117f003d99e62561c835f05f7ac8679b2e1f75 Mon Sep 17 00:00:00 2001 From: kalugin66 Date: Sun, 19 Jul 2026 00:42:39 +0500 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/activities/index.js | 8 +++---- modules/adminBookings/index.js | 24 ++++--------------- modules/bookings/index.js | 10 +++++--- modules/rooms/index.js | 30 ++++++++++------------- modules/settings/index.js | 3 +-- package.json | 3 +++ public/js/main.js | 6 +++-- server.js | 44 ++++++---------------------------- 8 files changed, 44 insertions(+), 84 deletions(-) diff --git a/modules/activities/index.js b/modules/activities/index.js index 6830376..528d681 100644 --- a/modules/activities/index.js +++ b/modules/activities/index.js @@ -187,6 +187,10 @@ function uploadActivityImage(req, res) { res.json({ message: 'Image uploaded', path: imagePath, filename }); } +function getAllActivities(req, res) { + getAdminActivities(req, res); +} + function setupRoutes(app, authenticateToken, requireAdmin, upload) { app.get('/api/activities', getPublicActivities); @@ -198,8 +202,4 @@ function setupRoutes(app, authenticateToken, requireAdmin, upload) { app.post('/api/admin/activities/:id/image', authenticateToken, upload.single('image'), uploadActivityImage); } -function getAllActivities(req, res) { - getAdminActivities(req, res); -} - module.exports = { init, setupRoutes, getUploadsDir }; diff --git a/modules/adminBookings/index.js b/modules/adminBookings/index.js index 14e945a..8beb4fa 100644 --- a/modules/adminBookings/index.js +++ b/modules/adminBookings/index.js @@ -1,4 +1,5 @@ const config = require('../../config'); +const { validatePromocode } = require('../bookings'); const promocodeRateLimit = new Map(); const PROMOCODE_WINDOW = 60 * 1000; @@ -37,22 +38,6 @@ function init(database) { db = database; } -function validatePromocode(promocode, callback) { - if (!promocode) return callback(null, null); - const now = new Date().toISOString(); - db.get(`SELECT * FROM promocodes WHERE code = ? AND is_active = 1`, [promocode], (err, row) => { - if (err || !row) return callback(null, null); - if (row.valid_from && row.valid_from > now) return callback(null, null); - if (row.valid_to && row.valid_to < now) return callback(null, null); - if (row.valid_days) { - const createdDate = new Date(row.created_at); - const expireDate = new Date(createdDate.getTime() + row.valid_days * 24 * 60 * 60 * 1000); - if (expireDate < new Date()) return callback(null, null); - } - callback(null, row); - }); -} - function getBookingsForAdmin(req, res) { const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 20; @@ -152,7 +137,8 @@ function updateBookingRoom(req, res) { const oldValue = booking.room_name ? booking.room_type + ' — ' + booking.room_name : booking.room_type || 'Не указан'; const newValue = room.type + ' — ' + room.name; - const basePrice = config.calculateBasePrice(room.type, booking.checkin_date, booking.checkout_date); + const totalGuests = (booking.adults || 0) + (booking.children || 0); + const basePrice = config.calculateBasePrice(room.type, booking.checkin_date, booking.checkout_date) * Math.max(1, totalGuests); const discountAmount = Math.round(basePrice * (booking.discount_percent || 0) / 100); const totalPrice = basePrice - discountAmount; @@ -375,7 +361,7 @@ function exportCSV(req, res) { params.push(from); } if (to) { - whereClause += ' AND b.checkout_date <= ?'; + whereClause += ' AND b.checkin_date <= ?'; params.push(to); } @@ -467,7 +453,7 @@ function getCalendar(req, res) { } function setupRoutes(app, authenticateToken, requireAdmin) { - app.get('/api/admin/bookings', authenticateToken, getBookingsForAdmin); + app.get('/api/admin/bookings', authenticateToken, requireAdmin, getBookingsForAdmin); app.patch('/api/admin/bookings/:id', authenticateToken, requireAdmin, updateBookingStatus); app.patch('/api/admin/bookings/:id/room', authenticateToken, requireAdmin, updateBookingRoom); app.patch('/api/admin/bookings/:id/comment', authenticateToken, requireAdmin, updateBookingComment); diff --git a/modules/bookings/index.js b/modules/bookings/index.js index 0d79e08..aaa7f34 100644 --- a/modules/bookings/index.js +++ b/modules/bookings/index.js @@ -31,15 +31,19 @@ function calculateNightPrices(roomType, checkin, checkout, callback) { let total = 0; let processed = 0; - let hasError = false; + let finished = false; dates.forEach(date => { getEffectivePrice(roomType, date, (err, price) => { - if (hasError) return; - if (err) { hasError = true; return callback(err, null); } + if (finished) return; + if (err) { + finished = true; + return callback(err, null); + } total += price; processed++; if (processed === dates.length) { + finished = true; callback(null, total); } }); diff --git a/modules/rooms/index.js b/modules/rooms/index.js index 43fb3a9..435503f 100644 --- a/modules/rooms/index.js +++ b/modules/rooms/index.js @@ -149,7 +149,7 @@ function uploadRoomImages(req, res) { return res.status(400).json({ error: 'Файлы не загружены' }); } - let processed = 0; + let completed = 0; const results = []; function processFile(index) { @@ -165,7 +165,12 @@ function uploadRoomImages(req, res) { [roomId, finalPath, sortOrder], function(insErr) { if (insErr) { console.error('Room image insert error:', insErr); } else results.push({ id: this.lastID, image_path: finalPath, sort_order: sortOrder }); - processFile(index + 1); + completed++; + if (completed === req.files.length) { + res.json({ message: 'Изображения загружены', images: results }); + } else { + processFile(index + 1); + } }); }); } @@ -181,7 +186,12 @@ function uploadRoomImages(req, res) { saveByPath('data/room_images/' + path.basename(outputPath)); }) .catch(() => { - saveByPath(imagePath); + completed++; + if (completed === req.files.length) { + res.json({ message: 'Изображения загружены', images: results }); + } else { + processFile(index + 1); + } }); } else { saveByPath(imagePath); @@ -189,20 +199,6 @@ function uploadRoomImages(req, res) { } processFile(0); - - const checkDone = setInterval(() => { - if (results.length + (processed > req.files.length ? 0 : 0) >= req.files.length) { - clearInterval(checkDone); - if (results.length === 0) { - // Still processing sharp conversions, wait a bit more - setTimeout(() => { - res.json({ message: 'Изображения загружены', images: results }); - }, 1000); - } else { - res.json({ message: 'Изображения загружены', images: results }); - } - } - }, 200); } function deleteRoomImage(req, res) { diff --git a/modules/settings/index.js b/modules/settings/index.js index a0a5e6e..5d5ce3d 100644 --- a/modules/settings/index.js +++ b/modules/settings/index.js @@ -142,8 +142,7 @@ function setupRoutes(app, authenticateToken, requireAdmin) { res.json({ message: `${envVarName} перегенерирован. НЕОБХОДИМО перезапустить сервер для применения нового ключа.${jwtWarning}`, - key: secret, - warning: 'Сохраните этот ключ. Он будет показан только один раз. После перезапуска сервера старый ключ перестанет работать.' + warning: 'Ключ сохранён в .env. После перезапуска сервера старый ключ перестанет работать.' }); }); diff --git a/package.json b/package.json index a54d436..3ba4ecb 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "sharp": "^0.34.5", "sqlite3": "^6.0.1" }, + "devDependencies": { + "nodemon": "^3.1.0" + }, "scripts": { "start": "node server.js", "dev": "nodemon server.js" diff --git a/public/js/main.js b/public/js/main.js index 85896b2..d6357f3 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -66,8 +66,10 @@ if (statsSection) counterObserver.observe(statsSection); // Set min date for checkin to today const today = new Date().toISOString().split('T')[0]; -document.querySelector('[name="checkin"]').min = today; -document.querySelector('[name="checkout"]').min = today; +const checkinInput = document.querySelector('[name="checkin"]'); +const checkoutInput = document.querySelector('[name="checkout"]'); +if (checkinInput) checkinInput.min = today; +if (checkoutInput) checkoutInput.min = today; // Booking modal - set room name document.querySelectorAll('.btn-book').forEach(btn => { diff --git a/server.js b/server.js index 19bb310..61884f8 100644 --- a/server.js +++ b/server.js @@ -179,9 +179,10 @@ if (!API_KEY) { process.exit(1); } -app.use(express.json({ limit: '10kb' })); +app.use(express.json({ limit: '1mb' })); +const CORS_ORIGIN = process.env.CORS_ORIGIN || '*'; app.use((req, res, next) => { - res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Origin', CORS_ORIGIN); res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') return res.sendStatus(200); @@ -189,7 +190,10 @@ app.use((req, res, next) => { }); app.use(express.static(path.join(__dirname, 'public'))); app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); -app.use('/data', express.static(path.join(__dirname, 'data'))); +app.use('/data/room_images', express.static(roomsUploadsDir)); +app.use('/data/activity_images', express.static(activityUploadsDir)); +app.use('/data/hero_images', express.static(heroImagesDir)); +app.use('/data/hero_videos', express.static(heroVideosDir)); app.use((req, res, next) => { const start = Date.now(); @@ -444,41 +448,7 @@ app.get('/api/cities/:countryCode', (req, res) => { } }); -app.get('/data/room_images/:filename', (req, res) => { - const filePath = path.join(roomsUploadsDir, req.params.filename); - if (fs.existsSync(filePath)) { - res.sendFile(filePath); - } else { - res.status(404).json({ error: 'File not found' }); - } -}); -app.get('/data/activity_images/:filename', (req, res) => { - const filePath = path.join(activityUploadsDir, req.params.filename); - if (fs.existsSync(filePath)) { - res.sendFile(filePath); - } else { - res.status(404).json({ error: 'File not found' }); - } -}); - -app.get('/data/hero_images/:filename', (req, res) => { - const filePath = path.join(heroImagesDir, req.params.filename); - if (fs.existsSync(filePath)) { - res.sendFile(filePath); - } else { - res.status(404).json({ error: 'File not found' }); - } -}); - -app.get('/data/hero_videos/:filename', (req, res) => { - const filePath = path.join(heroVideosDir, req.params.filename); - if (fs.existsSync(filePath)) { - res.sendFile(filePath); - } else { - res.status(404).json({ error: 'File not found' }); - } -}); app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html'));