286 lines
9.5 KiB
JavaScript
286 lines
9.5 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`,
|
|
[],
|
|
(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 maxReviews = 8;
|
|
let reviews = rows;
|
|
|
|
if (rows.length > maxReviews) {
|
|
for (let i = reviews.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[reviews[i], reviews[j]] = [reviews[j], reviews[i]];
|
|
}
|
|
reviews = reviews.slice(0, maxReviews);
|
|
}
|
|
|
|
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: reviews, 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 placeholders = countries.map(() => '?').join(',');
|
|
|
|
db.all(`
|
|
SELECT country_code, city, COUNT(*) as count
|
|
FROM reviews
|
|
WHERE is_approved = 1 AND country_code IN (${placeholders})
|
|
GROUP BY country_code, city
|
|
ORDER BY count DESC
|
|
`, countries.map(c => c.country_code), (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 }; |