Files
hotell777_260507/modules/reviews/index.js
2026-05-10 21:42:31 +05:00

276 lines
9.1 KiB
JavaScript

let db;
let settingsModule;
function init(database, settings) {
db = database;
settingsModule = settings;
}
function getApprovedReviews(req, res) {
const lang = req.query.lang || 'ru';
db.all(
`SELECT id, author_name, country, city, stars, text, created_at
FROM reviews
WHERE is_approved = 1
ORDER BY created_at DESC`,
[],
(err, rows) => {
if (err) {
console.error('Get reviews error:', err);
return res.status(500).json({ error: 'Database error' });
}
rows.forEach(row => {
row.created_at = row.created_at ? new Date(row.created_at).toISOString() : null;
});
const stats = rows.length > 0 ? {
count: rows.length,
avgStars: rows.reduce((sum, r) => sum + r.stars, 0) / rows.length
} : { count: 0, avgStars: 0 };
res.json({ reviews: rows, stats: stats });
}
);
}
function createReview(req, res) {
const { author_name, country_code, country_name, city, stars, text, review_code } = req.body;
const ip = req.ip || req.connection.remoteAddress || 'unknown';
if (!author_name || !country_code || stars === undefined || !text || !review_code) {
return res.status(400).json({ error: 'Missing required fields' });
}
const country = country_name || country_code;
if (author_name.length < 2) {
return res.status(400).json({ error: 'Name must be at least 2 characters' });
}
if (text.length < 20) {
return res.status(400).json({ error: 'Review text must be at least 20 characters' });
}
if (stars < 0 || stars > 5) {
return res.status(400).json({ error: 'Stars must be between 0 and 5' });
}
settingsModule.getReviewCode((err, correctCode) => {
if (err) return res.status(500).json({ error: 'Server error' });
if (review_code !== correctCode) {
return res.status(400).json({ error: 'Invalid review code' });
}
settingsModule.checkIpCooldown(ip, (err, isCooldown) => {
if (err) return res.status(500).json({ error: 'Server error' });
if (isCooldown) {
return res.status(429).json({ error: 'You have recently submitted a review. Please try again later.' });
}
const stmt = db.prepare(
`INSERT INTO reviews (author_name, country, country_code, city, stars, text, review_code, ip_address, is_approved)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0)`
);
stmt.run(author_name, country, country_code, city, parseFloat(stars).toFixed(1), text, review_code, ip, function(err) {
if (err) {
console.error('Create review error:', err);
return res.status(500).json({ error: 'Database error' });
}
res.status(201).json({
message: 'Review submitted for moderation',
id: this.lastID
});
});
stmt.finalize();
});
});
}
function getAllReviews(req, res) {
const filter = req.query.filter || 'all';
let whereClause = '1=1';
if (filter === 'pending') {
whereClause = 'is_approved = 0';
} else if (filter === 'approved') {
whereClause = 'is_approved = 1';
} else if (filter === 'rejected') {
whereClause = 'is_approved = -1';
}
db.all(
`SELECT * FROM reviews WHERE ${whereClause} ORDER BY created_at DESC`,
[],
(err, rows) => {
if (err) {
console.error('Get all reviews error:', err);
return res.status(500).json({ error: 'Database error' });
}
const stats = {
total: rows.length,
pending: rows.filter(r => r.is_approved === 0).length,
approved: rows.filter(r => r.is_approved === 1).length,
rejected: rows.filter(r => r.is_approved === -1).length
};
db.get(`SELECT COUNT(*) as cnt, SUM(CASE WHEN is_approved = 0 THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN is_approved = 1 THEN 1 ELSE 0 END) as approved,
SUM(CASE WHEN is_approved = -1 THEN 1 ELSE 0 END) as rejected
FROM reviews`, [], (err, globalStats) => {
if (!err && globalStats) {
stats.total = globalStats.cnt || 0;
stats.pending = globalStats.pending || 0;
stats.approved = globalStats.approved || 0;
stats.rejected = globalStats.rejected || 0;
}
rows.forEach(row => {
row.created_at = row.created_at ? new Date(row.created_at).toISOString() : null;
});
res.json({ reviews: rows, stats: stats });
});
}
);
}
function approveReview(req, res) {
const reviewId = parseInt(req.params.id);
const { approved } = req.body;
const newStatus = approved ? 1 : -1;
db.run(
`UPDATE reviews SET is_approved = ? WHERE id = ?`,
[newStatus, reviewId],
function(err) {
if (err) {
console.error('Approve review error:', err);
return res.status(500).json({ error: 'Database error' });
}
if (this.changes === 0) {
return res.status(404).json({ error: 'Review not found' });
}
res.json({ message: 'Review status updated', id: reviewId, is_approved: newStatus });
}
);
}
function deleteReview(req, res) {
const reviewId = parseInt(req.params.id);
db.run(`DELETE FROM reviews WHERE id = ?`, [reviewId], function(err) {
if (err) {
console.error('Delete review error:', err);
return res.status(500).json({ error: 'Database error' });
}
if (this.changes === 0) {
return res.status(404).json({ error: 'Review not found' });
}
res.json({ message: 'Review deleted', id: reviewId });
});
}
function getPopularCountries(req, res) {
if (!db) {
return res.status(500).json({ error: 'Database not initialized' });
}
db.all(`
SELECT country_code, COUNT(*) as count
FROM reviews
WHERE is_approved = 1 AND country_code IS NOT NULL AND country_code != ''
GROUP BY country_code
ORDER BY count DESC
LIMIT 20
`, (err, countries) => {
if (err) {
console.error('Get popular countries error:', err);
return res.status(500).json({ error: err.message });
}
if (!countries || countries.length === 0) {
return res.json({ countries: [], cities: {} });
}
const countryCodes = countries.map(c => `'${c.country_code}'`).join(',');
db.all(`
SELECT country_code, city, COUNT(*) as count
FROM reviews
WHERE is_approved = 1 AND country_code IN (${countryCodes})
GROUP BY country_code, city
ORDER BY count DESC
`, (err, cities) => {
if (err) {
console.error('Get popular cities error:', err);
return res.status(500).json({ error: err.message });
}
const citiesByCountry = {};
if (cities) {
cities.forEach(c => {
if (!citiesByCountry[c.country_code]) {
citiesByCountry[c.country_code] = [];
}
citiesByCountry[c.country_code].push(c.city);
});
}
res.json({ countries, cities: citiesByCountry });
});
});
}
function getPopularCitiesByCountry(req, res) {
const countryCode = req.params.countryCode;
if (!db) {
return res.status(500).json({ error: 'Database not initialized' });
}
if (!countryCode) {
return res.json({ popular: [], countryCode: null });
}
db.all(`
SELECT city, COUNT(*) as count
FROM reviews
WHERE is_approved = 1 AND country_code = ?
GROUP BY city
ORDER BY count DESC
`, [countryCode], (err, popularCities) => {
if (err) {
console.error('Get popular cities by country error:', err);
popularCities = [];
}
const popularCityNames = popularCities ? popularCities.map(c => c.city) : [];
res.json({ popular: popularCityNames, countryCode });
});
}
function setupRoutes(app, authenticateToken, requireAdmin) {
app.get('/api/reviews', getApprovedReviews);
app.post('/api/reviews', createReview);
app.get('/api/reviews/popular', getPopularCountries);
app.get('/api/reviews/cities/:countryCode', getPopularCitiesByCountry);
app.get('/api/admin/reviews', authenticateToken, getAllReviews);
app.patch('/api/admin/reviews/:id/approve', authenticateToken, approveReview);
app.delete('/api/admin/reviews/:id', authenticateToken, deleteReview);
}
module.exports = { init, setupRoutes };