← back to Dear Bubbe Admin
backend/routes/alerts.js
445 lines
const express = require('express')
const Alert = require('../models/Alert')
const User = require('../models/User')
const router = express.Router()
// Get all alerts with filtering and pagination
router.get('/', async (req, res) => {
try {
const {
page = 1,
limit = 50,
type,
severity,
status,
timeframe = '24h',
userId,
ip,
sort = '-timestamp'
} = req.query
// Build query
const query = {}
// Time filter
if (timeframe) {
const now = new Date()
let startTime
switch (timeframe) {
case '1h':
startTime = new Date(now - 60 * 60 * 1000)
break
case '24h':
startTime = new Date(now - 24 * 60 * 60 * 1000)
break
case '7d':
startTime = new Date(now - 7 * 24 * 60 * 60 * 1000)
break
case '30d':
startTime = new Date(now - 30 * 24 * 60 * 60 * 1000)
break
default:
startTime = new Date(now - 24 * 60 * 60 * 1000)
}
query.timestamp = { $gte: startTime }
}
if (type) query.type = type
if (severity) query.severity = severity
if (status) query.status = status
if (userId) query['userInfo.userId'] = userId
if (ip) query['userInfo.ip'] = ip
// Execute query
const alerts = await Alert.find(query)
.sort(sort)
.limit(limit * 1)
.skip((page - 1) * limit)
.lean()
const total = await Alert.countDocuments(query)
res.json({
success: true,
alerts,
pagination: {
current: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
})
} catch (error) {
console.error('Error fetching alerts:', error)
res.status(500).json({
success: false,
error: 'Failed to fetch alerts'
})
}
})
// Get alert statistics
router.get('/stats', async (req, res) => {
try {
const { timeframe = '24h' } = req.query
const stats = await Alert.getStats(timeframe)
// Get recent critical alerts
const recentCritical = await Alert.getRecentCritical(10)
// Get top IPs with alerts
const topIPs = await Alert.aggregate([
{
$match: {
timestamp: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
}
},
{
$group: {
_id: '$userInfo.ip',
count: { $sum: 1 },
severities: { $push: '$severity' },
types: { $push: '$type' },
latestAlert: { $max: '$timestamp' }
}
},
{
$sort: { count: -1 }
},
{
$limit: 10
}
])
// Get top users with alerts
const topUsers = await Alert.aggregate([
{
$match: {
timestamp: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
}
},
{
$group: {
_id: '$userInfo.userId',
count: { $sum: 1 },
severities: { $push: '$severity' },
types: { $push: '$type' },
latestAlert: { $max: '$timestamp' }
}
},
{
$sort: { count: -1 }
},
{
$limit: 10
}
])
res.json({
success: true,
timeframe,
stats: stats[0] || {},
recentCritical,
topOffenders: {
byIP: topIPs,
byUser: topUsers
}
})
} catch (error) {
console.error('Error fetching alert stats:', error)
res.status(500).json({
success: false,
error: 'Failed to fetch alert statistics'
})
}
})
// Get specific alert details
router.get('/:id', async (req, res) => {
try {
const alert = await Alert.findById(req.params.id).lean()
if (!alert) {
return res.status(404).json({
success: false,
error: 'Alert not found'
})
}
// Get related alerts from same IP/user
const relatedAlerts = await Alert.find({
$or: [
{ 'userInfo.ip': alert.userInfo.ip },
{ 'userInfo.userId': alert.userInfo.userId }
],
_id: { $ne: alert._id }
})
.sort({ timestamp: -1 })
.limit(10)
.lean()
res.json({
success: true,
alert,
relatedAlerts
})
} catch (error) {
console.error('Error fetching alert details:', error)
res.status(500).json({
success: false,
error: 'Failed to fetch alert details'
})
}
})
// Update alert status
router.put('/:id/status', async (req, res) => {
try {
const { status, reviewNotes, resolution } = req.body
const adminId = req.user.userId
const validStatuses = ['active', 'reviewed', 'resolved', 'false_positive']
if (!validStatuses.includes(status)) {
return res.status(400).json({
success: false,
error: 'Invalid status'
})
}
const alert = await Alert.findById(req.params.id)
if (!alert) {
return res.status(404).json({
success: false,
error: 'Alert not found'
})
}
alert.status = status
alert.reviewInfo = {
reviewedBy: adminId,
reviewedAt: new Date(),
reviewNotes: reviewNotes || '',
resolution: resolution || ''
}
await alert.save()
// Log the action
console.log(`Alert ${alert._id} status changed to ${status} by ${adminId}`)
res.json({
success: true,
alert: alert.getSummary()
})
} catch (error) {
console.error('Error updating alert status:', error)
res.status(500).json({
success: false,
error: 'Failed to update alert status'
})
}
})
// Block user based on alert
router.post('/:id/block-user', async (req, res) => {
try {
const { reason } = req.body
const adminId = req.user.userId
const alert = await Alert.findById(req.params.id)
if (!alert) {
return res.status(404).json({
success: false,
error: 'Alert not found'
})
}
const userId = alert.userInfo.userId
if (!userId) {
return res.status(400).json({
success: false,
error: 'No user associated with this alert'
})
}
// Block the user
const user = await User.findByIdOrCreate(userId)
await user.block(reason || `Blocked due to ${alert.type} violation`, adminId)
// Update alert to mark user as blocked
alert.reviewInfo = {
...alert.reviewInfo,
reviewedBy: adminId,
reviewedAt: new Date(),
reviewNotes: `User blocked: ${reason}`,
resolution: 'user_blocked'
}
alert.status = 'resolved'
await alert.save()
console.log(`User ${userId} blocked by ${adminId} due to alert ${alert._id}`)
res.json({
success: true,
message: 'User blocked successfully',
userId,
alertId: alert._id
})
} catch (error) {
console.error('Error blocking user:', error)
res.status(500).json({
success: false,
error: 'Failed to block user'
})
}
})
// Get alerts by IP address
router.get('/by-ip/:ip', async (req, res) => {
try {
const { ip } = req.params
const { limit = 100, timeframe = '7d' } = req.query
const now = new Date()
let startTime
switch (timeframe) {
case '24h':
startTime = new Date(now - 24 * 60 * 60 * 1000)
break
case '7d':
startTime = new Date(now - 7 * 24 * 60 * 60 * 1000)
break
case '30d':
startTime = new Date(now - 30 * 24 * 60 * 60 * 1000)
break
default:
startTime = new Date(now - 7 * 24 * 60 * 60 * 1000)
}
const alerts = await Alert.find({
'userInfo.ip': ip,
timestamp: { $gte: startTime }
})
.sort({ timestamp: -1 })
.limit(parseInt(limit))
.lean()
const stats = {
total: alerts.length,
bySeverity: alerts.reduce((acc, alert) => {
acc[alert.severity] = (acc[alert.severity] || 0) + 1
return acc
}, {}),
byType: alerts.reduce((acc, alert) => {
acc[alert.type] = (acc[alert.type] || 0) + 1
return acc
}, {}),
timeframe
}
res.json({
success: true,
ip,
alerts,
stats
})
} catch (error) {
console.error('Error fetching alerts by IP:', error)
res.status(500).json({
success: false,
error: 'Failed to fetch alerts by IP'
})
}
})
// Bulk update alerts
router.post('/bulk-update', async (req, res) => {
try {
const { alertIds, action, status, reviewNotes } = req.body
const adminId = req.user.userId
if (!alertIds || !Array.isArray(alertIds) || alertIds.length === 0) {
return res.status(400).json({
success: false,
error: 'Alert IDs required'
})
}
const updateData = {
'reviewInfo.reviewedBy': adminId,
'reviewInfo.reviewedAt': new Date()
}
if (status) updateData.status = status
if (reviewNotes) updateData['reviewInfo.reviewNotes'] = reviewNotes
const result = await Alert.updateMany(
{ _id: { $in: alertIds } },
{ $set: updateData }
)
console.log(`Bulk update: ${result.modifiedCount} alerts updated by ${adminId}`)
res.json({
success: true,
updated: result.modifiedCount,
action,
adminId
})
} catch (error) {
console.error('Error in bulk update:', error)
res.status(500).json({
success: false,
error: 'Failed to update alerts'
})
}
})
// Delete alert (super admin only)
router.delete('/:id', async (req, res) => {
try {
if (req.user.role !== 'super_admin') {
return res.status(403).json({
success: false,
error: 'Insufficient permissions'
})
}
const alert = await Alert.findByIdAndDelete(req.params.id)
if (!alert) {
return res.status(404).json({
success: false,
error: 'Alert not found'
})
}
console.log(`Alert ${req.params.id} deleted by ${req.user.userId}`)
res.json({
success: true,
message: 'Alert deleted successfully'
})
} catch (error) {
console.error('Error deleting alert:', error)
res.status(500).json({
success: false,
error: 'Failed to delete alert'
})
}
})
module.exports = router