← 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