Отзывы

This commit is contained in:
2026-05-10 21:42:31 +05:00
parent 001587799c
commit b8d7f23ef5
18 changed files with 2632 additions and 102 deletions

133
server.js
View File

@@ -203,6 +203,50 @@ db.serialize(() => {
role TEXT NOT NULL DEFAULT 'user',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
db.run(`CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`, (err) => {
if (err) {
console.error('CREATE TABLE settings FAILED:', err.message);
} else {
console.log('CREATE TABLE settings SUCCESS');
}
});
db.run(`CREATE TABLE IF NOT EXISTS reviews (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author_name TEXT NOT NULL,
country TEXT NOT NULL,
country_code TEXT,
city TEXT NOT NULL,
stars REAL NOT NULL CHECK(stars >= 0 AND stars <= 5),
text TEXT NOT NULL,
review_code TEXT NOT NULL,
ip_address TEXT,
is_approved INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`, (err) => {
if (err && !err.message.includes('already exists')) {
console.error('Reviews table error:', err.message);
}
db.run(`PRAGMA foreign_keys = ON`, (err) => {
if (err) console.error('Foreign keys error:', err.message);
});
db.all("PRAGMA table_info(reviews)", [], (err, cols) => {
if (err || !cols) return;
const colNames = cols.map(c => c.name);
if (!colNames.includes('country_code')) {
db.run("ALTER TABLE reviews ADD COLUMN country_code TEXT", (err) => {
if (err) console.log('Migration: country_code column (ignore if exists):', err.message);
else console.log('Migration: country_code column added');
});
}
});
});
});
const modules = {};
@@ -213,6 +257,9 @@ const adminBookingsModule = require('./modules/adminBookings');
const promocodesModule = require('./modules/promocodes');
const roomsModule = require('./modules/rooms');
const usersModule = require('./modules/users');
const settingsModule = require('./modules/settings');
const reviewsModule = require('./modules/reviews');
const translationsModule = require('./modules/translations');
const { runStartupTests } = require('./tests/runStartupTests');
modules.auth = authModule;
@@ -221,6 +268,9 @@ modules.promocodes = promocodesModule;
modules.rooms = roomsModule;
modules.users = usersModule;
modules.adminBookings = adminBookingsModule;
modules.settings = settingsModule;
modules.reviews = reviewsModule;
modules.translations = translationsModule;
authModule.init(db, JWT_SECRET);
bookingsModule.init(db);
@@ -228,6 +278,8 @@ adminBookingsModule.init(db);
promocodesModule.init(db);
roomsModule.init(db);
usersModule.init(db, bcrypt);
settingsModule.init(db);
reviewsModule.init(db, settingsModule);
function initDefaultRooms() {
db.get("SELECT COUNT(*) as count FROM rooms", (err, row) => {
@@ -279,6 +331,87 @@ adminBookingsModule.setupRoutes(app, authModule.authenticateToken, authModule.re
promocodesModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
roomsModule.setupRoutes(app);
usersModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
settingsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
reviewsModule.setupRoutes(app, authModule.authenticateToken, authModule.requireAdmin);
app.get('/api/translations/:lang', (req, res) => {
const lang = req.params.lang;
const translations = translationsModule.getTranslations(lang);
if (!translations) {
return res.status(404).json({ error: 'Language not found' });
}
res.json(translations);
});
app.get('/api/countries-cities', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'data', 'countries-cities.js'));
});
app.get('/api/countries', (req, res) => {
const countriesPath = path.join(__dirname, 'public', 'data', 'countries.js');
fs.readFile(countriesPath, 'utf8', (err, data) => {
if (err) return res.status(500).json({ error: 'File not found' });
const match = data.match(/const\s+COUNTRIES\s*=\s*(\[.*\]);/s);
if (match) {
try {
const countries = eval(match[1]);
res.json(countries);
} catch (e) {
res.status(500).json({ error: 'Parse error' });
}
} else {
res.status(500).json({ error: 'Parse error' });
}
});
});
app.get('/api/cities/:countryCode', (req, res) => {
const countryCode = req.params.countryCode;
const citiesPath = path.join(__dirname, 'public', 'data', 'cities', `${countryCode}.js`);
const majorCitiesPath = path.join(__dirname, 'public', 'data', 'cities', 'major.js');
const filePath = fs.existsSync(citiesPath) ? citiesPath : (fs.existsSync(majorCitiesPath) ? majorCitiesPath : null);
if (!filePath) {
return res.json({ cities: [], popular: [], countryCode });
}
try {
const data = fs.readFileSync(filePath, 'utf8');
let cities = [];
const arrayMatch = data.match(/const\s+CITIES_\w+\s*=\s*(\[.*?\]);/s);
if (arrayMatch) {
cities = JSON.parse(arrayMatch[1].replace(/"/g, '"').replace(/'/g, "'"));
}
if (cities.length === 0) {
const majorMatch = data.match(/CITIES_BY_CODE\s*=\s*({[\s\S]*?});/);
if (majorMatch) {
const codeMatch = majorMatch[1].match(new RegExp(`${countryCode}:\\s*\\[([^\\]]+)\\]`));
if (codeMatch) {
cities = codeMatch[1].split(',').map(c => c.trim().replace(/^["']|["']$/g, ''));
}
}
}
if (cities.length === 0) {
const defaultMatch = data.match(/CITIES_DEFAULT\s*=\s*(\[.*?\]);/s);
if (defaultMatch) {
try {
const evalResult = eval(defaultMatch[1]);
if (Array.isArray(evalResult)) {
cities = evalResult;
}
} catch (e) {}
}
}
res.json({ cities, popular: [], countryCode });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));