Major architectural improvements: - Created models/services layer for better separation of concerns - Location model with async methods for database operations - ProfanityWord model for content moderation - DatabaseService for centralized database management - ProfanityFilterService refactored to use models - Refactored frontend map implementations to share common code - MapBase class extracts 60-70% of duplicate functionality - Refactored implementations extend MapBase for specific features - Maintained unique geocoding capabilities per implementation - Updated server.js to use new service architecture - All routes now use async/await with models instead of raw queries - Enhanced error handling and maintainability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
97 lines
No EOL
3.9 KiB
JavaScript
97 lines
No EOL
3.9 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
|
|
module.exports = (locationModel, profanityFilter) => {
|
|
// Get all active locations (within 48 hours OR persistent)
|
|
router.get('/', async (req, res) => {
|
|
console.log('Fetching active locations');
|
|
|
|
try {
|
|
const locations = await locationModel.getActive();
|
|
console.log(`Fetched ${locations.length} active locations (including persistent)`);
|
|
res.json(locations);
|
|
} catch (err) {
|
|
console.error('Error fetching locations:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// Add a new location
|
|
router.post('/', async (req, res) => {
|
|
const { address, latitude, longitude } = req.body;
|
|
let { description } = req.body;
|
|
console.log(`Attempt to add new location: ${address}`);
|
|
|
|
if (!address) {
|
|
console.warn('Failed to add location: Address is required');
|
|
res.status(400).json({ error: 'Address is required' });
|
|
return;
|
|
}
|
|
|
|
// Check for profanity in description and reject if any is found
|
|
if (description && profanityFilter) {
|
|
try {
|
|
const analysis = profanityFilter.analyzeProfanity(description);
|
|
if (analysis.hasProfanity) {
|
|
console.warn(`Submission rejected due to inappropriate language (${analysis.count} word${analysis.count > 1 ? 's' : ''}, severity: ${analysis.severity}) - Original: "${req.body.description}"`);
|
|
|
|
// Reject any submission with profanity
|
|
const wordText = analysis.count === 1 ? 'word' : 'words';
|
|
const detectedWords = analysis.matches.map(m => m.word).join(', ');
|
|
|
|
return res.status(400).json({
|
|
error: 'Submission rejected',
|
|
message: `Your description contains inappropriate language and cannot be posted. Please revise your description to focus on road conditions and keep it professional.\n\nExample: "Multiple vehicles stuck, black ice present" or "Road very slippery, saw 3 accidents"`,
|
|
details: {
|
|
severity: analysis.severity,
|
|
wordCount: analysis.count,
|
|
detectedCategories: [...new Set(analysis.matches.map(m => m.category))]
|
|
}
|
|
});
|
|
}
|
|
} catch (filterError) {
|
|
console.error('Error checking profanity:', filterError);
|
|
// Continue with original description if filter fails
|
|
}
|
|
}
|
|
|
|
try {
|
|
const newLocation = await locationModel.create({
|
|
address,
|
|
latitude,
|
|
longitude,
|
|
description
|
|
});
|
|
|
|
console.log(`Location added successfully: ${address}`);
|
|
res.json({
|
|
...newLocation,
|
|
created_at: new Date().toISOString()
|
|
});
|
|
} catch (err) {
|
|
console.error('Error inserting location:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// Legacy delete route (keeping for backwards compatibility)
|
|
router.delete('/:id', async (req, res) => {
|
|
const { id } = req.params;
|
|
|
|
try {
|
|
const result = await locationModel.delete(id);
|
|
|
|
if (result.changes === 0) {
|
|
res.status(404).json({ error: 'Location not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ message: 'Location deleted successfully' });
|
|
} catch (err) {
|
|
console.error('Error deleting location:', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
return router;
|
|
}; |