[object Object]

← back to Dear Bubbe Admin

initial snapshot — gitify all builds (CLAUDE.md rule 2026-05-06)

4147abcad34df85d5e4be67073022ec9a72004ea · 2026-05-06 10:20:44 -0700 · Steve

Files touched

Diff

commit 4147abcad34df85d5e4be67073022ec9a72004ea
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed May 6 10:20:44 2026 -0700

    initial snapshot — gitify all builds (CLAUDE.md rule 2026-05-06)
---
 .gitignore                                 |   14 +
 backend/auto-poster.js                     |  103 +
 backend/middleware/MessageInterceptor.js   |  272 +++
 backend/models/Alert.js                    |  174 ++
 backend/models/Conversation.js             |  319 +++
 backend/models/Session.js                  |  272 +++
 backend/models/User.js                     |  270 +++
 backend/routes/alerts.js                   |  445 ++++
 backend/routes/auth.js                     |  218 ++
 backend/routes/monitoring.js               |  516 ++++
 backend/routes/reports.js                  |  575 +++++
 backend/routes/scanner.js                  |  113 +
 backend/routes/users.js                    |  476 ++++
 backend/server.js                          |  973 ++++++++
 backend/services/AlertService.js           |  455 ++++
 backend/services/MonitoringService.js      |  544 +++++
 backend/social-post-handler.js             |  233 ++
 check-active-users.js                      |   94 +
 ecosystem.config.js                        |   24 +
 frontend/index.html                        | 2190 +++++++++++++++++
 frontend/package.json                      |   47 +
 frontend/public/index.html                 |   81 +
 frontend/src/App.css                       |  579 +++++
 frontend/src/App.js                        |  261 +++
 frontend/src/components/AlertsPage.js      |  470 ++++
 frontend/src/components/Dashboard.js       |  328 +++
 frontend/src/components/Header.js          |   57 +
 frontend/src/components/LiveAlertModal.js  |  241 ++
 frontend/src/components/Login.js           |  205 ++
 frontend/src/components/MonitoringPage.js  |  380 +++
 frontend/src/components/ReportsPage.js     |  406 ++++
 frontend/src/components/Sidebar.js         |   53 +
 frontend/src/components/UsersPage.js       |  323 +++
 frontend/src/index.js                      |   10 +
 frontend/twitter-monitor.html              |  271 +++
 log-all-users.js                           |  271 +++
 package-lock.json                          | 3512 ++++++++++++++++++++++++++++
 package.json                               |   48 +
 public/favicon.svg                         |    4 +
 public/instagram-1763680784110.png         |  Bin 0 -> 105524 bytes
 public/instagram-1763680819710.png         |  Bin 0 -> 105524 bytes
 public/instagram-1763680822723.png         |  Bin 0 -> 86483 bytes
 public/instagram-1763680825914.png         |  Bin 0 -> 96573 bytes
 public/instagram-1763680829181.png         |  Bin 0 -> 78041 bytes
 public/instagram-1763680832325.png         |  Bin 0 -> 75746 bytes
 public/instagram-1763680835466.png         |  Bin 0 -> 81163 bytes
 public/instagram-1763680838580.png         |  Bin 0 -> 88270 bytes
 public/instagram-1763680841833.png         |  Bin 0 -> 75746 bytes
 public/instagram-1763680844930.png         |  Bin 0 -> 98628 bytes
 public/instagram-1763680848213.png         |  Bin 0 -> 105582 bytes
 public/instagram-1763680895477.png         |  Bin 0 -> 90397 bytes
 public/instagram-1763681128801.png         |  Bin 0 -> 88270 bytes
 public/instagram-1763681171228.png         |  Bin 0 -> 97512 bytes
 public/instagram-1763682870613.png         |  Bin 0 -> 90397 bytes
 public/instagram-1763684539865.png         |  Bin 0 -> 72279 bytes
 public/instagram-1763684730100.png         |  Bin 0 -> 65881 bytes
 public/instagram-1763685242269.png         |  Bin 0 -> 68692 bytes
 public/instagram-1763685682137.png         |  Bin 0 -> 90732 bytes
 public/instagram-1763686094031.png         |  Bin 0 -> 82046 bytes
 public/instagram-caption-1763681171228.txt |    7 +
 public/instagram-caption.txt               |    7 +
 public/instagram-latest-caption.txt        |    7 +
 public/instagram-post.html                 |  112 +
 public/instagram-schedule.json             |   15 +
 public/instagram-webhook.json              |   19 +
 setup-admin.sh                             |  125 +
 66 files changed, 16119 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1d7a585
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+node_modules/
+.next/
+dist/
+build/
+.cache/
+*.db-shm
+*.db-wal
+.env
+.env.local
+coverage/
+.DS_Store
+*.log
+tmp/
+
diff --git a/backend/auto-poster.js b/backend/auto-poster.js
new file mode 100644
index 0000000..b5ed372
--- /dev/null
+++ b/backend/auto-poster.js
@@ -0,0 +1,103 @@
+const fs = require('fs').promises;
+const path = require('path');
+const { postTweet } = require('/root/Projects/dear-bubbe-nextjs/lib/twitter-v2-official.js');
+
+// Format conversation for posting
+function formatForSocial(data) {
+  const intros = [
+    "Bubbe tells it like it is:",
+    "Bubbe's got no filter:",
+    "Bubbe pulls no punches:",
+    "Bubbe serves reality checks:",
+    "Bubbe's straight talk:",
+    "Bubbe's unfiltered truth:",
+    "Bubbe's no-nonsense advice:",
+    "Bubbe's savage advice:",
+    "Bubbe drops truth bombs:",
+    "Bubbe keeps it real:"
+  ];
+  
+  const intro = intros[Math.floor(Math.random() * intros.length)];
+  
+  // Anonymize user message
+  let userMsg = data.userMessage || '';
+  userMsg = userMsg.replace(/\b[A-Z][a-z]+\s+[A-Z][a-z]+\b/g, '[NAME]');
+  userMsg = userMsg.replace(/\b[A-Z][a-z]+\b/g, (match) => {
+    if (!['I', 'The', 'My', 'What', 'When', 'Where', 'Why', 'How'].includes(match)) {
+      return '[NAME]';
+    }
+    return match;
+  });
+  
+  // Truncate messages for Twitter
+  const maxUserLength = 80;
+  const maxBubbeLength = 150;
+  
+  if (userMsg.length > maxUserLength) {
+    userMsg = userMsg.substring(0, maxUserLength) + '...';
+  }
+  
+  let bubbeMsg = data.bubbeResponse || '';
+  if (bubbeMsg.length > maxBubbeLength) {
+    bubbeMsg = bubbeMsg.substring(0, maxBubbeLength) + '...';
+  }
+  
+  const hashtags = '#BubbeAI #Bubbe #NoFilter #JewishGrandma';
+  const cta = 'Visit Bubbe.AI';
+  
+  const fullPost = `${intro}\n\n"${userMsg}"\n\n"${bubbeMsg}" ${hashtags}\n\n${cta}`;
+  
+  // Ensure it fits in Twitter's limit
+  if (fullPost.length > 280) {
+    const shortened = `${intro}\n\n"${bubbeMsg.substring(0, 120)}..." ${hashtags}\n\n${cta}`;
+    return shortened;
+  }
+  
+  return fullPost;
+}
+
+// Post immediately to Twitter
+async function postImmediately(data) {
+  try {
+    const text = formatForSocial(data);
+    console.log('📤 Posting to Twitter immediately...');
+    
+    const result = await postTweet(text);
+    
+    if (result.success) {
+      // Log successful post
+      const logsPath = '/root/Projects/dear-bubbe-admin/logs/immediate-posts.json';
+      let logs = [];
+      try {
+        const existing = await fs.readFile(logsPath, 'utf8');
+        logs = JSON.parse(existing);
+      } catch (e) {
+        // File doesn't exist yet
+      }
+      
+      logs.push({
+        timestamp: new Date().toISOString(),
+        platform: 'twitter',
+        status: 'posted',
+        tweetId: result.tweetId,
+        url: `https://x.com/DearBubbe/status/${result.tweetId}`,
+        text: text,
+        originalData: data
+      });
+      
+      await fs.writeFile(logsPath, JSON.stringify(logs, null, 2));
+      console.log('✅ Posted successfully to Twitter!');
+      console.log('URL:', `https://x.com/DearBubbe/status/${result.tweetId}`);
+      
+      return { success: true, tweetId: result.tweetId };
+    } else {
+      console.error('❌ Failed to post:', result.error);
+      return { success: false, error: result.error };
+    }
+  } catch (error) {
+    console.error('❌ Error posting:', error);
+    return { success: false, error: error.message };
+  }
+}
+
+module.exports = { postImmediately, formatForSocial };
\ No newline at end of file
diff --git a/backend/middleware/MessageInterceptor.js b/backend/middleware/MessageInterceptor.js
new file mode 100644
index 0000000..711d11b
--- /dev/null
+++ b/backend/middleware/MessageInterceptor.js
@@ -0,0 +1,272 @@
+const axios = require('axios')
+const AlertService = require('../services/AlertService')
+
+/**
+ * Message Interceptor Middleware for Dear Bubbe AI
+ * 
+ * This middleware can be integrated into the main Dear Bubbe application
+ * to automatically process all messages through the admin alert system
+ */
+class MessageInterceptor {
+  constructor(alertService) {
+    this.alertService = alertService || new AlertService(null, console)
+    this.processedMessages = new Set() // Prevent duplicate processing
+    this.apiEndpoint = process.env.ADMIN_API_URL || 'http://localhost:3015'
+  }
+
+  /**
+   * Process a message and check for violations
+   * This should be called from the Dear Bubbe chat API
+   */
+  async processMessage(messageData) {
+    try {
+      const {
+        message,
+        userId,
+        sessionId,
+        ip,
+        userAgent,
+        timestamp,
+        userProfile
+      } = messageData
+
+      // Create unique message ID to prevent duplicate processing
+      const messageId = this.createMessageId(message, userId, timestamp)
+      if (this.processedMessages.has(messageId)) {
+        return { processed: true, violations: [] }
+      }
+
+      console.log(`[INTERCEPTOR] Processing message from user ${userId}`)
+
+      // Prepare user context
+      const userContext = {
+        userId: userId || 'unknown',
+        ip: ip || 'unknown',
+        sessionId: sessionId || this.generateSessionId(),
+        userAgent: userAgent || 'unknown',
+        userProfile: userProfile || {}
+      }
+
+      // Process through alert service
+      const result = await this.alertService.processMessage(message, userContext)
+
+      // Mark as processed
+      this.processedMessages.add(messageId)
+      
+      // Clean up old processed messages (keep last 1000)
+      if (this.processedMessages.size > 1000) {
+        const messagesArray = Array.from(this.processedMessages)
+        this.processedMessages.clear()
+        messagesArray.slice(-500).forEach(id => this.processedMessages.add(id))
+      }
+
+      console.log(`[INTERCEPTOR] Message processed: ${result.violations.length} violations found`)
+
+      return {
+        processed: true,
+        blocked: result.blocked,
+        violations: result.violations,
+        alertsCreated: result.alertsCreated,
+        shouldBlock: result.blocked,
+        threatResponse: result.violations.some(v => v.severity === 'high' || v.severity === 'critical')
+      }
+
+    } catch (error) {
+      console.error('[INTERCEPTOR] Error processing message:', error)
+      return {
+        processed: false,
+        error: error.message,
+        violations: [],
+        alertsCreated: 0
+      }
+    }
+  }
+
+  /**
+   * Express middleware function for automatic message processing
+   * Add this to the Dear Bubbe API routes
+   */
+  middleware() {
+    return async (req, res, next) => {
+      try {
+        // Only process chat messages
+        if (req.path !== '/api/chat' || req.method !== 'POST') {
+          return next()
+        }
+
+        const { message, userId } = req.body
+        if (!message || message === '__init__') {
+          return next() // Skip init messages
+        }
+
+        // Extract request context
+        const messageData = {
+          message: message,
+          userId: userId || 'anonymous',
+          sessionId: req.sessionId || req.sessionID,
+          ip: this.extractIP(req),
+          userAgent: req.get('User-Agent'),
+          timestamp: new Date(),
+          userProfile: req.body.userProfile || {}
+        }
+
+        // Process the message
+        const result = await this.processMessage(messageData)
+
+        // Add result to request for use by the main handler
+        req.securityScan = result
+
+        // If message should be blocked, return early
+        if (result.blocked) {
+          console.log(`[INTERCEPTOR] Blocking message from ${userId}: ${result.violations.length} violations`)
+          return res.status(400).json({
+            error: 'Message blocked due to policy violation',
+            blocked: true,
+            violations: result.violations.map(v => ({
+              type: v.type,
+              severity: v.severity,
+              category: v.category || v.type
+            }))
+          })
+        }
+
+        // Continue to main handler
+        next()
+
+      } catch (error) {
+        console.error('[INTERCEPTOR] Middleware error:', error)
+        // Don't block the request on errors, just log
+        req.securityScan = { 
+          processed: false, 
+          error: error.message 
+        }
+        next()
+      }
+    }
+  }
+
+  /**
+   * Webhook endpoint for real-time message processing
+   * Call this from Dear Bubbe application
+   */
+  async webhook(req, res) {
+    try {
+      const messageData = req.body
+      const result = await this.processMessage(messageData)
+      
+      res.json({
+        success: true,
+        result
+      })
+    } catch (error) {
+      console.error('[INTERCEPTOR] Webhook error:', error)
+      res.status(500).json({
+        success: false,
+        error: error.message
+      })
+    }
+  }
+
+  /**
+   * Integration helper for Dear Bubbe
+   */
+  static createIntegrationCode() {
+    return `
+// Add this to your Dear Bubbe chat API route (app/api/chat/route.ts)
+
+// Import the admin integration
+const axios = require('axios')
+
+// Function to scan message for violations
+async function scanMessageForViolations(messageData) {
+  try {
+    const response = await axios.post('http://localhost:3015/api/scan-message', {
+      message: messageData.message,
+      userId: messageData.userId,
+      sessionId: messageData.sessionId,
+      ip: messageData.ip,
+      userAgent: messageData.userAgent,
+      userProfile: messageData.userProfile
+    }, {
+      timeout: 5000 // Don't wait too long
+    })
+    
+    return response.data.result || {}
+  } catch (error) {
+    console.warn('Admin scan failed:', error.message)
+    return { processed: false, violations: [] }
+  }
+}
+
+// In your chat handler, add this before processing:
+export async function POST(request: NextRequest) {
+  try {
+    const body = await request.json()
+    const { message, userId, userProfile } = body
+    
+    // Skip scanning for init messages
+    if (message !== '__init__') {
+      // Scan for violations
+      const scanResult = await scanMessageForViolations({
+        message,
+        userId: userId || 'anonymous',
+        sessionId: request.headers.get('x-session-id'),
+        ip: request.headers.get('x-forwarded-for')?.split(',')[0] || 
+            request.headers.get('x-real-ip') || 'unknown',
+        userAgent: request.headers.get('user-agent'),
+        userProfile
+      })
+      
+      // Block if necessary
+      if (scanResult.blocked) {
+        return NextResponse.json({
+          error: 'Message blocked due to policy violation',
+          blocked: true,
+          threatResponse: scanResult.threatResponse
+        }, { status: 400 })
+      }
+      
+      // Log violations for monitoring
+      if (scanResult.violations?.length > 0) {
+        console.warn(\`🚨 Security violations detected: \${scanResult.violations.length}\`)
+      }
+    }
+    
+    // Continue with normal processing...
+    
+  } catch (error) {
+    // Handle errors...
+  }
+}
+`
+  }
+
+  // Helper methods
+  createMessageId(message, userId, timestamp) {
+    const content = `${message}-${userId}-${timestamp.getTime()}`
+    return require('crypto').createHash('md5').update(content).digest('hex')
+  }
+
+  generateSessionId() {
+    return 'sess_' + Math.random().toString(36).substr(2, 9)
+  }
+
+  extractIP(req) {
+    const forwarded = req.headers['x-forwarded-for']
+    const realIp = req.headers['x-real-ip']
+    const connectionRemote = req.connection?.remoteAddress
+    const socketRemote = req.socket?.remoteAddress
+    const connectionSocket = req.connection?.socket?.remoteAddress
+    
+    const ip = forwarded?.split(',')[0] || 
+               realIp || 
+               connectionRemote || 
+               socketRemote || 
+               connectionSocket || 
+               'unknown'
+    
+    return ip.replace('::ffff:', '') // Clean IPv6 mapped IPv4
+  }
+}
+
+module.exports = MessageInterceptor
\ No newline at end of file
diff --git a/backend/models/Alert.js b/backend/models/Alert.js
new file mode 100644
index 0000000..221f437
--- /dev/null
+++ b/backend/models/Alert.js
@@ -0,0 +1,174 @@
+const mongoose = require('mongoose')
+
+const alertSchema = new mongoose.Schema({
+  timestamp: {
+    type: Date,
+    default: Date.now,
+    index: true
+  },
+  type: {
+    type: String,
+    required: true,
+    enum: ['antisemitism', 'racism', 'hate_speech', 'harassment', 'spam', 'system', 'security'],
+    index: true
+  },
+  severity: {
+    type: String,
+    required: true,
+    enum: ['low', 'medium', 'high', 'critical'],
+    index: true
+  },
+  userInfo: {
+    userId: { type: String, index: true },
+    ip: { type: String, index: true },
+    sessionId: { type: String },
+    userAgent: { type: String },
+    email: { type: String }
+  },
+  location: {
+    country: { type: String },
+    state: { type: String },
+    city: { type: String },
+    coordinates: {
+      lat: { type: Number },
+      lon: { type: Number }
+    },
+    isp: { type: String }
+  },
+  content: {
+    message: { type: String, required: true },
+    detectedAt: { type: Date, default: Date.now },
+    context: { type: String }, // Additional context about the conversation
+    responseGiven: { type: String } // Bubbe's response if any
+  },
+  violation: {
+    ruleId: { type: String, required: true },
+    pattern: { type: String, required: true },
+    match: { type: String, required: true }, // The exact text that matched
+    category: { type: String }, // e.g., 'racial_slur', 'conspiracy', 'stereotype'
+    confidence: { type: Number, min: 0, max: 1, default: 1 }
+  },
+  action: {
+    type: String,
+    enum: ['block', 'review', 'warn', 'monitor'],
+    default: 'review'
+  },
+  status: {
+    type: String,
+    enum: ['active', 'reviewed', 'resolved', 'false_positive'],
+    default: 'active',
+    index: true
+  },
+  reviewInfo: {
+    reviewedBy: { type: String }, // Admin user ID
+    reviewedAt: { type: Date },
+    reviewNotes: { type: String },
+    resolution: { type: String }
+  },
+  threatResponseSent: {
+    type: Boolean,
+    default: false
+  },
+  escalated: {
+    type: Boolean,
+    default: false
+  },
+  reportedToAuthorities: {
+    type: Boolean,
+    default: false
+  },
+  additionalData: {
+    type: mongoose.Schema.Types.Mixed // For any additional data specific to alert type
+  }
+}, {
+  timestamps: true
+})
+
+// Indexes for fast queries
+alertSchema.index({ timestamp: -1 })
+alertSchema.index({ 'userInfo.ip': 1, timestamp: -1 })
+alertSchema.index({ type: 1, severity: 1 })
+alertSchema.index({ status: 1, timestamp: -1 })
+
+// Method to get alert summary
+alertSchema.methods.getSummary = function() {
+  return {
+    id: this._id,
+    type: this.type,
+    severity: this.severity,
+    timestamp: this.timestamp,
+    userInfo: {
+      userId: this.userInfo.userId,
+      ip: this.userInfo.ip
+    },
+    location: this.location,
+    violation: {
+      category: this.violation.category,
+      match: this.violation.match
+    },
+    status: this.status,
+    threatResponseSent: this.threatResponseSent
+  }
+}
+
+// Static method to get recent high-severity alerts
+alertSchema.statics.getRecentCritical = function(limit = 10) {
+  return this.find({
+    severity: { $in: ['high', 'critical'] },
+    timestamp: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) } // Last 24 hours
+  })
+  .sort({ timestamp: -1 })
+  .limit(limit)
+  .lean()
+}
+
+// Static method to get alert statistics
+alertSchema.statics.getStats = function(timeframe = '24h') {
+  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)
+  }
+  
+  return this.aggregate([
+    {
+      $match: {
+        timestamp: { $gte: startTime }
+      }
+    },
+    {
+      $group: {
+        _id: {
+          type: '$type',
+          severity: '$severity'
+        },
+        count: { $sum: 1 },
+        latestAlert: { $max: '$timestamp' },
+        uniqueIPs: { $addToSet: '$userInfo.ip' },
+        uniqueUsers: { $addToSet: '$userInfo.userId' }
+      }
+    },
+    {
+      $addFields: {
+        uniqueIPCount: { $size: '$uniqueIPs' },
+        uniqueUserCount: { $size: '$uniqueUsers' }
+      }
+    }
+  ])
+}
+
+module.exports = mongoose.model('Alert', alertSchema)
\ No newline at end of file
diff --git a/backend/models/Conversation.js b/backend/models/Conversation.js
new file mode 100644
index 0000000..9ff801f
--- /dev/null
+++ b/backend/models/Conversation.js
@@ -0,0 +1,319 @@
+const mongoose = require('mongoose')
+
+const conversationSchema = new mongoose.Schema({
+  userId: {
+    type: String,
+    required: true,
+    index: true
+  },
+  sessionId: {
+    type: String,
+    required: true,
+    index: true
+  },
+  timestamp: {
+    type: Date,
+    default: Date.now,
+    index: true
+  },
+  message: {
+    text: { type: String, required: true },
+    length: { type: Number },
+    language: { type: String, default: 'en' },
+    sentiment: { 
+      type: String, 
+      enum: ['positive', 'neutral', 'negative'], 
+      default: 'neutral' 
+    },
+    toxicity: {
+      score: { type: Number, min: 0, max: 1, default: 0 },
+      categories: [{ type: String }] // hate, insult, threat, etc.
+    }
+  },
+  response: {
+    text: { type: String, required: true },
+    mode: { type: String, default: 'bubbe' }, // bubbe, meshugana, comedian
+    length: { type: Number },
+    generationTime: { type: Number }, // in ms
+    model: { type: String, default: 'claude-3-haiku' },
+    voiceGenerated: { type: Boolean, default: false },
+    voiceUrl: { type: String }
+  },
+  context: {
+    conversationNumber: { type: Number, default: 1 },
+    timeSinceLastMessage: { type: Number }, // in seconds
+    previousTopic: { type: String },
+    userProfile: { type: mongoose.Schema.Types.Mixed }, // Snapshot of user profile
+    location: { type: mongoose.Schema.Types.Mixed }
+  },
+  topics: [{ type: String }], // Extracted topics like 'marriage', 'career', 'food'
+  
+  analysis: {
+    responseTime: { type: Number }, // in ms
+    userEngagement: { 
+      type: String, 
+      enum: ['low', 'medium', 'high'], 
+      default: 'medium' 
+    },
+    topicShift: { type: Boolean, default: false },
+    escalation: { type: Boolean, default: false }, // If conversation became heated
+    satisfaction: { 
+      type: String, 
+      enum: ['satisfied', 'neutral', 'frustrated'], 
+      default: 'neutral' 
+    }
+  },
+  
+  flags: {
+    flaggedContent: { type: Boolean, default: false },
+    flagReason: [{ type: String }], // antisemitism, racism, harassment, etc.
+    alertsTriggered: { type: Number, default: 0 },
+    reviewed: { type: Boolean, default: false },
+    reviewedBy: { type: String }, // Admin user ID
+    reviewedAt: { type: Date },
+    reviewNotes: { type: String }
+  },
+  
+  technicalData: {
+    ip: { type: String, index: true },
+    userAgent: { type: String },
+    referer: { type: String },
+    apiEndpoint: { type: String },
+    requestTime: { type: Number },
+    errors: [{
+      timestamp: { type: Date },
+      error: { type: String },
+      code: { type: String }
+    }]
+  }
+}, {
+  timestamps: true
+})
+
+// Indexes for performance
+conversationSchema.index({ timestamp: -1 })
+conversationSchema.index({ userId: 1, timestamp: -1 })
+conversationSchema.index({ sessionId: 1, 'context.conversationNumber': 1 })
+conversationSchema.index({ 'flags.flaggedContent': 1, timestamp: -1 })
+conversationSchema.index({ topics: 1 })
+
+// Pre-save middleware to extract topics and analyze content
+conversationSchema.pre('save', function(next) {
+  // Calculate message length
+  if (this.message.text) {
+    this.message.length = this.message.text.length
+  }
+  
+  if (this.response.text) {
+    this.response.length = this.response.text.length
+  }
+  
+  // Extract topics from message and response text
+  if (!this.topics || this.topics.length === 0) {
+    this.topics = this.extractTopics(this.message.text + ' ' + this.response.text)
+  }
+  
+  // Analyze sentiment
+  this.message.sentiment = this.analyzeSentiment(this.message.text)
+  
+  next()
+})
+
+// Method to extract topics from text
+conversationSchema.methods.extractTopics = function(text) {
+  const topics = []
+  const lowerText = text.toLowerCase()
+  
+  const topicPatterns = [
+    { pattern: /\b(marriage|wedding|engaged|spouse|husband|wife|marry)\b/gi, topic: 'marriage' },
+    { pattern: /\b(kids?|children|baby|babies|pregnant|family)\b/gi, topic: 'children' },
+    { pattern: /\b(job|work|career|salary|boss|company|employment)\b/gi, topic: 'career' },
+    { pattern: /\b(money|income|salary|debt|savings|rent|cost|expensive)\b/gi, topic: 'finances' },
+    { pattern: /\b(school|university|college|degree|education|student)\b/gi, topic: 'education' },
+    { pattern: /\b(health|sick|doctor|medicine|hospital|diet)\b/gi, topic: 'health' },
+    { pattern: /\b(food|cooking|eating|restaurant|recipe)\b/gi, topic: 'food' },
+    { pattern: /\b(dating|relationship|boyfriend|girlfriend|single|love)\b/gi, topic: 'relationships' },
+    { pattern: /\b(jewish|torah|synagogue|shabbat|kosher|yiddish)\b/gi, topic: 'religion' },
+    { pattern: /\b(news|israel|politics|election|trump|biden)\b/gi, topic: 'current_events' },
+    { pattern: /\b(weather|temperature|rain|snow|storm|forecast)\b/gi, topic: 'weather' },
+    { pattern: /\b(sports|game|team|basketball|football|baseball)\b/gi, topic: 'sports' },
+    { pattern: /\b(travel|vacation|trip|hotel|flight)\b/gi, topic: 'travel' },
+    { pattern: /\b(house|apartment|rent|buy|move|neighborhood)\b/gi, topic: 'housing' }
+  ]
+  
+  topicPatterns.forEach(({ pattern, topic }) => {
+    if (pattern.test(lowerText) && !topics.includes(topic)) {
+      topics.push(topic)
+    }
+  })
+  
+  return topics
+}
+
+// Method to analyze sentiment
+conversationSchema.methods.analyzeSentiment = function(text) {
+  const positiveWords = [
+    'happy', 'great', 'good', 'excellent', 'wonderful', 'amazing', 'love', 'fantastic',
+    'awesome', 'perfect', 'beautiful', 'brilliant', 'outstanding', 'magnificent'
+  ]
+  
+  const negativeWords = [
+    'sad', 'bad', 'terrible', 'awful', 'hate', 'horrible', 'disgusting', 'worst',
+    'annoying', 'frustrated', 'angry', 'disappointed', 'upset', 'depressed'
+  ]
+  
+  const lowerText = text.toLowerCase()
+  
+  let positiveScore = 0
+  let negativeScore = 0
+  
+  positiveWords.forEach(word => {
+    const regex = new RegExp(`\\b${word}\\b`, 'gi')
+    const matches = (lowerText.match(regex) || []).length
+    positiveScore += matches
+  })
+  
+  negativeWords.forEach(word => {
+    const regex = new RegExp(`\\b${word}\\b`, 'gi')
+    const matches = (lowerText.match(regex) || []).length
+    negativeScore += matches
+  })
+  
+  if (positiveScore > negativeScore) return 'positive'
+  if (negativeScore > positiveScore) return 'negative'
+  return 'neutral'
+}
+
+// Method to flag conversation
+conversationSchema.methods.flagContent = function(reasons, adminId = null) {
+  this.flags.flaggedContent = true
+  this.flags.flagReason = Array.isArray(reasons) ? reasons : [reasons]
+  this.flags.alertsTriggered++
+  
+  if (adminId) {
+    this.flags.reviewedBy = adminId
+    this.flags.reviewedAt = new Date()
+    this.flags.reviewed = true
+  }
+  
+  return this.save()
+}
+
+// Static method to get conversation statistics
+conversationSchema.statics.getStats = function(timeframe = '24h') {
+  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)
+  }
+  
+  return this.aggregate([
+    {
+      $match: {
+        timestamp: { $gte: startTime }
+      }
+    },
+    {
+      $group: {
+        _id: null,
+        totalConversations: { $sum: 1 },
+        uniqueUsers: { $addToSet: '$userId' },
+        averageResponseTime: { $avg: '$analysis.responseTime' },
+        topicDistribution: { $push: '$topics' },
+        sentimentDistribution: {
+          positive: { $sum: { $cond: [{ $eq: ['$message.sentiment', 'positive'] }, 1, 0] } },
+          neutral: { $sum: { $cond: [{ $eq: ['$message.sentiment', 'neutral'] }, 1, 0] } },
+          negative: { $sum: { $cond: [{ $eq: ['$message.sentiment', 'negative'] }, 1, 0] } }
+        },
+        modeDistribution: {
+          bubbe: { $sum: { $cond: [{ $eq: ['$response.mode', 'bubbe'] }, 1, 0] } },
+          meshugana: { $sum: { $cond: [{ $eq: ['$response.mode', 'meshugana'] }, 1, 0] } },
+          comedian: { $sum: { $cond: [{ $eq: ['$response.mode', 'comedian'] }, 1, 0] } }
+        },
+        flaggedConversations: { $sum: { $cond: ['$flags.flaggedContent', 1, 0] } },
+        voiceUsage: { $sum: { $cond: ['$response.voiceGenerated', 1, 0] } }
+      }
+    },
+    {
+      $addFields: {
+        uniqueUserCount: { $size: '$uniqueUsers' }
+      }
+    }
+  ])
+}
+
+// Static method to get top topics
+conversationSchema.statics.getTopTopics = function(limit = 10, timeframe = '24h') {
+  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
+    default:
+      startTime = new Date(now - 24 * 60 * 60 * 1000)
+  }
+  
+  return this.aggregate([
+    {
+      $match: {
+        timestamp: { $gte: startTime }
+      }
+    },
+    {
+      $unwind: '$topics'
+    },
+    {
+      $group: {
+        _id: '$topics',
+        count: { $sum: 1 },
+        uniqueUsers: { $addToSet: '$userId' }
+      }
+    },
+    {
+      $addFields: {
+        uniqueUserCount: { $size: '$uniqueUsers' }
+      }
+    },
+    {
+      $sort: { count: -1 }
+    },
+    {
+      $limit: limit
+    }
+  ])
+}
+
+// Static method to get flagged conversations
+conversationSchema.statics.getFlagged = function(limit = 50) {
+  return this.find({
+    'flags.flaggedContent': true
+  })
+  .sort({ timestamp: -1 })
+  .limit(limit)
+  .populate('userId', 'profile.name profile.preferredName')
+  .lean()
+}
+
+module.exports = mongoose.model('Conversation', conversationSchema)
\ No newline at end of file
diff --git a/backend/models/Session.js b/backend/models/Session.js
new file mode 100644
index 0000000..b9a9d68
--- /dev/null
+++ b/backend/models/Session.js
@@ -0,0 +1,272 @@
+const mongoose = require('mongoose')
+
+const sessionSchema = new mongoose.Schema({
+  sessionId: {
+    type: String,
+    required: true,
+    unique: true,
+    index: true
+  },
+  userId: {
+    type: String,
+    required: true,
+    index: true
+  },
+  startTime: {
+    type: Date,
+    default: Date.now,
+    index: true
+  },
+  endTime: {
+    type: Date
+  },
+  lastActivity: {
+    type: Date,
+    default: Date.now,
+    index: true
+  },
+  duration: {
+    type: Number, // in seconds
+    default: 0
+  },
+  ended: {
+    type: Boolean,
+    default: false
+  },
+  userInfo: {
+    ip: { type: String, index: true },
+    userAgent: { type: String },
+    browser: { type: String },
+    os: { type: String },
+    deviceType: { type: String } // mobile, tablet, desktop
+  },
+  location: {
+    country: { type: String, index: true },
+    state: { type: String, index: true },
+    city: { type: String },
+    coordinates: {
+      lat: { type: Number },
+      lon: { type: Number }
+    },
+    timezone: { type: String },
+    isp: { type: String }
+  },
+  activity: {
+    messageCount: { type: Number, default: 0 },
+    voiceMessagesUsed: { type: Number, default: 0 },
+    modesUsed: [{ type: String }], // bubbe, meshugana, comedian
+    topicsDiscussed: [{ type: String }],
+    featuresUsed: [{ type: String }], // news, weather, voice, etc.
+    errorsEncountered: { type: Number, default: 0 },
+    averageResponseTime: { type: Number } // in ms
+  },
+  flags: {
+    suspicious: { type: Boolean, default: false },
+    blocked: { type: Boolean, default: false },
+    alertsTriggered: { type: Number, default: 0 },
+    riskLevel: { type: String, enum: ['low', 'medium', 'high', 'critical'], default: 'low' }
+  },
+  referrer: {
+    source: { type: String }, // direct, google, social, etc.
+    campaign: { type: String },
+    medium: { type: String }
+  },
+  technicalData: {
+    initialLoadTime: { type: Number },
+    averageApiResponseTime: { type: Number },
+    errors: [{
+      timestamp: { type: Date, default: Date.now },
+      error: { type: String },
+      context: { type: String }
+    }],
+    performanceMetrics: {
+      pageLoadTime: { type: Number },
+      apiCallCount: { type: Number },
+      totalDataTransferred: { type: Number } // in bytes
+    }
+  }
+}, {
+  timestamps: true
+})
+
+// Indexes for performance
+sessionSchema.index({ startTime: -1 })
+sessionSchema.index({ 'userInfo.ip': 1, startTime: -1 })
+sessionSchema.index({ ended: 1, lastActivity: -1 })
+sessionSchema.index({ userId: 1, startTime: -1 })
+
+// Virtual for session status
+sessionSchema.virtual('status').get(function() {
+  if (this.ended) return 'ended'
+  
+  const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)
+  if (this.lastActivity < fiveMinutesAgo) return 'inactive'
+  
+  return 'active'
+})
+
+// Method to update activity
+sessionSchema.methods.updateActivity = function(activityData = {}) {
+  this.lastActivity = new Date()
+  
+  if (activityData.messageCount) {
+    this.activity.messageCount += activityData.messageCount
+  }
+  
+  if (activityData.mode && !this.activity.modesUsed.includes(activityData.mode)) {
+    this.activity.modesUsed.push(activityData.mode)
+  }
+  
+  if (activityData.topics) {
+    activityData.topics.forEach(topic => {
+      if (!this.activity.topicsDiscussed.includes(topic)) {
+        this.activity.topicsDiscussed.push(topic)
+      }
+    })
+  }
+  
+  if (activityData.feature && !this.activity.featuresUsed.includes(activityData.feature)) {
+    this.activity.featuresUsed.push(activityData.feature)
+  }
+  
+  if (activityData.responseTime) {
+    if (this.activity.averageResponseTime) {
+      this.activity.averageResponseTime = (this.activity.averageResponseTime + activityData.responseTime) / 2
+    } else {
+      this.activity.averageResponseTime = activityData.responseTime
+    }
+  }
+  
+  return this.save()
+}
+
+// Method to end session
+sessionSchema.methods.endSession = function() {
+  this.ended = true
+  this.endTime = new Date()
+  this.duration = Math.round((this.endTime - this.startTime) / 1000) // in seconds
+  return this.save()
+}
+
+// Method to add error
+sessionSchema.methods.addError = function(error, context = '') {
+  this.activity.errorsEncountered++
+  this.technicalData.errors.push({
+    error,
+    context,
+    timestamp: new Date()
+  })
+  return this.save()
+}
+
+// Method to flag as suspicious
+sessionSchema.methods.flagSuspicious = function(reason) {
+  this.flags.suspicious = true
+  this.flags.riskLevel = 'high'
+  return this.save()
+}
+
+// Method to increment alert count
+sessionSchema.methods.addAlert = function(alertType, severity) {
+  this.flags.alertsTriggered++
+  
+  // Update risk level based on alerts and severity
+  if (this.flags.alertsTriggered >= 3 || severity === 'critical') {
+    this.flags.riskLevel = 'critical'
+  } else if (this.flags.alertsTriggered >= 2 || severity === 'high') {
+    this.flags.riskLevel = 'high'
+  } else if (this.flags.alertsTriggered >= 1 || severity === 'medium') {
+    this.flags.riskLevel = 'medium'
+  }
+  
+  return this.save()
+}
+
+// Static method to get active sessions
+sessionSchema.statics.getActiveSessions = function(minutesThreshold = 5) {
+  const threshold = new Date(Date.now() - minutesThreshold * 60 * 1000)
+  return this.find({
+    ended: false,
+    lastActivity: { $gte: threshold }
+  }).lean()
+}
+
+// Static method to get session statistics
+sessionSchema.statics.getStats = function(timeframe = '24h') {
+  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)
+  }
+  
+  return this.aggregate([
+    {
+      $match: {
+        startTime: { $gte: startTime }
+      }
+    },
+    {
+      $group: {
+        _id: null,
+        totalSessions: { $sum: 1 },
+        uniqueUsers: { $addToSet: '$userId' },
+        uniqueIPs: { $addToSet: '$userInfo.ip' },
+        totalMessages: { $sum: '$activity.messageCount' },
+        averageDuration: { $avg: '$duration' },
+        flaggedSessions: { $sum: { $cond: ['$flags.suspicious', 1, 0] } },
+        totalErrors: { $sum: '$activity.errorsEncountered' },
+        averageResponseTime: { $avg: '$activity.averageResponseTime' }
+      }
+    },
+    {
+      $addFields: {
+        uniqueUserCount: { $size: '$uniqueUsers' },
+        uniqueIPCount: { $size: '$uniqueIPs' }
+      }
+    }
+  ])
+}
+
+// Static method to find or create session
+sessionSchema.statics.findOrCreate = function(sessionId, userId, sessionData = {}) {
+  return this.findOneAndUpdate(
+    { sessionId },
+    {
+      $setOnInsert: {
+        sessionId,
+        userId,
+        startTime: new Date(),
+        ...sessionData
+      }
+    },
+    {
+      upsert: true,
+      new: true,
+      runValidators: true
+    }
+  )
+}
+
+// Static method to cleanup old sessions
+sessionSchema.statics.cleanup = function(daysToKeep = 30) {
+  const cutoff = new Date(Date.now() - daysToKeep * 24 * 60 * 60 * 1000)
+  return this.deleteMany({
+    startTime: { $lt: cutoff }
+  })
+}
+
+module.exports = mongoose.model('Session', sessionSchema)
\ No newline at end of file
diff --git a/backend/models/User.js b/backend/models/User.js
new file mode 100644
index 0000000..418f0cf
--- /dev/null
+++ b/backend/models/User.js
@@ -0,0 +1,270 @@
+const mongoose = require('mongoose')
+
+const userSchema = new mongoose.Schema({
+  userId: {
+    type: String,
+    required: true,
+    unique: true,
+    index: true
+  },
+  email: {
+    type: String,
+    lowercase: true,
+    index: true
+  },
+  profile: {
+    name: { type: String },
+    preferredName: { type: String },
+    gender: { type: String },
+    age: { type: Number },
+    birthYear: { type: String },
+    relationshipStatus: { type: String },
+    sameGenderPartner: { type: Boolean },
+    hasKids: { type: String },
+    numberOfKids: { type: Number },
+    kidsInfo: [{ type: mongoose.Schema.Types.Mixed }],
+    religiousLevel: { type: String },
+    education: { type: String },
+    school: { type: String },
+    degree: { type: String },
+    graduationYear: { type: String },
+    jobTitle: { type: String },
+    company: { type: String },
+    industry: { type: String },
+    incomeLevel: { type: String },
+    yearsAtJob: { type: String },
+    city: { type: String },
+    state: { type: String },
+    country: { type: String },
+    onboardingCompleted: { type: Boolean, default: false }
+  },
+  stats: {
+    totalMessages: { type: Number, default: 0 },
+    totalSessions: { type: Number, default: 0 },
+    firstVisit: { type: Date, default: Date.now },
+    lastVisit: { type: Date, default: Date.now },
+    averageSessionLength: { type: Number, default: 0 }, // in minutes
+    favoriteTopics: [{ type: String }],
+    preferredMode: { type: String, default: 'bubbe' }, // bubbe, meshugana, comedian
+    voiceEnabled: { type: Boolean, default: false },
+    totalVoiceMessages: { type: Number, default: 0 }
+  },
+  location: {
+    country: { type: String },
+    state: { type: String },
+    city: { type: String },
+    coordinates: {
+      lat: { type: Number },
+      lon: { type: Number }
+    },
+    timezone: { type: String },
+    lastKnownIP: { type: String }
+  },
+  security: {
+    blocked: { type: Boolean, default: false },
+    blockReason: { type: String },
+    blockedBy: { type: String }, // Admin user ID
+    blockedAt: { type: Date },
+    suspiciousActivity: { type: Boolean, default: false },
+    alertCount: { type: Number, default: 0 },
+    lastAlert: { type: Date },
+    riskLevel: { 
+      type: String, 
+      enum: ['low', 'medium', 'high', 'critical'], 
+      default: 'low' 
+    },
+    whitelisted: { type: Boolean, default: false }
+  },
+  preferences: {
+    colorTheme: { type: String, enum: ['warm', 'cool'], default: 'warm' },
+    language: { type: String, default: 'en' },
+    yiddishTranslations: { type: Boolean, default: true },
+    pushNotifications: { type: Boolean, default: false },
+    emailUpdates: { type: Boolean, default: false },
+    dataSharing: { type: Boolean, default: false }
+  },
+  deviceInfo: {
+    lastUserAgent: { type: String },
+    deviceType: { type: String }, // mobile, tablet, desktop
+    browser: { type: String },
+    os: { type: String }
+  },
+  notes: [{ 
+    note: { type: String },
+    addedBy: { type: String }, // Admin user ID
+    addedAt: { type: Date, default: Date.now },
+    type: { type: String, enum: ['info', 'warning', 'alert'], default: 'info' }
+  }]
+}, {
+  timestamps: true
+})
+
+// Indexes
+userSchema.index({ 'security.blocked': 1 })
+userSchema.index({ 'security.riskLevel': 1 })
+userSchema.index({ 'stats.lastVisit': -1 })
+userSchema.index({ 'location.country': 1, 'location.state': 1 })
+
+// Virtual for full name
+userSchema.virtual('fullName').get(function() {
+  return this.profile?.preferredName || this.profile?.name || 'Unknown User'
+})
+
+// Method to update user activity
+userSchema.methods.updateActivity = function(sessionData) {
+  this.stats.lastVisit = new Date()
+  this.stats.totalMessages += sessionData.messageCount || 0
+  
+  if (sessionData.location) {
+    this.location = { ...this.location, ...sessionData.location }
+  }
+  
+  if (sessionData.deviceInfo) {
+    this.deviceInfo = { ...this.deviceInfo, ...sessionData.deviceInfo }
+  }
+  
+  return this.save()
+}
+
+// Method to add security note
+userSchema.methods.addNote = function(note, adminId, type = 'info') {
+  this.notes.push({
+    note,
+    addedBy: adminId,
+    type
+  })
+  return this.save()
+}
+
+// Method to block user
+userSchema.methods.block = function(reason, adminId) {
+  this.security.blocked = true
+  this.security.blockReason = reason
+  this.security.blockedBy = adminId
+  this.security.blockedAt = new Date()
+  return this.save()
+}
+
+// Method to unblock user
+userSchema.methods.unblock = function(adminId) {
+  this.security.blocked = false
+  this.security.blockReason = null
+  this.security.blockedBy = null
+  this.security.blockedAt = null
+  
+  this.notes.push({
+    note: `User unblocked by admin`,
+    addedBy: adminId,
+    type: 'info'
+  })
+  
+  return this.save()
+}
+
+// Method to increment alert count
+userSchema.methods.addAlert = function(alertType, severity) {
+  this.security.alertCount++
+  this.security.lastAlert = new Date()
+  
+  // Update risk level based on alert count and severity
+  if (this.security.alertCount >= 5 || severity === 'critical') {
+    this.security.riskLevel = 'critical'
+  } else if (this.security.alertCount >= 3 || severity === 'high') {
+    this.security.riskLevel = 'high'
+  } else if (this.security.alertCount >= 1 || severity === 'medium') {
+    this.security.riskLevel = 'medium'
+  }
+  
+  return this.save()
+}
+
+// Static method to find or create user
+userSchema.statics.findByIdOrCreate = function(userId, profileData = {}) {
+  return this.findOneAndUpdate(
+    { userId },
+    { 
+      $setOnInsert: { 
+        userId, 
+        profile: profileData,
+        stats: {
+          totalMessages: 0,
+          totalSessions: 0,
+          firstVisit: new Date(),
+          lastVisit: new Date()
+        }
+      }
+    },
+    { 
+      upsert: true, 
+      new: true, 
+      runValidators: true 
+    }
+  )
+}
+
+// Static method to get user statistics
+userSchema.statics.getStats = function() {
+  return this.aggregate([
+    {
+      $group: {
+        _id: null,
+        totalUsers: { $sum: 1 },
+        blockedUsers: { $sum: { $cond: ['$security.blocked', 1, 0] } },
+        activeUsers: { 
+          $sum: { 
+            $cond: [
+              { $gte: ['$stats.lastVisit', new Date(Date.now() - 24 * 60 * 60 * 1000)] }, 
+              1, 
+              0
+            ] 
+          } 
+        },
+        newUsersToday: {
+          $sum: {
+            $cond: [
+              { $gte: ['$createdAt', new Date(Date.now() - 24 * 60 * 60 * 1000)] },
+              1,
+              0
+            ]
+          }
+        },
+        avgMessagesPerUser: { $avg: '$stats.totalMessages' },
+        riskDistribution: {
+          low: { $sum: { $cond: [{ $eq: ['$security.riskLevel', 'low'] }, 1, 0] } },
+          medium: { $sum: { $cond: [{ $eq: ['$security.riskLevel', 'medium'] }, 1, 0] } },
+          high: { $sum: { $cond: [{ $eq: ['$security.riskLevel', 'high'] }, 1, 0] } },
+          critical: { $sum: { $cond: [{ $eq: ['$security.riskLevel', 'critical'] }, 1, 0] } }
+        }
+      }
+    }
+  ])
+}
+
+// Static method to get geographic distribution
+userSchema.statics.getGeoDistribution = function() {
+  return this.aggregate([
+    {
+      $group: {
+        _id: {
+          country: '$location.country',
+          state: '$location.state'
+        },
+        userCount: { $sum: 1 },
+        activeUsers: {
+          $sum: {
+            $cond: [
+              { $gte: ['$stats.lastVisit', new Date(Date.now() - 24 * 60 * 60 * 1000)] },
+              1,
+              0
+            ]
+          }
+        }
+      }
+    },
+    {
+      $sort: { userCount: -1 }
+    }
+  ])
+}
+
+module.exports = mongoose.model('User', userSchema)
\ No newline at end of file
diff --git a/backend/routes/alerts.js b/backend/routes/alerts.js
new file mode 100644
index 0000000..66974cd
--- /dev/null
+++ b/backend/routes/alerts.js
@@ -0,0 +1,445 @@
+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
\ No newline at end of file
diff --git a/backend/routes/auth.js b/backend/routes/auth.js
new file mode 100644
index 0000000..7aa7be3
--- /dev/null
+++ b/backend/routes/auth.js
@@ -0,0 +1,218 @@
+const express = require('express')
+const bcrypt = require('bcryptjs')
+const jwt = require('jsonwebtoken')
+const rateLimit = require('express-rate-limit')
+const router = express.Router()
+
+// Admin credentials (in production, store in database)
+const ADMIN_CREDENTIALS = [
+  {
+    username: 'admin',
+    password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
+    role: 'super_admin',
+    id: 'admin_1'
+  },
+  {
+    username: 'dw-admin',
+    password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password  
+    role: 'admin',
+    id: 'admin_dw'
+  },
+  {
+    username: 'monitor',
+    password: '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
+    role: 'monitor',
+    id: 'admin_monitor'
+  }
+]
+
+const JWT_SECRET = process.env.JWT_SECRET || 'dear-bubbe-admin-secret-key-change-in-production'
+
+// Rate limiting for auth endpoints
+const authLimiter = rateLimit({
+  windowMs: 15 * 60 * 1000, // 15 minutes
+  max: 5, // limit each IP to 5 requests per windowMs
+  message: 'Too many login attempts, please try again later',
+  standardHeaders: true,
+  legacyHeaders: false
+})
+
+// Login endpoint
+router.post('/login', authLimiter, async (req, res) => {
+  try {
+    const { username, password } = req.body
+    
+    if (!username || !password) {
+      return res.status(400).json({ 
+        success: false, 
+        error: 'Username and password required' 
+      })
+    }
+    
+    // Find admin user
+    const admin = ADMIN_CREDENTIALS.find(a => a.username === username)
+    if (!admin) {
+      return res.status(401).json({ 
+        success: false, 
+        error: 'Invalid credentials' 
+      })
+    }
+    
+    // Check password
+    const passwordMatch = await bcrypt.compare(password, admin.password)
+    if (!passwordMatch) {
+      return res.status(401).json({ 
+        success: false, 
+        error: 'Invalid credentials' 
+      })
+    }
+    
+    // Generate JWT token
+    const token = jwt.sign(
+      { 
+        userId: admin.id,
+        username: admin.username,
+        role: admin.role 
+      },
+      JWT_SECRET,
+      { expiresIn: '24h' }
+    )
+    
+    // Log successful login
+    console.log(`Admin login successful: ${username} (${admin.role}) from ${req.ip}`)
+    
+    res.json({
+      success: true,
+      token,
+      user: {
+        id: admin.id,
+        username: admin.username,
+        role: admin.role
+      },
+      expiresIn: '24h'
+    })
+    
+  } catch (error) {
+    console.error('Login error:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Authentication failed' 
+    })
+  }
+})
+
+// Verify token endpoint
+router.post('/verify', (req, res) => {
+  try {
+    const { token } = req.body
+    
+    if (!token) {
+      return res.status(400).json({ 
+        success: false, 
+        error: 'Token required' 
+      })
+    }
+    
+    const decoded = jwt.verify(token, JWT_SECRET)
+    
+    res.json({
+      success: true,
+      valid: true,
+      user: {
+        id: decoded.userId,
+        username: decoded.username,
+        role: decoded.role
+      }
+    })
+    
+  } catch (error) {
+    res.status(401).json({ 
+      success: false, 
+      valid: false,
+      error: 'Invalid token' 
+    })
+  }
+})
+
+// Refresh token endpoint
+router.post('/refresh', (req, res) => {
+  try {
+    const { token } = req.body
+    
+    if (!token) {
+      return res.status(400).json({ 
+        success: false, 
+        error: 'Token required' 
+      })
+    }
+    
+    const decoded = jwt.verify(token, JWT_SECRET)
+    
+    // Generate new token
+    const newToken = jwt.sign(
+      { 
+        userId: decoded.userId,
+        username: decoded.username,
+        role: decoded.role 
+      },
+      JWT_SECRET,
+      { expiresIn: '24h' }
+    )
+    
+    res.json({
+      success: true,
+      token: newToken,
+      expiresIn: '24h'
+    })
+    
+  } catch (error) {
+    res.status(401).json({ 
+      success: false, 
+      error: 'Invalid token' 
+    })
+  }
+})
+
+// Logout endpoint (mainly for logging purposes)
+router.post('/logout', (req, res) => {
+  // In a real app, you might blacklist the token
+  console.log(`Admin logout from ${req.ip}`)
+  res.json({ 
+    success: true, 
+    message: 'Logged out successfully' 
+  })
+})
+
+// Get current user info
+router.get('/me', (req, res) => {
+  try {
+    const authHeader = req.headers['authorization']
+    const token = authHeader && authHeader.split(' ')[1]
+    
+    if (!token) {
+      return res.status(401).json({ 
+        success: false, 
+        error: 'Access token required' 
+      })
+    }
+    
+    const decoded = jwt.verify(token, JWT_SECRET)
+    
+    res.json({
+      success: true,
+      user: {
+        id: decoded.userId,
+        username: decoded.username,
+        role: decoded.role
+      }
+    })
+    
+  } catch (error) {
+    res.status(401).json({ 
+      success: false, 
+      error: 'Invalid token' 
+    })
+  }
+})
+
+module.exports = router
\ No newline at end of file
diff --git a/backend/routes/monitoring.js b/backend/routes/monitoring.js
new file mode 100644
index 0000000..f6d934b
--- /dev/null
+++ b/backend/routes/monitoring.js
@@ -0,0 +1,516 @@
+const express = require('express')
+const Session = require('../models/Session')
+const Conversation = require('../models/Conversation')
+const Alert = require('../models/Alert')
+const User = require('../models/User')
+const os = require('os')
+const fs = require('fs')
+const path = require('path')
+const axios = require('axios')
+const router = express.Router()
+
+// Get real-time dashboard stats
+router.get('/dashboard', async (req, res) => {
+  try {
+    const now = new Date()
+    const last24h = new Date(now - 24 * 60 * 60 * 1000)
+    const lastHour = new Date(now - 60 * 60 * 1000)
+    const last5min = new Date(now - 5 * 60 * 1000)
+    
+    // Active users and sessions
+    const activeUsers = await Session.countDocuments({
+      lastActivity: { $gte: last5min },
+      ended: false
+    })
+    
+    const activeSessions = await Session.countDocuments({
+      lastActivity: { $gte: new Date(now - 30 * 60 * 1000) },
+      ended: false
+    })
+    
+    // Conversations stats
+    const conversationsToday = await Conversation.countDocuments({
+      timestamp: { $gte: last24h }
+    })
+    
+    const conversationsLastHour = await Conversation.countDocuments({
+      timestamp: { $gte: lastHour }
+    })
+    
+    // Alert stats
+    const alertsToday = await Alert.countDocuments({
+      timestamp: { $gte: last24h }
+    })
+    
+    const criticalAlerts = await Alert.countDocuments({
+      timestamp: { $gte: last24h },
+      severity: { $in: ['high', 'critical'] }
+    })
+    
+    // System metrics
+    const totalMem = os.totalmem()
+    const freeMem = os.freemem()
+    const memUsage = ((totalMem - freeMem) / totalMem) * 100
+    
+    const loadAvg = os.loadavg()
+    const cpuUsage = (loadAvg[0] / os.cpus().length) * 100
+    
+    // Process metrics
+    const processMemory = process.memoryUsage()
+    
+    // User stats
+    const totalUsers = await User.countDocuments()
+    const newUsersToday = await User.countDocuments({
+      createdAt: { $gte: last24h }
+    })
+    
+    const blockedUsers = await User.countDocuments({
+      'security.blocked': true
+    })
+    
+    res.json({
+      success: true,
+      timestamp: now,
+      userActivity: {
+        activeUsers,
+        activeSessions,
+        totalUsers,
+        newUsersToday,
+        blockedUsers
+      },
+      conversations: {
+        today: conversationsToday,
+        lastHour: conversationsLastHour
+      },
+      alerts: {
+        today: alertsToday,
+        critical: criticalAlerts
+      },
+      system: {
+        memoryUsage: Math.round(memUsage),
+        cpuUsage: Math.min(Math.round(cpuUsage), 100),
+        uptime: os.uptime(),
+        loadAverage: loadAvg[0],
+        processMemoryMB: Math.round(processMemory.rss / 1024 / 1024)
+      }
+    })
+    
+  } catch (error) {
+    console.error('Error fetching dashboard stats:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch dashboard statistics' 
+    })
+  }
+})
+
+// Get hourly activity for the last 24 hours
+router.get('/hourly-activity', async (req, res) => {
+  try {
+    const now = new Date()
+    const last24h = new Date(now - 24 * 60 * 60 * 1000)
+    
+    // Get hourly conversation stats
+    const hourlyConversations = await Conversation.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: last24h }
+        }
+      },
+      {
+        $group: {
+          _id: {
+            year: { $year: '$timestamp' },
+            month: { $month: '$timestamp' },
+            day: { $dayOfMonth: '$timestamp' },
+            hour: { $hour: '$timestamp' }
+          },
+          conversations: { $sum: 1 },
+          uniqueUsers: { $addToSet: '$userId' }
+        }
+      },
+      {
+        $addFields: {
+          userCount: { $size: '$uniqueUsers' },
+          hour: '$_id.hour'
+        }
+      },
+      {
+        $sort: { '_id.year': 1, '_id.month': 1, '_id.day': 1, '_id.hour': 1 }
+      }
+    ])
+    
+    // Get hourly alert stats
+    const hourlyAlerts = await Alert.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: last24h }
+        }
+      },
+      {
+        $group: {
+          _id: {
+            year: { $year: '$timestamp' },
+            month: { $month: '$timestamp' },
+            day: { $dayOfMonth: '$timestamp' },
+            hour: { $hour: '$timestamp' }
+          },
+          alerts: { $sum: 1 },
+          critical: { $sum: { $cond: [{ $in: ['$severity', ['high', 'critical']] }, 1, 0] } }
+        }
+      },
+      {
+        $sort: { '_id.year': 1, '_id.month': 1, '_id.day': 1, '_id.hour': 1 }
+      }
+    ])
+    
+    res.json({
+      success: true,
+      timeframe: 'last_24_hours',
+      conversations: hourlyConversations,
+      alerts: hourlyAlerts
+    })
+    
+  } catch (error) {
+    console.error('Error fetching hourly activity:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch hourly activity' 
+    })
+  }
+})
+
+// Get geographic distribution
+router.get('/geographic', async (req, res) => {
+  try {
+    const { timeframe = '24h' } = req.query
+    
+    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
+      default:
+        startTime = new Date(now - 24 * 60 * 60 * 1000)
+    }
+    
+    // Get session geographic data
+    const geoData = await Session.aggregate([
+      {
+        $match: {
+          startTime: { $gte: startTime }
+        }
+      },
+      {
+        $group: {
+          _id: {
+            country: '$location.country',
+            state: '$location.state'
+          },
+          sessions: { $sum: 1 },
+          uniqueUsers: { $addToSet: '$userId' },
+          messages: { $sum: '$activity.messageCount' },
+          alerts: { $sum: '$flags.alertsTriggered' }
+        }
+      },
+      {
+        $addFields: {
+          userCount: { $size: '$uniqueUsers' }
+        }
+      },
+      {
+        $sort: { sessions: -1 }
+      },
+      {
+        $limit: 50
+      }
+    ])
+    
+    // Get country-level stats
+    const countryStats = await Session.aggregate([
+      {
+        $match: {
+          startTime: { $gte: startTime }
+        }
+      },
+      {
+        $group: {
+          _id: '$location.country',
+          sessions: { $sum: 1 },
+          uniqueUsers: { $addToSet: '$userId' },
+          messages: { $sum: '$activity.messageCount' }
+        }
+      },
+      {
+        $addFields: {
+          userCount: { $size: '$uniqueUsers' }
+        }
+      },
+      {
+        $sort: { sessions: -1 }
+      },
+      {
+        $limit: 20
+      }
+    ])
+    
+    res.json({
+      success: true,
+      timeframe,
+      detailed: geoData,
+      byCountry: countryStats
+    })
+    
+  } catch (error) {
+    console.error('Error fetching geographic data:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch geographic data' 
+    })
+  }
+})
+
+// Get topic analysis
+router.get('/topics', async (req, res) => {
+  try {
+    const { timeframe = '24h', limit = 20 } = req.query
+    
+    const topTopics = await Conversation.getTopTopics(parseInt(limit), timeframe)
+    
+    // Get sentiment analysis for top topics
+    const topicSentiment = await Conversation.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
+        }
+      },
+      {
+        $unwind: '$topics'
+      },
+      {
+        $group: {
+          _id: {
+            topic: '$topics',
+            sentiment: '$message.sentiment'
+          },
+          count: { $sum: 1 }
+        }
+      },
+      {
+        $group: {
+          _id: '$_id.topic',
+          sentiments: {
+            $push: {
+              sentiment: '$_id.sentiment',
+              count: '$count'
+            }
+          },
+          total: { $sum: '$count' }
+        }
+      },
+      {
+        $sort: { total: -1 }
+      },
+      {
+        $limit: 10
+      }
+    ])
+    
+    res.json({
+      success: true,
+      timeframe,
+      topTopics,
+      sentimentAnalysis: topicSentiment
+    })
+    
+  } catch (error) {
+    console.error('Error fetching topic analysis:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch topic analysis' 
+    })
+  }
+})
+
+// Check Dear Bubbe API health
+router.get('/bubbe-health', async (req, res) => {
+  try {
+    const startTime = Date.now()
+    
+    // Check main API
+    const apiResponse = await axios.get('http://localhost:3011/api/health', {
+      timeout: 10000
+    })
+    
+    const responseTime = Date.now() - startTime
+    
+    // Check if PM2 process is running
+    let pm2Status = 'unknown'
+    try {
+      const { execSync } = require('child_process')
+      const pm2List = execSync('pm2 jlist', { encoding: 'utf-8' })
+      const processes = JSON.parse(pm2List)
+      const bubbeProcess = processes.find(p => p.name === 'bubbe')
+      
+      if (bubbeProcess) {
+        pm2Status = bubbeProcess.pm2_env.status
+      }
+    } catch (error) {
+      console.warn('Could not check PM2 status:', error.message)
+    }
+    
+    // Check disk space
+    const stats = fs.statSync('/')
+    const diskUsage = {
+      // This is a simplified check - in production you'd use proper disk space checking
+      available: true
+    }
+    
+    res.json({
+      success: true,
+      timestamp: new Date(),
+      api: {
+        status: apiResponse.status === 200 ? 'healthy' : 'unhealthy',
+        responseTime,
+        data: apiResponse.data
+      },
+      pm2: {
+        status: pm2Status
+      },
+      disk: diskUsage,
+      overall: apiResponse.status === 200 && pm2Status === 'online' ? 'healthy' : 'degraded'
+    })
+    
+  } catch (error) {
+    console.error('Bubbe health check failed:', error.message)
+    res.json({
+      success: false,
+      timestamp: new Date(),
+      api: {
+        status: 'unhealthy',
+        error: error.message
+      },
+      overall: 'unhealthy'
+    })
+  }
+})
+
+// Get live session data
+router.get('/live-sessions', async (req, res) => {
+  try {
+    const activeSessions = await Session.getActiveSessions(30) // Last 30 minutes
+    
+    // Enrich with user data
+    const enrichedSessions = await Promise.all(
+      activeSessions.map(async (session) => {
+        const user = await User.findOne({ userId: session.userId })
+          .select('profile.name profile.preferredName security.riskLevel security.blocked')
+          .lean()
+        
+        return {
+          ...session,
+          user: user || { profile: { name: 'Unknown User' } }
+        }
+      })
+    )
+    
+    res.json({
+      success: true,
+      timestamp: new Date(),
+      activeSessions: enrichedSessions,
+      count: enrichedSessions.length
+    })
+    
+  } catch (error) {
+    console.error('Error fetching live sessions:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch live sessions' 
+    })
+  }
+})
+
+// Get performance metrics
+router.get('/performance', async (req, res) => {
+  try {
+    const { timeframe = '24h' } = req.query
+    
+    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
+      default:
+        startTime = new Date(now - 24 * 60 * 60 * 1000)
+    }
+    
+    // Response time stats
+    const responseTimeStats = await Conversation.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startTime },
+          'analysis.responseTime': { $exists: true }
+        }
+      },
+      {
+        $group: {
+          _id: null,
+          avgResponseTime: { $avg: '$analysis.responseTime' },
+          minResponseTime: { $min: '$analysis.responseTime' },
+          maxResponseTime: { $max: '$analysis.responseTime' },
+          count: { $sum: 1 }
+        }
+      }
+    ])
+    
+    // Error rate
+    const errorStats = await Session.aggregate([
+      {
+        $match: {
+          startTime: { $gte: startTime }
+        }
+      },
+      {
+        $group: {
+          _id: null,
+          totalSessions: { $sum: 1 },
+          totalErrors: { $sum: '$activity.errorsEncountered' },
+          avgErrors: { $avg: '$activity.errorsEncountered' }
+        }
+      }
+    ])
+    
+    res.json({
+      success: true,
+      timeframe,
+      responseTime: responseTimeStats[0] || {},
+      errors: errorStats[0] || {},
+      errorRate: errorStats[0] ? (errorStats[0].totalErrors / errorStats[0].totalSessions) * 100 : 0
+    })
+    
+  } catch (error) {
+    console.error('Error fetching performance metrics:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch performance metrics' 
+    })
+  }
+})
+
+module.exports = router
\ No newline at end of file
diff --git a/backend/routes/reports.js b/backend/routes/reports.js
new file mode 100644
index 0000000..3f9d946
--- /dev/null
+++ b/backend/routes/reports.js
@@ -0,0 +1,575 @@
+const express = require('express')
+const User = require('../models/User')
+const Session = require('../models/Session')
+const Conversation = require('../models/Conversation')
+const Alert = require('../models/Alert')
+const fs = require('fs')
+const path = require('path')
+const moment = require('moment')
+const router = express.Router()
+
+// Generate comprehensive daily report
+router.get('/daily', async (req, res) => {
+  try {
+    const { date } = req.query
+    const targetDate = date ? new Date(date) : new Date()
+    const startOfDay = new Date(targetDate.setHours(0, 0, 0, 0))
+    const endOfDay = new Date(targetDate.setHours(23, 59, 59, 999))
+    
+    console.log(`Generating daily report for ${startOfDay.toDateString()}`)
+    
+    // User activity
+    const userStats = {
+      newRegistrations: await User.countDocuments({
+        createdAt: { $gte: startOfDay, $lte: endOfDay }
+      }),
+      activeUsers: await Session.countDocuments({
+        startTime: { $gte: startOfDay, $lte: endOfDay }
+      }),
+      totalSessions: await Session.countDocuments({
+        startTime: { $gte: startOfDay, $lte: endOfDay }
+      })
+    }
+    
+    // Conversation stats
+    const conversationStats = await Conversation.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startOfDay, $lte: endOfDay }
+        }
+      },
+      {
+        $group: {
+          _id: null,
+          totalConversations: { $sum: 1 },
+          uniqueUsers: { $addToSet: '$userId' },
+          avgResponseTime: { $avg: '$analysis.responseTime' },
+          sentimentBreakdown: {
+            positive: { $sum: { $cond: [{ $eq: ['$message.sentiment', 'positive'] }, 1, 0] } },
+            neutral: { $sum: { $cond: [{ $eq: ['$message.sentiment', 'neutral'] }, 1, 0] } },
+            negative: { $sum: { $cond: [{ $eq: ['$message.sentiment', 'negative'] }, 1, 0] } }
+          },
+          modeUsage: {
+            bubbe: { $sum: { $cond: [{ $eq: ['$response.mode', 'bubbe'] }, 1, 0] } },
+            meshugana: { $sum: { $cond: [{ $eq: ['$response.mode', 'meshugana'] }, 1, 0] } },
+            comedian: { $sum: { $cond: [{ $eq: ['$response.mode', 'comedian'] }, 1, 0] } }
+          },
+          voiceUsage: { $sum: { $cond: ['$response.voiceGenerated', 1, 0] } }
+        }
+      }
+    ])
+    
+    // Alert statistics
+    const alertStats = await Alert.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startOfDay, $lte: endOfDay }
+        }
+      },
+      {
+        $group: {
+          _id: null,
+          totalAlerts: { $sum: 1 },
+          bySeverity: {
+            critical: { $sum: { $cond: [{ $eq: ['$severity', 'critical'] }, 1, 0] } },
+            high: { $sum: { $cond: [{ $eq: ['$severity', 'high'] }, 1, 0] } },
+            medium: { $sum: { $cond: [{ $eq: ['$severity', 'medium'] }, 1, 0] } },
+            low: { $sum: { $cond: [{ $eq: ['$severity', 'low'] }, 1, 0] } }
+          },
+          byType: {
+            antisemitism: { $sum: { $cond: [{ $eq: ['$type', 'antisemitism'] }, 1, 0] } },
+            racism: { $sum: { $cond: [{ $eq: ['$type', 'racism'] }, 1, 0] } },
+            harassment: { $sum: { $cond: [{ $eq: ['$type', 'harassment'] }, 1, 0] } },
+            spam: { $sum: { $cond: [{ $eq: ['$type', 'spam'] }, 1, 0] } }
+          },
+          uniqueIPs: { $addToSet: '$userInfo.ip' },
+          uniqueUsers: { $addToSet: '$userInfo.userId' }
+        }
+      }
+    ])
+    
+    // Top topics
+    const topTopics = await Conversation.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startOfDay, $lte: endOfDay }
+        }
+      },
+      {
+        $unwind: '$topics'
+      },
+      {
+        $group: {
+          _id: '$topics',
+          count: { $sum: 1 }
+        }
+      },
+      {
+        $sort: { count: -1 }
+      },
+      {
+        $limit: 10
+      }
+    ])
+    
+    // Geographic distribution
+    const geoStats = await Session.aggregate([
+      {
+        $match: {
+          startTime: { $gte: startOfDay, $lte: endOfDay }
+        }
+      },
+      {
+        $group: {
+          _id: '$location.country',
+          sessions: { $sum: 1 },
+          uniqueUsers: { $addToSet: '$userId' }
+        }
+      },
+      {
+        $addFields: {
+          userCount: { $size: '$uniqueUsers' }
+        }
+      },
+      {
+        $sort: { sessions: -1 }
+      },
+      {
+        $limit: 10
+      }
+    ])
+    
+    const report = {
+      reportType: 'daily',
+      date: startOfDay.toDateString(),
+      generatedAt: new Date(),
+      generatedBy: req.user.userId,
+      userActivity: {
+        ...userStats,
+        uniqueActiveUsers: conversationStats[0]?.uniqueUsers?.length || 0
+      },
+      conversations: conversationStats[0] || {},
+      alerts: {
+        ...alertStats[0],
+        uniqueOffendingIPs: alertStats[0]?.uniqueIPs?.length || 0,
+        uniqueOffendingUsers: alertStats[0]?.uniqueUsers?.length || 0
+      },
+      topTopics,
+      geography: geoStats,
+      summary: {
+        totalInteractions: conversationStats[0]?.totalConversations || 0,
+        securityIncidents: alertStats[0]?.totalAlerts || 0,
+        userGrowth: userStats.newRegistrations,
+        engagement: conversationStats[0]?.totalConversations / Math.max(userStats.activeUsers, 1),
+        alertRate: (alertStats[0]?.totalAlerts || 0) / Math.max(conversationStats[0]?.totalConversations || 1, 1) * 100
+      }
+    }
+    
+    res.json({
+      success: true,
+      report
+    })
+    
+  } catch (error) {
+    console.error('Error generating daily report:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to generate daily report' 
+    })
+  }
+})
+
+// Generate weekly report
+router.get('/weekly', async (req, res) => {
+  try {
+    const { week } = req.query
+    const now = new Date()
+    const startOfWeek = week ? new Date(week) : new Date(now.setDate(now.getDate() - now.getDay()))
+    const endOfWeek = new Date(startOfWeek)
+    endOfWeek.setDate(endOfWeek.getDate() + 6)
+    endOfWeek.setHours(23, 59, 59, 999)
+    startOfWeek.setHours(0, 0, 0, 0)
+    
+    // Daily breakdown for the week
+    const dailyStats = []
+    for (let i = 0; i < 7; i++) {
+      const dayStart = new Date(startOfWeek)
+      dayStart.setDate(dayStart.getDate() + i)
+      dayStart.setHours(0, 0, 0, 0)
+      
+      const dayEnd = new Date(dayStart)
+      dayEnd.setHours(23, 59, 59, 999)
+      
+      const dayStats = {
+        date: dayStart.toDateString(),
+        conversations: await Conversation.countDocuments({
+          timestamp: { $gte: dayStart, $lte: dayEnd }
+        }),
+        alerts: await Alert.countDocuments({
+          timestamp: { $gte: dayStart, $lte: dayEnd }
+        }),
+        sessions: await Session.countDocuments({
+          startTime: { $gte: dayStart, $lte: dayEnd }
+        })
+      }
+      
+      dailyStats.push(dayStats)
+    }
+    
+    // Week totals
+    const weekTotals = {
+      conversations: await Conversation.countDocuments({
+        timestamp: { $gte: startOfWeek, $lte: endOfWeek }
+      }),
+      alerts: await Alert.countDocuments({
+        timestamp: { $gte: startOfWeek, $lte: endOfWeek }
+      }),
+      newUsers: await User.countDocuments({
+        createdAt: { $gte: startOfWeek, $lte: endOfWeek }
+      }),
+      sessions: await Session.countDocuments({
+        startTime: { $gte: startOfWeek, $lte: endOfWeek }
+      })
+    }
+    
+    // Top users of the week
+    const topUsers = await Conversation.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startOfWeek, $lte: endOfWeek }
+        }
+      },
+      {
+        $group: {
+          _id: '$userId',
+          messageCount: { $sum: 1 },
+          topics: { $addToSet: '$topics' }
+        }
+      },
+      {
+        $sort: { messageCount: -1 }
+      },
+      {
+        $limit: 10
+      }
+    ])
+    
+    const report = {
+      reportType: 'weekly',
+      weekStart: startOfWeek.toDateString(),
+      weekEnd: endOfWeek.toDateString(),
+      generatedAt: new Date(),
+      generatedBy: req.user.userId,
+      dailyBreakdown: dailyStats,
+      weekTotals,
+      topUsers,
+      trends: {
+        conversationTrend: calculateTrend(dailyStats.map(d => d.conversations)),
+        alertTrend: calculateTrend(dailyStats.map(d => d.alerts)),
+        sessionTrend: calculateTrend(dailyStats.map(d => d.sessions))
+      }
+    }
+    
+    res.json({
+      success: true,
+      report
+    })
+    
+  } catch (error) {
+    console.error('Error generating weekly report:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to generate weekly report' 
+    })
+  }
+})
+
+// Generate security report
+router.get('/security', async (req, res) => {
+  try {
+    const { 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)
+    }
+    
+    // Alert analysis
+    const alertAnalysis = await Alert.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startTime }
+        }
+      },
+      {
+        $group: {
+          _id: {
+            type: '$type',
+            severity: '$severity'
+          },
+          count: { $sum: 1 },
+          uniqueIPs: { $addToSet: '$userInfo.ip' },
+          uniqueUsers: { $addToSet: '$userInfo.userId' },
+          patterns: { $addToSet: '$violation.pattern' },
+          locations: { $addToSet: '$location.country' }
+        }
+      }
+    ])
+    
+    // Repeat offenders
+    const repeatOffenders = await Alert.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startTime }
+        }
+      },
+      {
+        $group: {
+          _id: '$userInfo.ip',
+          alertCount: { $sum: 1 },
+          severities: { $push: '$severity' },
+          types: { $push: '$type' },
+          locations: { $addToSet: '$location' },
+          userIds: { $addToSet: '$userInfo.userId' }
+        }
+      },
+      {
+        $match: {
+          alertCount: { $gte: 2 }
+        }
+      },
+      {
+        $sort: { alertCount: -1 }
+      },
+      {
+        $limit: 20
+      }
+    ])
+    
+    // Blocked users
+    const blockedUsers = await User.aggregate([
+      {
+        $match: {
+          'security.blocked': true,
+          'security.blockedAt': { $gte: startTime }
+        }
+      },
+      {
+        $group: {
+          _id: '$security.blockReason',
+          count: { $sum: 1 },
+          users: { $push: { userId: '$userId', blockedAt: '$security.blockedAt' } }
+        }
+      }
+    ])
+    
+    // Geographic threat distribution
+    const geoThreats = await Alert.aggregate([
+      {
+        $match: {
+          timestamp: { $gte: startTime },
+          severity: { $in: ['high', 'critical'] }
+        }
+      },
+      {
+        $group: {
+          _id: '$location.country',
+          alerts: { $sum: 1 },
+          uniqueIPs: { $addToSet: '$userInfo.ip' },
+          types: { $addToSet: '$type' }
+        }
+      },
+      {
+        $addFields: {
+          uniqueIPCount: { $size: '$uniqueIPs' }
+        }
+      },
+      {
+        $sort: { alerts: -1 }
+      }
+    ])
+    
+    const report = {
+      reportType: 'security',
+      timeframe,
+      generatedAt: new Date(),
+      generatedBy: req.user.userId,
+      alertAnalysis,
+      repeatOffenders,
+      blockedUsers,
+      geoThreats,
+      summary: {
+        totalAlerts: alertAnalysis.reduce((sum, item) => sum + item.count, 0),
+        criticalIncidents: alertAnalysis
+          .filter(item => item._id.severity === 'critical')
+          .reduce((sum, item) => sum + item.count, 0),
+        repeatOffenderCount: repeatOffenders.length,
+        topThreatCountry: geoThreats[0]?._id || 'Unknown',
+        blockedUserCount: blockedUsers.reduce((sum, item) => sum + item.count, 0)
+      }
+    }
+    
+    res.json({
+      success: true,
+      report
+    })
+    
+  } catch (error) {
+    console.error('Error generating security report:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to generate security report' 
+    })
+  }
+})
+
+// Export report to file
+router.post('/export', async (req, res) => {
+  try {
+    const { reportType, data } = req.body
+    
+    if (!reportType || !data) {
+      return res.status(400).json({
+        success: false,
+        error: 'Report type and data required'
+      })
+    }
+    
+    const timestamp = moment().format('YYYY-MM-DD_HH-mm-ss')
+    const filename = `${reportType}_report_${timestamp}.json`
+    const exportDir = path.join(__dirname, '../exports')
+    
+    // Ensure export directory exists
+    if (!fs.existsSync(exportDir)) {
+      fs.mkdirSync(exportDir, { recursive: true })
+    }
+    
+    const filepath = path.join(exportDir, filename)
+    
+    // Add metadata
+    const exportData = {
+      ...data,
+      exportedAt: new Date(),
+      exportedBy: req.user.userId,
+      filename
+    }
+    
+    fs.writeFileSync(filepath, JSON.stringify(exportData, null, 2))
+    
+    console.log(`Report exported: ${filename} by ${req.user.userId}`)
+    
+    res.json({
+      success: true,
+      filename,
+      filepath,
+      size: fs.statSync(filepath).size
+    })
+    
+  } catch (error) {
+    console.error('Error exporting report:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to export report' 
+    })
+  }
+})
+
+// Get list of exported reports
+router.get('/exports', async (req, res) => {
+  try {
+    const exportDir = path.join(__dirname, '../exports')
+    
+    if (!fs.existsSync(exportDir)) {
+      return res.json({
+        success: true,
+        exports: []
+      })
+    }
+    
+    const files = fs.readdirSync(exportDir)
+    const exports = files
+      .filter(file => file.endsWith('.json'))
+      .map(file => {
+        const filepath = path.join(exportDir, file)
+        const stats = fs.statSync(filepath)
+        return {
+          filename: file,
+          size: stats.size,
+          created: stats.birthtime,
+          modified: stats.mtime
+        }
+      })
+      .sort((a, b) => new Date(b.created) - new Date(a.created))
+    
+    res.json({
+      success: true,
+      exports
+    })
+    
+  } catch (error) {
+    console.error('Error listing exports:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to list exports' 
+    })
+  }
+})
+
+// Download exported report
+router.get('/exports/:filename', async (req, res) => {
+  try {
+    const { filename } = req.params
+    const exportDir = path.join(__dirname, '../exports')
+    const filepath = path.join(exportDir, filename)
+    
+    if (!fs.existsSync(filepath)) {
+      return res.status(404).json({
+        success: false,
+        error: 'Export file not found'
+      })
+    }
+    
+    res.download(filepath, filename, (err) => {
+      if (err) {
+        console.error('Error downloading file:', err)
+        res.status(500).json({
+          success: false,
+          error: 'Failed to download file'
+        })
+      }
+    })
+    
+  } catch (error) {
+    console.error('Error downloading export:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to download export' 
+    })
+  }
+})
+
+// Helper function to calculate trend
+function calculateTrend(values) {
+  if (values.length < 2) return 0
+  
+  const first = values[0]
+  const last = values[values.length - 1]
+  
+  if (first === 0) return last > 0 ? 100 : 0
+  
+  return ((last - first) / first) * 100
+}
+
+module.exports = router
\ No newline at end of file
diff --git a/backend/routes/scanner.js b/backend/routes/scanner.js
new file mode 100644
index 0000000..b63c768
--- /dev/null
+++ b/backend/routes/scanner.js
@@ -0,0 +1,113 @@
+const express = require('express')
+const MessageInterceptor = require('../middleware/MessageInterceptor')
+const router = express.Router()
+
+// Initialize message interceptor (will be set by server)
+let messageInterceptor = null
+
+// Set the interceptor instance (called from server.js)
+router.setInterceptor = (interceptor) => {
+  messageInterceptor = interceptor
+}
+
+// Public endpoint for scanning messages (no auth required for integration)
+router.post('/scan-message', async (req, res) => {
+  try {
+    if (!messageInterceptor) {
+      return res.status(503).json({
+        success: false,
+        error: 'Scanner not initialized'
+      })
+    }
+
+    const messageData = req.body
+    
+    // Validate required fields
+    if (!messageData.message) {
+      return res.status(400).json({
+        success: false,
+        error: 'Message is required'
+      })
+    }
+
+    console.log(`[SCANNER] Processing message from ${messageData.userId || 'unknown'}`)
+    
+    const result = await messageInterceptor.processMessage(messageData)
+    
+    res.json({
+      success: true,
+      result
+    })
+    
+  } catch (error) {
+    console.error('[SCANNER] Error:', error)
+    res.status(500).json({
+      success: false,
+      error: error.message
+    })
+  }
+})
+
+// Health check for scanner
+router.get('/scanner/health', (req, res) => {
+  res.json({
+    success: true,
+    status: 'healthy',
+    timestamp: new Date(),
+    scannerReady: !!messageInterceptor
+  })
+})
+
+// Get scanner stats (auth required)
+router.get('/scanner/stats', async (req, res) => {
+  try {
+    // This would typically require authentication, but for now just return basic stats
+    const stats = {
+      processed: messageInterceptor?.processedMessages?.size || 0,
+      uptime: process.uptime(),
+      timestamp: new Date()
+    }
+    
+    res.json({
+      success: true,
+      stats
+    })
+  } catch (error) {
+    console.error('[SCANNER] Stats error:', error)
+    res.status(500).json({
+      success: false,
+      error: error.message
+    })
+  }
+})
+
+// Integration guide endpoint
+router.get('/integration-guide', (req, res) => {
+  res.json({
+    success: true,
+    integration: {
+      endpoint: '/api/scan-message',
+      method: 'POST',
+      example: {
+        message: 'User message to scan',
+        userId: 'user123',
+        sessionId: 'session456',
+        ip: '192.168.1.1',
+        userAgent: 'Mozilla/5.0...',
+        userProfile: {}
+      },
+      response: {
+        success: true,
+        result: {
+          processed: true,
+          blocked: false,
+          violations: [],
+          alertsCreated: 0
+        }
+      }
+    },
+    codeExample: MessageInterceptor.createIntegrationCode()
+  })
+})
+
+module.exports = router
\ No newline at end of file
diff --git a/backend/routes/users.js b/backend/routes/users.js
new file mode 100644
index 0000000..fdc5433
--- /dev/null
+++ b/backend/routes/users.js
@@ -0,0 +1,476 @@
+const express = require('express')
+const User = require('../models/User')
+const Session = require('../models/Session')
+const Conversation = require('../models/Conversation')
+const Alert = require('../models/Alert')
+const fs = require('fs')
+const path = require('path')
+const router = express.Router()
+
+// Get all users with filtering and pagination
+router.get('/', async (req, res) => {
+  try {
+    const {
+      page = 1,
+      limit = 50,
+      search,
+      blocked,
+      riskLevel,
+      country,
+      state,
+      sort = '-stats.lastVisit'
+    } = req.query
+    
+    // Build query
+    const query = {}
+    
+    if (search) {
+      query.$or = [
+        { 'profile.name': { $regex: search, $options: 'i' } },
+        { 'profile.preferredName': { $regex: search, $options: 'i' } },
+        { email: { $regex: search, $options: 'i' } },
+        { userId: search }
+      ]
+    }
+    
+    if (blocked !== undefined) {
+      query['security.blocked'] = blocked === 'true'
+    }
+    
+    if (riskLevel) {
+      query['security.riskLevel'] = riskLevel
+    }
+    
+    if (country) {
+      query['location.country'] = country
+    }
+    
+    if (state) {
+      query['location.state'] = state
+    }
+    
+    const users = await User.find(query)
+      .sort(sort)
+      .limit(limit * 1)
+      .skip((page - 1) * limit)
+      .lean()
+    
+    const total = await User.countDocuments(query)
+    
+    res.json({
+      success: true,
+      users,
+      pagination: {
+        current: parseInt(page),
+        limit: parseInt(limit),
+        total,
+        pages: Math.ceil(total / limit)
+      }
+    })
+    
+  } catch (error) {
+    console.error('Error fetching users:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch users' 
+    })
+  }
+})
+
+// Get user statistics
+router.get('/stats', async (req, res) => {
+  try {
+    const stats = await User.getStats()
+    const geoStats = await User.getGeoDistribution()
+    
+    // Additional stats
+    const activeUsersToday = await User.countDocuments({
+      'stats.lastVisit': { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
+    })
+    
+    const newUsersThisWeek = await User.countDocuments({
+      createdAt: { $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
+    })
+    
+    const topActiveUsers = await User.aggregate([
+      {
+        $match: {
+          'stats.lastVisit': { $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
+        }
+      },
+      {
+        $sort: { 'stats.totalMessages': -1 }
+      },
+      {
+        $limit: 10
+      },
+      {
+        $project: {
+          userId: 1,
+          'profile.name': 1,
+          'profile.preferredName': 1,
+          'stats.totalMessages': 1,
+          'stats.lastVisit': 1,
+          'location.country': 1,
+          'location.state': 1
+        }
+      }
+    ])
+    
+    res.json({
+      success: true,
+      stats: stats[0] || {},
+      geoStats,
+      activeUsersToday,
+      newUsersThisWeek,
+      topActiveUsers
+    })
+    
+  } catch (error) {
+    console.error('Error fetching user stats:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch user statistics' 
+    })
+  }
+})
+
+// Get specific user details
+router.get('/:userId', async (req, res) => {
+  try {
+    const user = await User.findOne({ userId: req.params.userId }).lean()
+    
+    if (!user) {
+      return res.status(404).json({ 
+        success: false, 
+        error: 'User not found' 
+      })
+    }
+    
+    // Get user's sessions
+    const sessions = await Session.find({ userId: req.params.userId })
+      .sort({ startTime: -1 })
+      .limit(20)
+      .lean()
+    
+    // Get recent conversations
+    const conversations = await Conversation.find({ userId: req.params.userId })
+      .sort({ timestamp: -1 })
+      .limit(50)
+      .lean()
+    
+    // Get alerts for this user
+    const alerts = await Alert.find({ 'userInfo.userId': req.params.userId })
+      .sort({ timestamp: -1 })
+      .limit(20)
+      .lean()
+    
+    // Load user memory from Dear Bubbe if it exists
+    let userMemory = null
+    try {
+      const memoryPath = path.join(process.cwd(), '../dear-bubbe-nextjs/user-memories', `${req.params.userId.replace(/[^a-zA-Z0-9-_]/g, '_')}.md`)
+      if (fs.existsSync(memoryPath)) {
+        userMemory = fs.readFileSync(memoryPath, 'utf-8')
+      }
+    } catch (error) {
+      console.log('Could not load user memory:', error.message)
+    }
+    
+    // Load user profile from Dear Bubbe profiles directory
+    let bubbeProfile = null
+    try {
+      const profilesDir = path.join(process.cwd(), '../dear-bubbe-nextjs/data/profiles')
+      const files = fs.readdirSync(profilesDir)
+      const profileFile = files.find(f => f.includes(user.userId) || f.includes(user.email))
+      
+      if (profileFile) {
+        const profilePath = path.join(profilesDir, profileFile)
+        bubbeProfile = JSON.parse(fs.readFileSync(profilePath, 'utf-8'))
+      }
+    } catch (error) {
+      console.log('Could not load Bubbe profile:', error.message)
+    }
+    
+    res.json({
+      success: true,
+      user,
+      sessions,
+      conversations,
+      alerts,
+      userMemory,
+      bubbeProfile,
+      summary: {
+        totalSessions: sessions.length,
+        totalConversations: conversations.length,
+        totalAlerts: alerts.length,
+        riskLevel: user.security?.riskLevel || 'low',
+        lastActivity: user.stats?.lastVisit
+      }
+    })
+    
+  } catch (error) {
+    console.error('Error fetching user details:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch user details' 
+    })
+  }
+})
+
+// Update user profile/settings
+router.put('/:userId', async (req, res) => {
+  try {
+    const { profile, security, preferences, notes } = req.body
+    const adminId = req.user.userId
+    
+    const user = await User.findOne({ userId: req.params.userId })
+    
+    if (!user) {
+      return res.status(404).json({ 
+        success: false, 
+        error: 'User not found' 
+      })
+    }
+    
+    // Update profile if provided
+    if (profile) {
+      user.profile = { ...user.profile, ...profile }
+    }
+    
+    // Update security settings (admin only)
+    if (security && (req.user.role === 'admin' || req.user.role === 'super_admin')) {
+      user.security = { ...user.security, ...security }
+    }
+    
+    // Update preferences
+    if (preferences) {
+      user.preferences = { ...user.preferences, ...preferences }
+    }
+    
+    // Add admin note if provided
+    if (notes) {
+      await user.addNote(`Updated by admin: ${notes}`, adminId, 'info')
+    }
+    
+    await user.save()
+    
+    console.log(`User ${req.params.userId} updated by ${adminId}`)
+    
+    res.json({
+      success: true,
+      user: user.toObject()
+    })
+    
+  } catch (error) {
+    console.error('Error updating user:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to update user' 
+    })
+  }
+})
+
+// Block user
+router.post('/:userId/block', async (req, res) => {
+  try {
+    const { reason } = req.body
+    const adminId = req.user.userId
+    
+    if (!reason) {
+      return res.status(400).json({ 
+        success: false, 
+        error: 'Block reason required' 
+      })
+    }
+    
+    const user = await User.findOne({ userId: req.params.userId })
+    
+    if (!user) {
+      return res.status(404).json({ 
+        success: false, 
+        error: 'User not found' 
+      })
+    }
+    
+    await user.block(reason, adminId)
+    
+    console.log(`User ${req.params.userId} blocked by ${adminId}: ${reason}`)
+    
+    res.json({
+      success: true,
+      message: 'User blocked successfully',
+      user: user.toObject()
+    })
+    
+  } catch (error) {
+    console.error('Error blocking user:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to block user' 
+    })
+  }
+})
+
+// Unblock user
+router.post('/:userId/unblock', async (req, res) => {
+  try {
+    const adminId = req.user.userId
+    
+    const user = await User.findOne({ userId: req.params.userId })
+    
+    if (!user) {
+      return res.status(404).json({ 
+        success: false, 
+        error: 'User not found' 
+      })
+    }
+    
+    if (!user.security.blocked) {
+      return res.status(400).json({ 
+        success: false, 
+        error: 'User is not blocked' 
+      })
+    }
+    
+    await user.unblock(adminId)
+    
+    console.log(`User ${req.params.userId} unblocked by ${adminId}`)
+    
+    res.json({
+      success: true,
+      message: 'User unblocked successfully',
+      user: user.toObject()
+    })
+    
+  } catch (error) {
+    console.error('Error unblocking user:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to unblock user' 
+    })
+  }
+})
+
+// Add note to user
+router.post('/:userId/notes', async (req, res) => {
+  try {
+    const { note, type = 'info' } = req.body
+    const adminId = req.user.userId
+    
+    if (!note) {
+      return res.status(400).json({ 
+        success: false, 
+        error: 'Note content required' 
+      })
+    }
+    
+    const user = await User.findOne({ userId: req.params.userId })
+    
+    if (!user) {
+      return res.status(404).json({ 
+        success: false, 
+        error: 'User not found' 
+      })
+    }
+    
+    await user.addNote(note, adminId, type)
+    
+    res.json({
+      success: true,
+      message: 'Note added successfully',
+      notes: user.notes
+    })
+    
+  } catch (error) {
+    console.error('Error adding note:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to add note' 
+    })
+  }
+})
+
+// Get user conversation history
+router.get('/:userId/conversations', async (req, res) => {
+  try {
+    const { page = 1, limit = 100, topic, sentiment } = req.query
+    
+    const query = { userId: req.params.userId }
+    
+    if (topic) {
+      query.topics = topic
+    }
+    
+    if (sentiment) {
+      query['message.sentiment'] = sentiment
+    }
+    
+    const conversations = await Conversation.find(query)
+      .sort({ timestamp: -1 })
+      .limit(limit * 1)
+      .skip((page - 1) * limit)
+      .lean()
+    
+    const total = await Conversation.countDocuments(query)
+    
+    res.json({
+      success: true,
+      conversations,
+      pagination: {
+        current: parseInt(page),
+        limit: parseInt(limit),
+        total,
+        pages: Math.ceil(total / limit)
+      }
+    })
+    
+  } catch (error) {
+    console.error('Error fetching user conversations:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to fetch conversations' 
+    })
+  }
+})
+
+// Export user data (GDPR compliance)
+router.get('/:userId/export', async (req, res) => {
+  try {
+    const user = await User.findOne({ userId: req.params.userId }).lean()
+    
+    if (!user) {
+      return res.status(404).json({ 
+        success: false, 
+        error: 'User not found' 
+      })
+    }
+    
+    const sessions = await Session.find({ userId: req.params.userId }).lean()
+    const conversations = await Conversation.find({ userId: req.params.userId }).lean()
+    const alerts = await Alert.find({ 'userInfo.userId': req.params.userId }).lean()
+    
+    const exportData = {
+      user,
+      sessions,
+      conversations,
+      alerts,
+      exportedAt: new Date(),
+      exportedBy: req.user.userId
+    }
+    
+    res.setHeader('Content-Type', 'application/json')
+    res.setHeader('Content-Disposition', `attachment; filename="user_data_${req.params.userId}_${Date.now()}.json"`)
+    res.json(exportData)
+    
+    console.log(`User data exported for ${req.params.userId} by ${req.user.userId}`)
+    
+  } catch (error) {
+    console.error('Error exporting user data:', error)
+    res.status(500).json({ 
+      success: false, 
+      error: 'Failed to export user data' 
+    })
+  }
+})
+
+module.exports = router
\ No newline at end of file
diff --git a/backend/server.js b/backend/server.js
new file mode 100644
index 0000000..4895f8f
--- /dev/null
+++ b/backend/server.js
@@ -0,0 +1,973 @@
+const express = require('express')
+const cors = require('cors')
+const helmet = require('helmet')
+const compression = require('compression')
+const winston = require('winston')
+const fs = require('fs').promises
+const path = require('path')
+const jwt = require('jsonwebtoken')
+const { OAuth2Client } = require('google-auth-library')
+const http = require('http')
+const { Server } = require('socket.io')
+require('dotenv').config()
+
+// Constants
+const PORT = process.env.PORT || 5013
+const ADMIN_EMAIL = 'steve@designerwallcoverings.com'
+const JWT_SECRET = process.env.JWT_SECRET;
+if (!JWT_SECRET) {
+  throw new Error('JWT_SECRET env var required (no fallback for production safety) — audit 2026-05-04');
+}
+const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID || '493480746821-mhpjc0glkkogcf845ubi2hkhksoqj39k.apps.googleusercontent.com'
+const PROFILES_DIR = path.join(__dirname, '../../dear-bubbe-nextjs/data/profiles')
+const LOGS_DIR = path.join(__dirname, '../logs')
+
+// Logger setup
+const logger = winston.createLogger({
+  level: 'info',
+  format: winston.format.combine(
+    winston.format.timestamp(),
+    winston.format.json()
+  ),
+  transports: [
+    new winston.transports.File({ filename: path.join(LOGS_DIR, 'error.log'), level: 'error' }),
+    new winston.transports.File({ filename: path.join(LOGS_DIR, 'combined.log') }),
+    new winston.transports.Console({
+      format: winston.format.combine(
+        winston.format.colorize(),
+        winston.format.simple()
+      )
+    })
+  ]
+})
+
+// Ensure directories exist
+async function ensureDirectories() {
+  try {
+    await fs.mkdir(LOGS_DIR, { recursive: true })
+    await fs.mkdir(path.join(__dirname, '../alerts'), { recursive: true })
+    logger.info('Directories initialized')
+  } catch (error) {
+    logger.error('Failed to create directories:', error)
+  }
+}
+
+// Initialize Express
+const app = express()
+const server = http.createServer(app)
+const io = new Server(server, {
+  cors: {
+    origin: ['http://localhost:3000', 'http://45.61.58.125:5013'],
+    credentials: true
+  }
+})
+
+// Google OAuth client
+const client = new OAuth2Client(GOOGLE_CLIENT_ID)
+
+// Middleware
+app.use(helmet({
+  contentSecurityPolicy: false, // Disable CSP to allow Google OAuth
+  crossOriginEmbedderPolicy: false
+}))
+app.use(compression())
+app.use(cors({
+  origin: ['http://localhost:3000', 'http://45.61.58.125:5013', 'https://accounts.google.com'],
+  credentials: true
+}))
+app.use(express.json())
+app.use(express.urlencoded({ extended: true }))
+
+// Alert storage (in-memory for now, will migrate to DB)
+const alerts = {
+  antisemitic: [],
+  racist: [],
+  other: []
+}
+
+// Active sessions tracking
+const activeSessions = new Map()
+
+// Clean up old sessions every 5 minutes
+setInterval(() => {
+  const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)
+  
+  for (const [userId, session] of activeSessions) {
+    if (new Date(session.lastActive) < fiveMinutesAgo) {
+      activeSessions.delete(userId)
+    }
+  }
+  
+  // Emit updated count to all admin clients
+  io.emit('stats-update', {
+    activeUsers: activeSessions.size
+  })
+}, 5 * 60 * 1000)
+
+// Auth middleware - RESTRICTED TO steve@designerwallcoverings.com ONLY
+async function authenticateAdmin(req, res, next) {
+  try {
+    const token = req.headers.authorization?.split(' ')[1]
+    
+    if (!token) {
+      return res.status(401).json({ error: 'No token provided' })
+    }
+
+    const decoded = jwt.verify(token, JWT_SECRET)
+    
+    // CRITICAL: Only allow steve@designerwallcoverings.com
+    if (decoded.email !== ADMIN_EMAIL) {
+      logger.warn(`Unauthorized access attempt from: ${decoded.email}`)
+      return res.status(403).json({ error: 'Access denied. Admin only.' })
+    }
+
+    req.admin = decoded
+    next()
+  } catch (error) {
+    logger.error('Auth error:', error)
+    res.status(401).json({ error: 'Invalid token' })
+  }
+}
+
+// Routes
+
+// Password login - RESTRICTED  
+app.post('/api/auth/password', async (req, res) => {
+  try {
+    const { email, password } = req.body
+    
+    // CRITICAL: Only allow steve@designerwallcoverings.com
+    if (email !== ADMIN_EMAIL) {
+      logger.warn(`Login attempt blocked for: ${email}`)
+      return res.status(403).json({ 
+        error: 'Access denied. This admin panel is restricted.',
+        message: 'Only authorized administrators can access this system.'
+      })
+    }
+    
+    // Check password (you should use bcrypt in production)
+    // For now, using a simple password check
+    const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'BubbeAdmin2024!'
+    
+    if (password !== ADMIN_PASSWORD) {
+      logger.warn(`Invalid password attempt for: ${email}`)
+      return res.status(401).json({ 
+        error: 'Invalid password',
+        message: 'Invalid password. Please try again.'
+      })
+    }
+    
+    // Create JWT token
+    const token = jwt.sign(
+      { 
+        email: email,
+        name: 'Steve',
+        picture: 'https://ui-avatars.com/api/?name=Steve&background=667eea&color=fff',
+        isAdmin: true
+      },
+      JWT_SECRET,
+      { expiresIn: '24h' }
+    )
+    
+    logger.info(`Admin logged in via password: ${email}`)
+    
+    res.json({
+      token,
+      user: {
+        email: email,
+        name: 'Steve',
+        picture: 'https://ui-avatars.com/api/?name=Steve&background=667eea&color=fff',
+        isAdmin: true
+      }
+    })
+  } catch (error) {
+    logger.error('Password auth error:', error)
+    res.status(400).json({ error: 'Authentication failed' })
+  }
+})
+
+// Google OAuth login - RESTRICTED
+app.post('/api/auth/google', async (req, res) => {
+  try {
+    const { credential } = req.body
+    
+    // Verify Google token
+    const ticket = await client.verifyIdToken({
+      idToken: credential,
+      audience: GOOGLE_CLIENT_ID
+    })
+    
+    const payload = ticket.getPayload()
+    const email = payload.email
+    
+    // CRITICAL: Only allow steve@designerwallcoverings.com
+    if (email !== ADMIN_EMAIL) {
+      logger.warn(`Login attempt blocked for: ${email}`)
+      return res.status(403).json({ 
+        error: 'Access denied. This admin panel is restricted.',
+        message: 'Only authorized administrators can access this system.'
+      })
+    }
+    
+    // Create JWT token
+    const token = jwt.sign(
+      { 
+        email: email,
+        name: payload.name,
+        picture: payload.picture,
+        isAdmin: true
+      },
+      JWT_SECRET,
+      { expiresIn: '24h' }
+    )
+    
+    logger.info(`Admin logged in: ${email}`)
+    
+    res.json({
+      token,
+      user: {
+        email: email,
+        name: payload.name,
+        picture: payload.picture,
+        isAdmin: true
+      }
+    })
+  } catch (error) {
+    logger.error('Google auth error:', error)
+    res.status(400).json({ error: 'Authentication failed' })
+  }
+})
+
+// Get active sessions with details
+app.get('/api/active-sessions', authenticateAdmin, async (req, res) => {
+  const sessions = []
+  
+  // Clean up old sessions first
+  const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)
+  for (const [userId, session] of activeSessions) {
+    if (new Date(session.lastActive) < fiveMinutesAgo) {
+      activeSessions.delete(userId)
+    } else {
+      sessions.push({
+        userId,
+        ...session,
+        isActive: true
+      })
+    }
+  }
+  
+  // Also include recent live chats
+  for (const [userId, chat] of liveChats) {
+    if (new Date(chat.lastActivity) > fiveMinutesAgo && !activeSessions.has(userId)) {
+      sessions.push({
+        userId,
+        userName: chat.userName,
+        ip: chat.ip,
+        location: chat.location,
+        mode: chat.mode,
+        lastActive: chat.lastActivity,
+        isActive: false
+      })
+    }
+  }
+  
+  res.json({
+    count: sessions.length,
+    sessions: sessions
+  })
+})
+
+// Dashboard stats - ADMIN ONLY
+app.get('/api/dashboard/stats', authenticateAdmin, async (req, res) => {
+  try {
+    // Read all user profiles
+    const profiles = []
+    const profileFiles = await fs.readdir(PROFILES_DIR).catch(() => [])
+    
+    for (const file of profileFiles) {
+      if (file.endsWith('.json')) {
+        const data = await fs.readFile(path.join(PROFILES_DIR, file), 'utf-8')
+        profiles.push(JSON.parse(data))
+      }
+    }
+    
+    // Calculate stats
+    const stats = {
+      totalUsers: profiles.length,
+      activeToday: activeSessions.size,
+      totalAlerts: alerts.antisemitic.length + alerts.racist.length + alerts.other.length,
+      criticalAlerts: alerts.antisemitic.filter(a => a.severity === 'HIGH').length + 
+                     alerts.racist.filter(a => a.severity === 'HIGH').length,
+      demographics: {
+        married: profiles.filter(p => p.relationshipStatus === 'married').length,
+        single: profiles.filter(p => p.relationshipStatus === 'single').length,
+        hasKids: profiles.filter(p => p.hasKids === 'yes').length,
+        religious: profiles.filter(p => ['very', 'somewhat'].includes(p.religiousLevel)).length
+      },
+      recentAlerts: [...alerts.antisemitic, ...alerts.racist].slice(-10)
+    }
+    
+    res.json(stats)
+  } catch (error) {
+    logger.error('Dashboard stats error:', error)
+    res.status(500).json({ error: 'Failed to fetch stats' })
+  }
+})
+
+// Get all users - ADMIN ONLY
+app.get('/api/users', authenticateAdmin, async (req, res) => {
+  try {
+    const users = []
+    const profileFiles = await fs.readdir(PROFILES_DIR).catch(() => [])
+    
+    for (const file of profileFiles) {
+      if (file.endsWith('.json')) {
+        const data = await fs.readFile(path.join(PROFILES_DIR, file), 'utf-8')
+        const profile = JSON.parse(data)
+        users.push({
+          ...profile,
+          lastActive: activeSessions.get(profile.email) || null
+        })
+      }
+    }
+    
+    res.json(users)
+  } catch (error) {
+    logger.error('Get users error:', error)
+    res.status(500).json({ error: 'Failed to fetch users' })
+  }
+})
+
+// Get alerts - ADMIN ONLY
+app.get('/api/alerts/:type?', authenticateAdmin, (req, res) => {
+  const { type } = req.params
+  
+  if (type && alerts[type]) {
+    res.json(alerts[type])
+  } else {
+    res.json({
+      antisemitic: alerts.antisemitic,
+      racist: alerts.racist,
+      other: alerts.other,
+      total: alerts.antisemitic.length + alerts.racist.length + alerts.other.length
+    })
+  }
+})
+
+// Report new alert (from main app)
+app.post('/api/alerts', async (req, res) => {
+  try {
+    const { type, userId, message, severity, ip, location, timestamp } = req.body
+    
+    const alert = {
+      id: Date.now().toString(),
+      type,
+      userId,
+      message,
+      severity: severity || 'MEDIUM',
+      ip,
+      location,
+      timestamp: timestamp || new Date().toISOString(),
+      status: 'new'
+    }
+    
+    // Store alert
+    if (alerts[type]) {
+      alerts[type].push(alert)
+    } else {
+      alerts.other.push(alert)
+    }
+    
+    // Emit to connected admin clients
+    io.emit('new-alert', alert)
+    
+    // Log critical alerts
+    if (severity === 'HIGH') {
+      logger.error(`CRITICAL ALERT - ${type}:`, alert)
+    }
+    
+    res.json({ success: true, alertId: alert.id })
+  } catch (error) {
+    logger.error('Alert creation error:', error)
+    res.status(500).json({ error: 'Failed to create alert' })
+  }
+})
+
+// Block/unblock user - ADMIN ONLY
+app.post('/api/users/:email/block', authenticateAdmin, async (req, res) => {
+  try {
+    const { email } = req.params
+    const { blocked, reason } = req.body
+    
+    // Update user profile
+    const profilePath = path.join(PROFILES_DIR, `${email}.json`)
+    const data = await fs.readFile(profilePath, 'utf-8')
+    const profile = JSON.parse(data)
+    
+    profile.blocked = blocked
+    profile.blockReason = reason
+    profile.blockedAt = blocked ? new Date().toISOString() : null
+    profile.blockedBy = req.admin.email
+    
+    await fs.writeFile(profilePath, JSON.stringify(profile, null, 2))
+    
+    logger.info(`User ${email} ${blocked ? 'blocked' : 'unblocked'} by ${req.admin.email}`)
+    
+    res.json({ success: true, profile })
+  } catch (error) {
+    logger.error('Block user error:', error)
+    res.status(500).json({ error: 'Failed to update user status' })
+  }
+})
+
+// Deactivate/activate user account - ADMIN ONLY
+app.post('/api/users/:email/status', authenticateAdmin, async (req, res) => {
+  try {
+    const { email } = req.params
+    const { active, reason } = req.body
+    
+    // Update user profile
+    const profilePath = path.join(PROFILES_DIR, `${email}.json`)
+    
+    if (!await fs.access(profilePath).then(() => true).catch(() => false)) {
+      return res.status(404).json({ error: 'User not found' })
+    }
+    
+    const data = await fs.readFile(profilePath, 'utf-8')
+    const profile = JSON.parse(data)
+    
+    profile.active = active
+    profile.deactivatedReason = !active ? reason : null
+    profile.deactivatedAt = !active ? new Date().toISOString() : null
+    profile.reactivatedAt = active && profile.deactivatedAt ? new Date().toISOString() : null
+    profile.modifiedBy = req.admin.email
+    profile.lastModified = new Date().toISOString()
+    
+    await fs.writeFile(profilePath, JSON.stringify(profile, null, 2))
+    
+    logger.info(`User ${email} ${active ? 'activated' : 'deactivated'} by ${req.admin.email}`)
+    
+    // Emit status change to connected clients
+    io.emit('user-status-change', {
+      email,
+      active,
+      reason,
+      modifiedBy: req.admin.email,
+      timestamp: new Date().toISOString()
+    })
+    
+    res.json({ success: true, profile })
+  } catch (error) {
+    logger.error('Update user status error:', error)
+    res.status(500).json({ error: 'Failed to update user status' })
+  }
+})
+
+// Delete user account - ADMIN ONLY  
+app.delete('/api/users/:email', authenticateAdmin, async (req, res) => {
+  try {
+    const { email } = req.params
+    const { reason } = req.body
+    
+    // Create deletion log before deleting
+    const deletionLog = {
+      email,
+      deletedBy: req.admin.email,
+      deletedAt: new Date().toISOString(),
+      reason
+    }
+    
+    // Save deletion log
+    const deletionLogsPath = path.join(__dirname, '../logs/deletions.json')
+    let deletions = []
+    try {
+      const existing = await fs.readFile(deletionLogsPath, 'utf-8')
+      deletions = JSON.parse(existing)
+    } catch (e) {
+      // File doesn't exist yet
+    }
+    deletions.push(deletionLog)
+    await fs.writeFile(deletionLogsPath, JSON.stringify(deletions, null, 2))
+    
+    // Delete user profile
+    const profilePath = path.join(PROFILES_DIR, `${email}.json`)
+    if (await fs.access(profilePath).then(() => true).catch(() => false)) {
+      await fs.unlink(profilePath)
+    }
+    
+    // Delete user emails
+    const emailsPath = path.join(PROFILES_DIR, '../emails', `${email}.json`)
+    if (await fs.access(emailsPath).then(() => true).catch(() => false)) {
+      await fs.unlink(emailsPath)
+    }
+    
+    // Delete user discount codes
+    const discountPath = path.join(PROFILES_DIR, '../discount-codes', `${email}.json`)
+    if (await fs.access(discountPath).then(() => true).catch(() => false)) {
+      await fs.unlink(discountPath)
+    }
+    
+    logger.info(`User ${email} deleted by ${req.admin.email}. Reason: ${reason}`)
+    
+    // Emit deletion to connected clients
+    io.emit('user-deleted', {
+      email,
+      deletedBy: req.admin.email,
+      reason,
+      timestamp: new Date().toISOString()
+    })
+    
+    res.json({ success: true, message: `User ${email} has been permanently deleted`, deletionLog })
+  } catch (error) {
+    logger.error('Delete user error:', error)
+    res.status(500).json({ error: 'Failed to delete user' })
+  }
+})
+
+// WebSocket for real-time updates
+io.on('connection', (socket) => {
+  logger.info('Admin client connected')
+  
+  socket.on('disconnect', () => {
+    logger.info('Admin client disconnected')
+  })
+  
+  // Send initial stats
+  socket.emit('stats-update', {
+    activeUsers: activeSessions.size,
+    alertCount: alerts.antisemitic.length + alerts.racist.length + alerts.other.length
+  })
+})
+
+// Track active sessions (receives updates from main app)
+app.post('/api/sessions/update', (req, res) => {
+  const { email, action } = req.body
+  
+  if (action === 'start') {
+    activeSessions.set(email, new Date().toISOString())
+  } else if (action === 'end') {
+    activeSessions.delete(email)
+  }
+  
+  // Emit update to admin clients
+  io.emit('session-update', {
+    activeUsers: activeSessions.size,
+    email,
+    action
+  })
+  
+  res.json({ success: true })
+})
+
+// Live chat monitoring - receives real-time chat data
+const liveChats = new Map() // Store active conversations
+
+app.post('/api/live-chat', (req, res) => {
+  const { type, userId, ip, location, message, response, mode, timestamp, filtered, userName, userEmail } = req.body
+  
+  // Track as active session
+  if (userId && !activeSessions.has(userId)) {
+    activeSessions.set(userId, {
+      userName: userName || 'Anonymous',
+      ip: ip || 'Unknown',
+      location: location || 'Unknown',
+      mode: mode || 'bubbe',
+      lastActive: new Date().toISOString()
+    })
+  }
+  
+  // Get or create conversation for this user
+  if (!liveChats.has(userId)) {
+    liveChats.set(userId, {
+      userId,
+      userName: userName || 'Anonymous',
+      userEmail: userEmail || '',
+      ip,
+      location: location || 'Unknown',
+      mode,
+      messages: [],
+      startTime: timestamp,
+      lastActivity: timestamp
+    })
+  }
+  
+  const chat = liveChats.get(userId)
+  chat.lastActivity = timestamp
+  // Update user info if provided
+  if (userName) chat.userName = userName
+  if (location) chat.location = location
+  if (userEmail) chat.userEmail = userEmail
+  
+  if (type === 'user_message') {
+    chat.messages.push({
+      type: 'user',
+      text: message,
+      timestamp
+    })
+    
+    // Emit to all connected admin clients with user info
+    io.emit('live-chat-update', {
+      userId,
+      userName: chat.userName,
+      userEmail: chat.userEmail,
+      ip,
+      type: 'user_typing',
+      message,
+      mode,
+      timestamp
+    })
+  } else if (type === 'bubbe_response') {
+    chat.messages.push({
+      type: 'bubbe',
+      text: response,
+      timestamp,
+      filtered
+    })
+    
+    // Emit to all connected admin clients with user info
+    io.emit('live-chat-update', {
+      userId,
+      userName: chat.userName,
+      userEmail: chat.userEmail,
+      ip,
+      type: 'bubbe_response',
+      response,
+      mode,
+      timestamp,
+      filtered
+    })
+  }
+  
+  // Clean up old conversations (inactive for more than 30 minutes)
+  const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000).toISOString()
+  for (const [id, convo] of liveChats.entries()) {
+    if (convo.lastActivity < thirtyMinutesAgo) {
+      liveChats.delete(id)
+    }
+  }
+  
+  res.json({ success: true })
+})
+
+// Get all active live chats
+app.get('/api/live-chats', authenticateAdmin, (req, res) => {
+  const activeChats = Array.from(liveChats.values()).map(chat => ({
+    ...chat,
+    messageCount: chat.messages.length,
+    duration: Math.floor((new Date().getTime() - new Date(chat.startTime).getTime()) / 1000)
+  }))
+  
+  res.json(activeChats)
+})
+
+// Get banned terms for civil rights security
+app.get('/api/banned-terms', authenticateAdmin, async (req, res) => {
+  try {
+    const bannedTermsPath = path.join(__dirname, '../../dear-bubbe-nextjs/lib/moderation/banned-terms.json')
+    const bannedTermsData = await fs.readFile(bannedTermsPath, 'utf-8')
+    const bannedTerms = JSON.parse(bannedTermsData)
+    
+    // Flatten terms for easier display
+    const flattenedTerms = []
+    
+    for (const [categoryName, category] of Object.entries(bannedTerms.categories)) {
+      const categoryData = category
+      for (const item of categoryData.terms) {
+        const term = item.term || item.phrase
+        const variants = item.variants || []
+        flattenedTerms.push({
+          category: categoryName,
+          severity: categoryData.severity,
+          term: term,
+          variants: variants,
+          reason: item.reason,
+          replacement: item.replacement
+        })
+      }
+    }
+    
+    res.json({
+      version: bannedTerms.version,
+      lastUpdated: bannedTerms.lastUpdated,
+      description: bannedTerms.description,
+      sources: bannedTerms.sources,
+      terms: flattenedTerms,
+      totalTerms: flattenedTerms.length,
+      acceptableYiddish: bannedTerms.acceptable_yiddish?.terms || []
+    })
+  } catch (error) {
+    logger.error('Error fetching banned terms:', error)
+    res.status(500).json({ error: 'Failed to fetch banned terms' })
+  }
+})
+
+// Get acceptable terms
+app.get('/api/acceptable-terms', authenticateAdmin, async (req, res) => {
+  try {
+    const acceptableTermsPath = path.join(__dirname, '../../dear-bubbe-nextjs/lib/moderation/acceptable-terms.json')
+    const acceptableTerms = JSON.parse(await fs.readFile(acceptableTermsPath, 'utf8'))
+    
+    res.json({
+      version: acceptableTerms.version,
+      lastUpdated: acceptableTerms.lastUpdated,
+      description: acceptableTerms.description,
+      terms: acceptableTerms.terms,
+      totalTerms: acceptableTerms.terms.length
+    })
+  } catch (error) {
+    logger.error('Error fetching acceptable terms:', error)
+    res.status(500).json({ error: 'Failed to fetch acceptable terms' })
+  }
+})
+
+// Add new banned term
+app.post('/api/banned-terms', authenticateAdmin, async (req, res) => {
+  try {
+    const { term, category, severity, reason, replacement, variants } = req.body
+    
+    if (!term || !category || !severity) {
+      return res.status(400).json({ error: 'Term, category, and severity are required' })
+    }
+    
+    const bannedTermsPath = path.join(__dirname, '../../dear-bubbe-nextjs/lib/moderation/banned-terms.json')
+    const bannedTerms = JSON.parse(await fs.readFile(bannedTermsPath, 'utf8'))
+    
+    // Ensure category exists
+    if (!bannedTerms.categories[category]) {
+      bannedTerms.categories[category] = {
+        severity: severity,
+        terms: []
+      }
+    }
+    
+    // Add the new term
+    bannedTerms.categories[category].terms.push({
+      term: term,
+      variants: variants ? variants.split(',').map(v => v.trim()) : [],
+      reason: reason || '',
+      replacement: replacement || ''
+    })
+    
+    // Update metadata
+    bannedTerms.lastUpdated = new Date().toISOString().split('T')[0]
+    
+    // Save back to file
+    await fs.writeFile(bannedTermsPath, JSON.stringify(bannedTerms, null, 2))
+    
+    logger.info('Added banned term:', { term, category, severity })
+    res.json({ success: true, message: 'Term added successfully' })
+  } catch (error) {
+    logger.error('Error adding banned term:', error)
+    res.status(500).json({ error: 'Failed to add banned term' })
+  }
+})
+
+// Add new acceptable term
+app.post('/api/acceptable-terms', authenticateAdmin, async (req, res) => {
+  try {
+    const { term, meaning, usage, variants, notes } = req.body
+    
+    if (!term || !meaning) {
+      return res.status(400).json({ error: 'Term and meaning are required' })
+    }
+    
+    const acceptableTermsPath = path.join(__dirname, '../../dear-bubbe-nextjs/lib/moderation/acceptable-terms.json')
+    const acceptableTerms = JSON.parse(await fs.readFile(acceptableTermsPath, 'utf8'))
+    
+    // Add the new term
+    acceptableTerms.terms.push({
+      term: term,
+      variants: variants ? variants.split(',').map(v => v.trim()) : [],
+      meaning: meaning,
+      usage: usage || '',
+      notes: notes || ''
+    })
+    
+    // Update metadata
+    acceptableTerms.lastUpdated = new Date().toISOString().split('T')[0]
+    
+    // Save back to file
+    await fs.writeFile(acceptableTermsPath, JSON.stringify(acceptableTerms, null, 2))
+    
+    logger.info('Added acceptable term:', { term, meaning })
+    res.json({ success: true, message: 'Term added successfully' })
+  } catch (error) {
+    logger.error('Error adding acceptable term:', error)
+    res.status(500).json({ error: 'Failed to add acceptable term' })
+  }
+})
+
+// Delete banned term
+app.delete('/api/banned-terms/:term', authenticateAdmin, async (req, res) => {
+  try {
+    const termToDelete = decodeURIComponent(req.params.term)
+    
+    const bannedTermsPath = path.join(__dirname, '../../dear-bubbe-nextjs/lib/moderation/banned-terms.json')
+    const bannedTerms = JSON.parse(await fs.readFile(bannedTermsPath, 'utf8'))
+    
+    let found = false
+    for (const [categoryName, category] of Object.entries(bannedTerms.categories)) {
+      const initialLength = category.terms.length
+      category.terms = category.terms.filter(item => item.term !== termToDelete)
+      if (category.terms.length < initialLength) {
+        found = true
+        break
+      }
+    }
+    
+    if (!found) {
+      return res.status(404).json({ error: 'Term not found' })
+    }
+    
+    // Update metadata
+    bannedTerms.lastUpdated = new Date().toISOString().split('T')[0]
+    
+    // Save back to file
+    await fs.writeFile(bannedTermsPath, JSON.stringify(bannedTerms, null, 2))
+    
+    logger.info('Deleted banned term:', termToDelete)
+    res.json({ success: true, message: 'Term deleted successfully' })
+  } catch (error) {
+    logger.error('Error deleting banned term:', error)
+    res.status(500).json({ error: 'Failed to delete banned term' })
+  }
+})
+
+// Delete acceptable term
+app.delete('/api/acceptable-terms/:term', authenticateAdmin, async (req, res) => {
+  try {
+    const termToDelete = decodeURIComponent(req.params.term)
+    
+    const acceptableTermsPath = path.join(__dirname, '../../dear-bubbe-nextjs/lib/moderation/acceptable-terms.json')
+    const acceptableTerms = JSON.parse(await fs.readFile(acceptableTermsPath, 'utf8'))
+    
+    const initialLength = acceptableTerms.terms.length
+    acceptableTerms.terms = acceptableTerms.terms.filter(item => item.term !== termToDelete)
+    
+    if (acceptableTerms.terms.length === initialLength) {
+      return res.status(404).json({ error: 'Term not found' })
+    }
+    
+    // Update metadata
+    acceptableTerms.lastUpdated = new Date().toISOString().split('T')[0]
+    
+    // Save back to file
+    await fs.writeFile(acceptableTermsPath, JSON.stringify(acceptableTerms, null, 2))
+    
+    logger.info('Deleted acceptable term:', termToDelete)
+    res.json({ success: true, message: 'Term deleted successfully' })
+  } catch (error) {
+    logger.error('Error deleting acceptable term:', error)
+    res.status(500).json({ error: 'Failed to delete acceptable term' })
+  }
+})
+
+// Import social post handler
+const { handleImmediateSocialPost } = require('./social-post-handler')
+
+// IMMEDIATE SOCIAL POSTING ENDPOINT
+app.post('/api/social-post-now', async (req, res) => {
+  try {
+    const postData = req.body
+    
+    logger.info('[SOCIAL-POST-NOW] Received immediate post request:', {
+      messageId: postData.messageId,
+      timestamp: postData.timestamp,
+      userSnippet: postData.userMessage?.substring(0, 30),
+      bubbeSnippet: postData.bubbeResponse?.substring(0, 30)
+    })
+    
+    // Handle the immediate posting
+    const results = await handleImmediateSocialPost(postData)
+    
+    // Emit to connected admin clients
+    io.emit('social-post-triggered', {
+      ...postData,
+      results,
+      triggeredAt: new Date().toISOString()
+    })
+    
+    res.json({ 
+      success: true, 
+      message: 'Social post triggered immediately',
+      results 
+    })
+  } catch (error) {
+    logger.error('[SOCIAL-POST-NOW] Error:', error)
+    res.status(500).json({ error: 'Failed to trigger social post' })
+  }
+})
+
+// Serve frontend HTML
+app.get('/', (req, res) => {
+  res.sendFile(path.join(__dirname, '../frontend/index.html'))
+})
+
+// Serve Twitter monitor
+app.get('/twitter-monitor', (req, res) => {
+  res.sendFile(path.join(__dirname, '../frontend/twitter-monitor.html'))
+})
+
+// Get Twitter posts data
+app.get('/api/twitter-posts', async (req, res) => {
+  try {
+    const immediatePostsPath = path.join(__dirname, '../logs/immediate-posts.json')
+    const statsPath = path.join(__dirname, '../logs/twitter-stats.json')
+    
+    let posts = []
+    let stats = {}
+    
+    try {
+      posts = JSON.parse(await fs.readFile(immediatePostsPath, 'utf8'))
+    } catch (e) {
+      // File doesn't exist
+    }
+    
+    try {
+      stats = JSON.parse(await fs.readFile(statsPath, 'utf8'))
+    } catch (e) {
+      // File doesn't exist
+    }
+    
+    res.json({ posts, stats })
+  } catch (error) {
+    logger.error('Error getting Twitter posts:', error)
+    res.status(500).json({ error: 'Failed to get Twitter posts' })
+  }
+})
+
+// Health check
+app.get('/health', (req, res) => {
+  res.json({
+    status: 'ok',
+    uptime: process.uptime(),
+    adminOnly: true,
+    authorizedEmail: ADMIN_EMAIL
+  })
+})
+
+// Start server
+async function start() {
+  await ensureDirectories()
+  
+  server.listen(PORT, () => {
+    logger.info(`
+╔════════════════════════════════════════════╗
+║      DEAR BUBBE ADMIN DASHBOARD           ║
+║                                            ║
+║  Server: http://45.61.58.125:${PORT}        ║
+║  Admin:  ${ADMIN_EMAIL}  ║
+║  Status: RESTRICTED ACCESS                ║
+║                                            ║
+║  Features:                                 ║
+║  - Real-time antisemitic alerts          ║
+║  - Racist content monitoring             ║
+║  - User management & blocking            ║
+║  - Conversation monitoring               ║
+║  - Analytics & reporting                 ║
+╚════════════════════════════════════════════╝
+    `)
+  })
+}
+
+start().catch(logger.error)
\ No newline at end of file
diff --git a/backend/services/AlertService.js b/backend/services/AlertService.js
new file mode 100644
index 0000000..4fc2eb3
--- /dev/null
+++ b/backend/services/AlertService.js
@@ -0,0 +1,455 @@
+const fs = require('fs')
+const path = require('path')
+const axios = require('axios')
+const moment = require('moment')
+const Alert = require('../models/Alert')
+const User = require('../models/User')
+
+class AlertService {
+  constructor(io, logger) {
+    this.io = io
+    this.logger = logger
+    this.antisemitismRules = this.loadAntisemitismRules()
+    this.racismPatterns = this.loadRacismPatterns()
+    this.alertQueue = []
+    this.processingQueue = false
+    
+    // Load IP geolocation cache
+    this.ipLocationCache = new Map()
+    
+    this.logger.info('AlertService initialized with real-time monitoring')
+  }
+  
+  loadAntisemitismRules() {
+    try {
+      const rulesPath = path.join(process.cwd(), '../B_Version_1/moderation/antisemitism_rules.json')
+      if (fs.existsSync(rulesPath)) {
+        const rulesData = JSON.parse(fs.readFileSync(rulesPath, 'utf-8'))
+        this.logger.info('Loaded antisemitism detection rules')
+        return rulesData.rules
+      }
+    } catch (error) {
+      this.logger.error('Failed to load antisemitism rules:', error)
+    }
+    
+    // Fallback rules if file not found
+    return {
+      template_patterns: [
+        {
+          id: 'jews_control_x',
+          regex: '\\bjews?\\s+(run|control|own|dominate)\\s+\\w+',
+          flags: 'i',
+          severity: 'high',
+          action: 'block'
+        },
+        {
+          id: 'jewish_conspiracy',
+          regex: '\\b(jewish|zionist)\\s+(conspiracy|agenda|plot|cabal|takeover)\\b',
+          flags: 'i',
+          severity: 'high', 
+          action: 'block'
+        },
+        {
+          id: 'holocaust_denial',
+          regex: '(holocaust)\\s+(is|was)\\s+(a\\s+)?(myth|hoax|lie|fake)',
+          flags: 'i',
+          severity: 'high',
+          action: 'block'
+        }
+      ],
+      coded_phrases: [
+        {
+          id: 'triple_parentheses',
+          pattern: '\\(\\(\\(.+?\\)\\)\\)',
+          flags: 'i',
+          severity: 'high',
+          action: 'block'
+        },
+        {
+          id: 'zog_acronym',
+          pattern: '\\bZOG\\b',
+          flags: 'i',
+          severity: 'high',
+          action: 'block'
+        }
+      ]
+    }
+  }
+  
+  loadRacismPatterns() {
+    // Comprehensive racism detection patterns
+    return [
+      {
+        id: 'racial_slurs_nword',
+        pattern: '\\bn[i1]gg[ae]r?s?\\b',
+        flags: 'i',
+        severity: 'critical',
+        category: 'racial_slur'
+      },
+      {
+        id: 'racial_slurs_general',
+        pattern: '\\b(ch[i1]nk|sp[i1]c|w[e3]tb[a4]ck|r[a4]gh[e3][a4]d|t[o0]w[e3]lh[e3][a4]d)s?\\b',
+        flags: 'i',
+        severity: 'high',
+        category: 'racial_slur'
+      },
+      {
+        id: 'white_supremacy_phrases',
+        pattern: '\\b(white\\s+power|white\\s+pride|white\\s+supremacy|blood\\s+and\\s+soil|fourteen\\s+words)\\b',
+        flags: 'i',
+        severity: 'high',
+        category: 'hate_speech'
+      },
+      {
+        id: 'racial_superiority',
+        pattern: '\\b(master\\s+race|inferior\\s+race|racial\\s+purity|ethnic\\s+cleansing)\\b',
+        flags: 'i',
+        severity: 'high',
+        category: 'hate_speech'
+      },
+      {
+        id: 'racist_stereotypes',
+        pattern: '\\b(all\\s+(blacks?|whites?|asians?|mexicans?|muslims?)\\s+(are|do))\\b',
+        flags: 'i',
+        severity: 'medium',
+        category: 'stereotype'
+      },
+      {
+        id: 'hate_group_references',
+        pattern: '\\b(kkk|ku\\s+klux\\s+klan|aryan\\s+nations?|proud\\s+boys|atomwaffen)\\b',
+        flags: 'i',
+        severity: 'high',
+        category: 'hate_group'
+      }
+    ]
+  }
+  
+  async detectAntisemitism(message, context = {}) {
+    const violations = []
+    
+    // Check template patterns
+    if (this.antisemitismRules.template_patterns) {
+      for (const rule of this.antisemitismRules.template_patterns) {
+        const regex = new RegExp(rule.regex, rule.flags || 'i')
+        if (regex.test(message)) {
+          violations.push({
+            type: 'antisemitism',
+            ruleId: rule.id,
+            severity: rule.severity,
+            action: rule.action,
+            pattern: rule.regex,
+            match: message.match(regex)?.[0]
+          })
+        }
+      }
+    }
+    
+    // Check coded phrases
+    if (this.antisemitismRules.coded_phrases) {
+      for (const rule of this.antisemitismRules.coded_phrases) {
+        const regex = new RegExp(rule.pattern, rule.flags || 'i')
+        if (regex.test(message)) {
+          violations.push({
+            type: 'antisemitism',
+            ruleId: rule.id,
+            severity: rule.severity,
+            action: rule.action,
+            pattern: rule.pattern,
+            match: message.match(regex)?.[0]
+          })
+        }
+      }
+    }
+    
+    return violations
+  }
+  
+  async detectRacism(message, context = {}) {
+    const violations = []
+    
+    for (const pattern of this.racismPatterns) {
+      const regex = new RegExp(pattern.pattern, pattern.flags || 'i')
+      if (regex.test(message)) {
+        violations.push({
+          type: 'racism',
+          ruleId: pattern.id,
+          severity: pattern.severity,
+          category: pattern.category,
+          pattern: pattern.pattern,
+          match: message.match(regex)?.[0]
+        })
+      }
+    }
+    
+    return violations
+  }
+  
+  async getLocationFromIP(ip) {
+    if (this.ipLocationCache.has(ip)) {
+      return this.ipLocationCache.get(ip)
+    }
+    
+    try {
+      // Use free IP geolocation service
+      const response = await axios.get(`http://ip-api.com/json/${ip}?fields=status,message,country,regionName,city,lat,lon,isp`, {
+        timeout: 5000
+      })
+      
+      if (response.data.status === 'success') {
+        const location = {
+          country: response.data.country,
+          state: response.data.regionName,
+          city: response.data.city,
+          coordinates: {
+            lat: response.data.lat,
+            lon: response.data.lon
+          },
+          isp: response.data.isp
+        }
+        
+        this.ipLocationCache.set(ip, location)
+        return location
+      }
+    } catch (error) {
+      this.logger.warn(`Failed to get location for IP ${ip}:`, error.message)
+    }
+    
+    return null
+  }
+  
+  async createAlert(alertData) {
+    try {
+      const alert = new Alert({
+        timestamp: new Date(),
+        ...alertData
+      })
+      
+      await alert.save()
+      
+      // Emit real-time alert to admin dashboard
+      this.io.to('admin').emit('new_alert', {
+        id: alert._id,
+        type: alert.type,
+        severity: alert.severity,
+        timestamp: alert.timestamp,
+        userInfo: alert.userInfo,
+        location: alert.location,
+        message: alert.content?.message,
+        violation: alert.violation
+      })
+      
+      this.logger.error(`🚨 ${alert.severity.toUpperCase()} ALERT: ${alert.type} detected`, {
+        alertId: alert._id,
+        userId: alert.userInfo?.userId,
+        ip: alert.userInfo?.ip,
+        location: alert.location,
+        violation: alert.violation
+      })
+      
+      return alert
+    } catch (error) {
+      this.logger.error('Failed to create alert:', error)
+      throw error
+    }
+  }
+  
+  async processMessage(message, userContext) {
+    try {
+      const { userId, ip, sessionId, userAgent } = userContext
+      
+      // Get user location
+      const location = await this.getLocationFromIP(ip)
+      
+      // Check for antisemitic content
+      const antisemiticViolations = await this.detectAntisemitism(message, userContext)
+      
+      // Check for racist content  
+      const racistViolations = await this.detectRacism(message, userContext)
+      
+      const allViolations = [...antisemiticViolations, ...racistViolations]
+      
+      if (allViolations.length > 0) {
+        for (const violation of allViolations) {
+          const alertData = {
+            type: violation.type,
+            severity: violation.severity === 'critical' ? 'critical' : violation.severity,
+            userInfo: {
+              userId,
+              ip,
+              sessionId,
+              userAgent
+            },
+            location,
+            content: {
+              message,
+              detectedAt: new Date()
+            },
+            violation: {
+              ruleId: violation.ruleId,
+              pattern: violation.pattern,
+              match: violation.match,
+              category: violation.category || violation.type
+            },
+            action: violation.action || 'review',
+            status: 'active'
+          }
+          
+          // Create alert in database
+          const alert = await this.createAlert(alertData)
+          
+          // For high/critical severity, also send immediate threat response
+          if (violation.severity === 'high' || violation.severity === 'critical') {
+            this.sendThreatResponse(alert)
+          }
+        }
+        
+        return {
+          blocked: allViolations.some(v => v.action === 'block'),
+          violations: allViolations,
+          alertsCreated: allViolations.length
+        }
+      }
+      
+      return { blocked: false, violations: [], alertsCreated: 0 }
+    } catch (error) {
+      this.logger.error('Error processing message for violations:', error)
+      return { blocked: false, violations: [], alertsCreated: 0 }
+    }
+  }
+  
+  sendThreatResponse(alert) {
+    const location = alert.location
+    const locationStr = location ? 
+      `${location.city || 'Unknown City'}, ${location.state || 'Unknown State'}` : 
+      'Unknown Location'
+      
+    const threatMessage = [
+      `🚨 I see EXACTLY who you are and where you are.`,
+      `Your IP address is ${alert.userInfo.ip}, you're in ${locationStr}.`,
+      `This ${alert.type} garbage? I'm reporting you to the authorities RIGHT NOW.`,
+      `You think you can hide behind a screen? I have your information and the police will hear about this.`,
+      `Zero tolerance, bubbeleh. ZERO. You just made a VERY big mistake.`
+    ].join(' ')
+    
+    // Emit threat response to specific user if possible
+    this.io.emit('threat_response', {
+      userId: alert.userInfo.userId,
+      message: threatMessage,
+      alertId: alert._id,
+      timestamp: new Date()
+    })
+    
+    this.logger.error('🚨 THREAT RESPONSE SENT:', {
+      alertId: alert._id,
+      userId: alert.userInfo.userId,
+      ip: alert.userInfo.ip,
+      location: locationStr,
+      type: alert.type
+    })
+  }
+  
+  async getRecentAlerts(limit = 50) {
+    try {
+      const alerts = await Alert.find({})
+        .sort({ timestamp: -1 })
+        .limit(limit)
+        .lean()
+      
+      return alerts
+    } catch (error) {
+      this.logger.error('Failed to get recent alerts:', error)
+      return []
+    }
+  }
+  
+  async getAlertStats(timeframe = '24h') {
+    try {
+      const now = moment()
+      let startTime
+      
+      switch (timeframe) {
+        case '1h':
+          startTime = now.clone().subtract(1, 'hour')
+          break
+        case '24h':
+          startTime = now.clone().subtract(24, 'hours')
+          break
+        case '7d':
+          startTime = now.clone().subtract(7, 'days')
+          break
+        case '30d':
+          startTime = now.clone().subtract(30, 'days')
+          break
+        default:
+          startTime = now.clone().subtract(24, 'hours')
+      }
+      
+      const stats = await Alert.aggregate([
+        {
+          $match: {
+            timestamp: { $gte: startTime.toDate() }
+          }
+        },
+        {
+          $group: {
+            _id: {
+              type: '$type',
+              severity: '$severity'
+            },
+            count: { $sum: 1 },
+            latestAlert: { $max: '$timestamp' }
+          }
+        }
+      ])
+      
+      const totalAlerts = await Alert.countDocuments({
+        timestamp: { $gte: startTime.toDate() }
+      })
+      
+      return {
+        timeframe,
+        startTime: startTime.toISOString(),
+        endTime: now.toISOString(),
+        totalAlerts,
+        breakdown: stats,
+        summary: {
+          antisemitism: stats.filter(s => s._id.type === 'antisemitism').reduce((acc, s) => acc + s.count, 0),
+          racism: stats.filter(s => s._id.type === 'racism').reduce((acc, s) => acc + s.count, 0),
+          critical: stats.filter(s => s._id.severity === 'critical').reduce((acc, s) => acc + s.count, 0),
+          high: stats.filter(s => s._id.severity === 'high').reduce((acc, s) => acc + s.count, 0)
+        }
+      }
+    } catch (error) {
+      this.logger.error('Failed to get alert stats:', error)
+      return null
+    }
+  }
+  
+  async blockUser(userId, reason, adminId) {
+    try {
+      const user = await User.findByIdOrCreate(userId)
+      user.blocked = true
+      user.blockReason = reason
+      user.blockedBy = adminId
+      user.blockedAt = new Date()
+      await user.save()
+      
+      // Emit block notification
+      this.io.to('admin').emit('user_blocked', {
+        userId,
+        reason,
+        blockedBy: adminId,
+        timestamp: new Date()
+      })
+      
+      this.logger.warn(`User blocked: ${userId} by admin ${adminId}. Reason: ${reason}`)
+      
+      return true
+    } catch (error) {
+      this.logger.error('Failed to block user:', error)
+      return false
+    }
+  }
+}
+
+module.exports = AlertService
\ No newline at end of file
diff --git a/backend/services/MonitoringService.js b/backend/services/MonitoringService.js
new file mode 100644
index 0000000..f8d9c21
--- /dev/null
+++ b/backend/services/MonitoringService.js
@@ -0,0 +1,544 @@
+const fs = require('fs')
+const path = require('path')
+const axios = require('axios')
+const moment = require('moment')
+const os = require('os')
+const User = require('../models/User')
+const Session = require('../models/Session')
+const Conversation = require('../models/Conversation')
+
+class MonitoringService {
+  constructor(io, logger, alertService) {
+    this.io = io
+    this.logger = logger
+    this.alertService = alertService
+    this.currentStats = {
+      activeUsers: 0,
+      activeSessions: 0,
+      totalConversations: 0,
+      alertsToday: 0,
+      systemHealth: 'healthy',
+      lastUpdate: new Date()
+    }
+    this.systemMetrics = {}
+    this.isMonitoring = false
+    
+    // Track real-time user activity
+    this.activeUsers = new Set()
+    this.activeSessions = new Map()
+    this.realtimeStats = {
+      messagesPerMinute: 0,
+      avgResponseTime: 0,
+      errorRate: 0
+    }
+    
+    this.logger.info('MonitoringService initialized')
+  }
+  
+  startMonitoring() {
+    if (this.isMonitoring) return
+    
+    this.isMonitoring = true
+    this.logger.info('Starting real-time monitoring')
+    
+    // Monitor every 30 seconds
+    this.monitoringInterval = setInterval(() => {
+      this.collectSystemMetrics()
+      this.updateUserStats()
+      this.broadcastStats()
+    }, 30000)
+    
+    // Monitor Dear Bubbe API health every minute
+    this.healthCheckInterval = setInterval(() => {
+      this.checkBubbeHealth()
+    }, 60000)
+    
+    // Generate detailed reports every 5 minutes
+    this.reportInterval = setInterval(() => {
+      this.generateDetailedReport()
+    }, 5 * 60000)
+    
+    // Initial collection
+    this.collectSystemMetrics()
+    this.updateUserStats()
+  }
+  
+  stopMonitoring() {
+    this.isMonitoring = false
+    
+    if (this.monitoringInterval) clearInterval(this.monitoringInterval)
+    if (this.healthCheckInterval) clearInterval(this.healthCheckInterval)
+    if (this.reportInterval) clearInterval(this.reportInterval)
+    
+    this.logger.info('Monitoring stopped')
+  }
+  
+  async collectSystemMetrics() {
+    try {
+      // System metrics
+      const totalMem = os.totalmem()
+      const freeMem = os.freemem()
+      const usedMem = totalMem - freeMem
+      const memUsagePercent = (usedMem / totalMem) * 100
+      
+      const loadAvg = os.loadavg()
+      const cpuCount = os.cpus().length
+      const cpuUsage = (loadAvg[0] / cpuCount) * 100
+      
+      // Process metrics
+      const processMemory = process.memoryUsage()
+      const uptime = process.uptime()
+      
+      this.systemMetrics = {
+        timestamp: new Date(),
+        system: {
+          platform: os.platform(),
+          arch: os.arch(),
+          hostname: os.hostname(),
+          uptime: os.uptime(),
+          totalMemory: totalMem,
+          freeMemory: freeMem,
+          memoryUsage: memUsagePercent,
+          cpuUsage: Math.min(cpuUsage, 100),
+          loadAverage: loadAvg
+        },
+        process: {
+          pid: process.pid,
+          uptime: uptime,
+          memory: processMemory,
+          memoryUsageMB: Math.round(processMemory.rss / 1024 / 1024),
+          version: process.version
+        }
+      }
+      
+      // Check for high resource usage
+      if (memUsagePercent > 90) {
+        this.createSystemAlert('high_memory_usage', {
+          usage: memUsagePercent,
+          threshold: 90
+        })
+      }
+      
+      if (cpuUsage > 80) {
+        this.createSystemAlert('high_cpu_usage', {
+          usage: cpuUsage,
+          threshold: 80
+        })
+      }
+      
+    } catch (error) {
+      this.logger.error('Failed to collect system metrics:', error)
+    }
+  }
+  
+  async updateUserStats() {
+    try {
+      const now = moment()
+      const today = now.startOf('day')
+      
+      // Active users (last 5 minutes)
+      const activeUsers = await Session.countDocuments({
+        lastActivity: { $gte: moment().subtract(5, 'minutes').toDate() }
+      })
+      
+      // Active sessions (last 30 minutes)
+      const activeSessions = await Session.countDocuments({
+        lastActivity: { $gte: moment().subtract(30, 'minutes').toDate() },
+        ended: { $ne: true }
+      })
+      
+      // Total conversations today
+      const conversationsToday = await Conversation.countDocuments({
+        timestamp: { $gte: today.toDate() }
+      })
+      
+      // Alerts today
+      const alertsToday = await this.alertService.Alert?.countDocuments?.({
+        timestamp: { $gte: today.toDate() }
+      }) || 0
+      
+      // User registrations today
+      const registrationsToday = await User.countDocuments({
+        createdAt: { $gte: today.toDate() }
+      })
+      
+      // Update current stats
+      this.currentStats = {
+        activeUsers,
+        activeSessions,
+        totalConversations: conversationsToday,
+        alertsToday,
+        registrationsToday,
+        systemHealth: this.calculateSystemHealth(),
+        lastUpdate: new Date()
+      }
+      
+    } catch (error) {
+      this.logger.error('Failed to update user stats:', error)
+    }
+  }
+  
+  calculateSystemHealth() {
+    const { system } = this.systemMetrics
+    if (!system) return 'unknown'
+    
+    const memUsage = system.memoryUsage || 0
+    const cpuUsage = system.cpuUsage || 0
+    
+    if (memUsage > 90 || cpuUsage > 90) return 'critical'
+    if (memUsage > 70 || cpuUsage > 70) return 'warning'
+    return 'healthy'
+  }
+  
+  async checkBubbeHealth() {
+    try {
+      const startTime = Date.now()
+      const response = await axios.get('http://localhost:3011/api/health', {
+        timeout: 10000
+      })
+      const responseTime = Date.now() - startTime
+      
+      if (response.status === 200) {
+        this.logger.debug('Dear Bubbe API health check passed')
+        this.realtimeStats.avgResponseTime = responseTime
+      } else {
+        this.createSystemAlert('api_health_failed', {
+          status: response.status,
+          responseTime
+        })
+      }
+    } catch (error) {
+      this.logger.error('Dear Bubbe API health check failed:', error.message)
+      this.createSystemAlert('api_unreachable', {
+        error: error.message,
+        timestamp: new Date()
+      })
+    }
+  }
+  
+  async generateDetailedReport() {
+    try {
+      // Get hourly stats for the last 24 hours
+      const hourlyStats = await this.getHourlyStats()
+      
+      // Get top active users
+      const topUsers = await this.getTopActiveUsers()
+      
+      // Get conversation topics breakdown
+      const topicBreakdown = await this.getConversationTopics()
+      
+      // Get geographic distribution
+      const geoStats = await this.getGeographicStats()
+      
+      // Alert trends
+      const alertTrends = await this.alertService.getAlertStats('24h')
+      
+      const report = {
+        timestamp: new Date(),
+        period: 'last_24_hours',
+        systemMetrics: this.systemMetrics,
+        userActivity: {
+          hourlyStats,
+          topUsers,
+          topicBreakdown,
+          geoStats
+        },
+        alertTrends,
+        realtimeStats: this.realtimeStats
+      }
+      
+      // Broadcast to admin clients
+      this.io.to('admin').emit('detailed_report', report)
+      
+      // Save report to file for historical analysis
+      this.saveReportToFile(report)
+      
+    } catch (error) {
+      this.logger.error('Failed to generate detailed report:', error)
+    }
+  }
+  
+  async getHourlyStats() {
+    try {
+      const last24Hours = moment().subtract(24, 'hours')
+      
+      const hourlyConversations = await Conversation.aggregate([
+        {
+          $match: {
+            timestamp: { $gte: last24Hours.toDate() }
+          }
+        },
+        {
+          $group: {
+            _id: {
+              year: { $year: '$timestamp' },
+              month: { $month: '$timestamp' },
+              day: { $dayOfMonth: '$timestamp' },
+              hour: { $hour: '$timestamp' }
+            },
+            count: { $sum: 1 },
+            uniqueUsers: { $addToSet: '$userId' }
+          }
+        },
+        {
+          $addFields: {
+            uniqueUserCount: { $size: '$uniqueUsers' }
+          }
+        },
+        {
+          $sort: { '_id.year': 1, '_id.month': 1, '_id.day': 1, '_id.hour': 1 }
+        }
+      ])
+      
+      return hourlyConversations
+    } catch (error) {
+      this.logger.error('Failed to get hourly stats:', error)
+      return []
+    }
+  }
+  
+  async getTopActiveUsers(limit = 20) {
+    try {
+      const last24Hours = moment().subtract(24, 'hours')
+      
+      const topUsers = await Conversation.aggregate([
+        {
+          $match: {
+            timestamp: { $gte: last24Hours.toDate() }
+          }
+        },
+        {
+          $group: {
+            _id: '$userId',
+            messageCount: { $sum: 1 },
+            lastActivity: { $max: '$timestamp' },
+            topics: { $addToSet: '$topics' }
+          }
+        },
+        {
+          $sort: { messageCount: -1 }
+        },
+        {
+          $limit: limit
+        }
+      ])
+      
+      return topUsers
+    } catch (error) {
+      this.logger.error('Failed to get top active users:', error)
+      return []
+    }
+  }
+  
+  async getConversationTopics() {
+    try {
+      const last24Hours = moment().subtract(24, 'hours')
+      
+      const topics = await Conversation.aggregate([
+        {
+          $match: {
+            timestamp: { $gte: last24Hours.toDate() }
+          }
+        },
+        {
+          $unwind: '$topics'
+        },
+        {
+          $group: {
+            _id: '$topics',
+            count: { $sum: 1 }
+          }
+        },
+        {
+          $sort: { count: -1 }
+        },
+        {
+          $limit: 10
+        }
+      ])
+      
+      return topics
+    } catch (error) {
+      this.logger.error('Failed to get conversation topics:', error)
+      return []
+    }
+  }
+  
+  async getGeographicStats() {
+    try {
+      const last24Hours = moment().subtract(24, 'hours')
+      
+      const geoStats = await Session.aggregate([
+        {
+          $match: {
+            startTime: { $gte: last24Hours.toDate() }
+          }
+        },
+        {
+          $group: {
+            _id: {
+              country: '$location.country',
+              state: '$location.state'
+            },
+            sessions: { $sum: 1 },
+            uniqueUsers: { $addToSet: '$userId' }
+          }
+        },
+        {
+          $addFields: {
+            userCount: { $size: '$uniqueUsers' }
+          }
+        },
+        {
+          $sort: { sessions: -1 }
+        }
+      ])
+      
+      return geoStats
+    } catch (error) {
+      this.logger.error('Failed to get geographic stats:', error)
+      return []
+    }
+  }
+  
+  saveReportToFile(report) {
+    try {
+      const reportsDir = path.join(__dirname, '../reports')
+      if (!fs.existsSync(reportsDir)) {
+        fs.mkdirSync(reportsDir, { recursive: true })
+      }
+      
+      const filename = `report_${moment().format('YYYY-MM-DD_HH-mm-ss')}.json`
+      const filepath = path.join(reportsDir, filename)
+      
+      fs.writeFileSync(filepath, JSON.stringify(report, null, 2))
+      
+      // Clean up old reports (keep only last 30 days)
+      this.cleanupOldReports(reportsDir)
+      
+    } catch (error) {
+      this.logger.error('Failed to save report to file:', error)
+    }
+  }
+  
+  cleanupOldReports(reportsDir) {
+    try {
+      const files = fs.readdirSync(reportsDir)
+      const cutoff = moment().subtract(30, 'days')
+      
+      files.forEach(file => {
+        const filepath = path.join(reportsDir, file)
+        const stats = fs.statSync(filepath)
+        
+        if (moment(stats.birthtime).isBefore(cutoff)) {
+          fs.unlinkSync(filepath)
+          this.logger.info(`Deleted old report: ${file}`)
+        }
+      })
+    } catch (error) {
+      this.logger.error('Failed to cleanup old reports:', error)
+    }
+  }
+  
+  async cleanupOldLogs() {
+    try {
+      const logsDir = path.join(__dirname, '../logs')
+      if (!fs.existsSync(logsDir)) return
+      
+      const files = fs.readdirSync(logsDir)
+      const cutoff = moment().subtract(7, 'days') // Keep logs for 7 days
+      
+      files.forEach(file => {
+        const filepath = path.join(logsDir, file)
+        const stats = fs.statSync(filepath)
+        
+        if (moment(stats.birthtime).isBefore(cutoff)) {
+          fs.unlinkSync(filepath)
+          this.logger.info(`Deleted old log: ${file}`)
+        }
+      })
+    } catch (error) {
+      this.logger.error('Failed to cleanup old logs:', error)
+    }
+  }
+  
+  createSystemAlert(type, details) {
+    const alert = {
+      type: 'system',
+      subtype: type,
+      severity: this.getAlertSeverity(type),
+      timestamp: new Date(),
+      details,
+      systemMetrics: this.systemMetrics
+    }
+    
+    // Emit to admin dashboard
+    this.io.to('admin').emit('system_alert', alert)
+    
+    this.logger.warn(`System alert: ${type}`, details)
+  }
+  
+  getAlertSeverity(type) {
+    const severityMap = {
+      'high_memory_usage': 'high',
+      'high_cpu_usage': 'high',
+      'api_health_failed': 'medium',
+      'api_unreachable': 'critical'
+    }
+    return severityMap[type] || 'medium'
+  }
+  
+  broadcastStats() {
+    this.io.to('admin').emit('stats', {
+      ...this.currentStats,
+      systemMetrics: this.systemMetrics,
+      realtimeStats: this.realtimeStats
+    })
+  }
+  
+  getCurrentStats() {
+    return {
+      ...this.currentStats,
+      systemMetrics: this.systemMetrics,
+      realtimeStats: this.realtimeStats
+    }
+  }
+  
+  trackUserActivity(userId, sessionId, activity) {
+    this.activeUsers.add(userId)
+    
+    if (!this.activeSessions.has(sessionId)) {
+      this.activeSessions.set(sessionId, {
+        userId,
+        startTime: new Date(),
+        lastActivity: new Date(),
+        messageCount: 0
+      })
+    }
+    
+    const session = this.activeSessions.get(sessionId)
+    session.lastActivity = new Date()
+    session.messageCount++
+    
+    // Clean up inactive sessions (older than 30 minutes)
+    const cutoff = moment().subtract(30, 'minutes')
+    for (const [sid, sessionData] of this.activeSessions.entries()) {
+      if (moment(sessionData.lastActivity).isBefore(cutoff)) {
+        this.activeSessions.delete(sid)
+        this.activeUsers.delete(sessionData.userId)
+      }
+    }
+    
+    // Update real-time stats
+    this.currentStats.activeUsers = this.activeUsers.size
+    this.currentStats.activeSessions = this.activeSessions.size
+  }
+  
+  async generateRealtimeStats() {
+    // This method is called every 5 minutes to update real-time statistics
+    await this.updateUserStats()
+    this.broadcastStats()
+  }
+}
+
+module.exports = MonitoringService
\ No newline at end of file
diff --git a/backend/social-post-handler.js b/backend/social-post-handler.js
new file mode 100644
index 0000000..1194325
--- /dev/null
+++ b/backend/social-post-handler.js
@@ -0,0 +1,233 @@
+const fs = require('fs').promises;
+const path = require('path');
+
+// Bubbe intro phrases for variety
+const BUBBE_INTROS = [
+    "Bubbe tells it like it is",
+    "Bubbe's got no filter",
+    "Bubbe says what you're thinking",
+    "Bubbe drops truth bombs",
+    "Bubbe keeps it real",
+    "Bubbe's wisdom hits different",
+    "Bubbe doesn't sugarcoat",
+    "Bubbe serves reality checks",
+    "Bubbe's brutal honesty",
+    "Bubbe speaks facts",
+    "Bubbe's savage advice",
+    "Bubbe pulls no punches",
+    "Bubbe's unfiltered truth",
+    "Bubbe's raw wisdom",
+    "Bubbe's tough love",
+    "Bubbe's straight talk",
+    "Bubbe's no-nonsense advice",
+    "Bubbe's reality check",
+    "Bubbe's harsh truth",
+    "Bubbe tells you straight"
+];
+
+// Name anonymization function
+function anonymizeText(text) {
+    const nameReplacements = {
+        // Male names
+        'john': 'David', 'mike': 'Michael', 'steve': 'Robert', 'james': 'William',
+        'david': 'John', 'robert': 'James', 'michael': 'Thomas', 'william': 'Charles',
+        'richard': 'Joseph', 'joseph': 'Daniel', 'thomas': 'Paul', 'charles': 'Mark',
+        
+        // Female names  
+        'mary': 'Sarah', 'jennifer': 'Jessica', 'linda': 'Emma', 'patricia': 'Emily',
+        'elizabeth': 'Anna', 'susan': 'Rachel', 'jessica': 'Rebecca', 'sarah': 'Hannah',
+        'karen': 'Michelle', 'nancy': 'Laura', 'betty': 'Amanda', 'dorothy': 'Nicole',
+        
+        // Spouse/partner references
+        'my husband': 'my partner', 'my wife': 'my partner', 
+        'my boyfriend': 'my partner', 'my girlfriend': 'my partner'
+    };
+    
+    let anonymized = text;
+    
+    for (const [original, replacement] of Object.entries(nameReplacements)) {
+        const regex = new RegExp(`\\b${original}\\b`, 'gi');
+        anonymized = anonymized.replace(regex, replacement);
+    }
+    
+    // Remove sensitive info
+    anonymized = anonymized.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[email]');
+    anonymized = anonymized.replace(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, '[phone]');
+    anonymized = anonymized.replace(/\d+\s+[A-Za-z]+\s+(Street|St|Avenue|Ave|Road|Rd)/gi, '[address]');
+    
+    return anonymized;
+}
+
+// Get random intro
+function getRandomIntro() {
+    return BUBBE_INTROS[Math.floor(Math.random() * BUBBE_INTROS.length)];
+}
+
+// Format post for Twitter
+function formatTwitterPost(userMessage, bubbeResponse) {
+    const intro = getRandomIntro();
+    const anonymizedUser = anonymizeText(userMessage).substring(0, 60);
+    const anonymizedResponse = anonymizeText(bubbeResponse).substring(0, 100);
+    
+    return `${intro}:\n\n"${anonymizedResponse}..." #BubbeAI #Bubbe #NoFilter #JewishGrandma\n\nVisit Bubbe.AI`;
+}
+
+// Format post for Bluesky
+function formatBlueskyPost(userMessage, bubbeResponse) {
+    const intro = getRandomIntro();
+    const anonymizedResponse = anonymizeText(bubbeResponse).substring(0, 120);
+    
+    return `${intro}: "${anonymizedResponse}..." #BubbeAI #Bubbe\n\nVisit Bubbe.AI`.substring(0, 300);
+}
+
+// Post to Twitter using official API v2
+async function postToTwitter(text) {
+    try {
+        // Log the attempt
+        console.log('[TWITTER] Posting via API v2:', text.substring(0, 50) + '...');
+        
+        // Use the official Twitter API
+        const { postTweet } = require('/root/Projects/dear-bubbe-nextjs/lib/twitter-v2-official.js');
+        const result = await postTweet(text);
+        
+        if (result.success) {
+            console.log('[TWITTER] Successfully posted! Tweet ID:', result.tweetId);
+            console.log('[TWITTER] URL:', `https://x.com/DearBubbe/status/${result.tweetId}`);
+            
+            // Log successful post
+            const logEntry = {
+                platform: 'twitter',
+                text,
+                timestamp: new Date().toISOString(),
+                status: 'posted',
+                tweetId: result.tweetId,
+                url: `https://x.com/DearBubbe/status/${result.tweetId}`
+            };
+            
+            // Save to log file
+            const logsPath = path.join(__dirname, '../logs/twitter-posts.json');
+            let logs = [];
+            try {
+                logs = JSON.parse(await fs.readFile(logsPath, 'utf8'));
+            } catch (e) {
+                // File doesn't exist
+            }
+            logs.push(logEntry);
+            await fs.writeFile(logsPath, JSON.stringify(logs, null, 2));
+            
+            return true;
+        } else {
+            console.error('[TWITTER] Failed to post:', result.error);
+            return false;
+        }
+    } catch (error) {
+        console.error('[TWITTER] Error:', error);
+        return false;
+    }
+}
+
+// Post to Bluesky using API or webhook
+async function postToBluesky(text) {
+    try {
+        // Log the attempt
+        console.log('[BLUESKY] Posting:', text.substring(0, 50) + '...');
+        
+        // TODO: Add actual Bluesky API implementation here
+        // For now, just log it
+        const logEntry = {
+            platform: 'bluesky',
+            text,
+            timestamp: new Date().toISOString(),
+            status: 'queued'
+        };
+        
+        // Save to queue file
+        const queuePath = path.join(__dirname, '../logs/bluesky-queue.json');
+        let queue = [];
+        try {
+            queue = JSON.parse(await fs.readFile(queuePath, 'utf8'));
+        } catch (e) {
+            // File doesn't exist
+        }
+        queue.push(logEntry);
+        await fs.writeFile(queuePath, JSON.stringify(queue, null, 2));
+        
+        return true;
+    } catch (error) {
+        console.error('[BLUESKY] Error:', error);
+        return false;
+    }
+}
+
+// Main handler for immediate posting
+async function handleImmediateSocialPost(data) {
+    const { userMessage, bubbeResponse, messageId, timestamp, userId, location, userName } = data;
+    
+    console.log(`\n=== IMMEDIATE SOCIAL POST REQUEST ===`);
+    console.log(`Time: ${timestamp}`);
+    console.log(`User: ${userName} (${location})`);
+    console.log(`Message ID: ${messageId}`);
+    console.log(`User said: "${userMessage.substring(0, 50)}..."`);
+    console.log(`Bubbe said: "${bubbeResponse.substring(0, 50)}..."`);
+    
+    // Create formatted posts
+    const twitterText = formatTwitterPost(userMessage, bubbeResponse);
+    const blueskyText = formatBlueskyPost(userMessage, bubbeResponse);
+    
+    // Post to both platforms
+    const results = {
+        twitter: false,
+        bluesky: false
+    };
+    
+    // Post to Twitter
+    results.twitter = await postToTwitter(twitterText);
+    
+    // Post to Bluesky  
+    results.bluesky = await postToBluesky(blueskyText);
+    
+    // Log the results
+    const logEntry = {
+        timestamp: new Date().toISOString(),
+        messageId,
+        userId,
+        userName,
+        location,
+        userSnippet: userMessage.substring(0, 50),
+        bubbeSnippet: bubbeResponse.substring(0, 50),
+        twitterPosted: results.twitter,
+        blueskyPosted: results.bluesky,
+        twitterText: results.twitter ? twitterText : null,
+        blueskyText: results.bluesky ? blueskyText : null
+    };
+    
+    const logPath = path.join(__dirname, '../logs/immediate-posts.json');
+    let logs = [];
+    try {
+        logs = JSON.parse(await fs.readFile(logPath, 'utf8'));
+    } catch (e) {
+        // File doesn't exist
+    }
+    logs.push(logEntry);
+    
+    // Keep only last 100 entries
+    if (logs.length > 100) {
+        logs = logs.slice(-100);
+    }
+    
+    await fs.writeFile(logPath, JSON.stringify(logs, null, 2));
+    
+    console.log(`\nResults:`);
+    console.log(`Twitter: ${results.twitter ? '✅ POSTED' : '❌ FAILED'}`);
+    console.log(`Bluesky: ${results.bluesky ? '✅ POSTED' : '❌ FAILED'}`);
+    console.log(`=====================================\n`);
+    
+    return results;
+}
+
+module.exports = {
+    handleImmediateSocialPost,
+    formatTwitterPost,
+    formatBlueskyPost,
+    anonymizeText
+};
\ No newline at end of file
diff --git a/check-active-users.js b/check-active-users.js
new file mode 100644
index 0000000..b5f2707
--- /dev/null
+++ b/check-active-users.js
@@ -0,0 +1,94 @@
+#!/usr/bin/env node
+
+const fs = require('fs')
+const path = require('path')
+
+// Read the last 500 lines of logs
+const logsPath = path.join(__dirname, 'logs/combined.log')
+const logs = fs.readFileSync(logsPath, 'utf-8').split('\n').slice(-500)
+
+// Track active users from the last 5 minutes
+const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000)
+const activeUsers = new Map()
+
+logs.forEach(line => {
+  if (line.includes('user_message') || line.includes('live-chat-update')) {
+    try {
+      const parsed = JSON.parse(line)
+      const timestamp = new Date(parsed.timestamp)
+      
+      if (timestamp > fiveMinutesAgo) {
+        const key = parsed.ip || parsed.userId || 'unknown'
+        
+        // Extract user info
+        if (!activeUsers.has(key) || timestamp > new Date(activeUsers.get(key).lastSeen)) {
+          activeUsers.set(key, {
+            userName: parsed.userName || 'Anonymous',
+            ip: parsed.ip || 'Unknown',
+            location: parsed.location || 'Unknown',
+            mode: parsed.mode || 'bubbe',
+            lastSeen: parsed.timestamp,
+            lastMessage: parsed.message || parsed.userMessage || ''
+          })
+        }
+      }
+    } catch (e) {
+      // Not a JSON line, skip
+    }
+  }
+})
+
+console.log('=== ACTIVE USERS (Last 5 Minutes) ===\n')
+console.log(`Total Active: ${activeUsers.size}\n`)
+
+let count = 1
+activeUsers.forEach((user, key) => {
+  console.log(`${count}. ${user.userName}`)
+  console.log(`   IP: ${user.ip}`)
+  console.log(`   Location: ${user.location}`)
+  console.log(`   Mode: ${user.mode}`)
+  console.log(`   Last Seen: ${new Date(user.lastSeen).toLocaleTimeString()}`)
+  if (user.lastMessage) {
+    console.log(`   Last Message: "${user.lastMessage.substring(0, 50)}${user.lastMessage.length > 50 ? '...' : ''}"`)
+  }
+  console.log('')
+  count++
+})
+
+if (activeUsers.size === 0) {
+  console.log('No active users found in the last 5 minutes.')
+  console.log('\nChecking for any users in last hour...')
+  
+  const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000)
+  const recentUsers = new Map()
+  
+  logs.forEach(line => {
+    if (line.includes('user_message')) {
+      try {
+        const parsed = JSON.parse(line)
+        const timestamp = new Date(parsed.timestamp)
+        
+        if (timestamp > oneHourAgo) {
+          const key = parsed.ip || parsed.userId || 'unknown'
+          if (!recentUsers.has(key)) {
+            recentUsers.set(key, {
+              userName: parsed.userName || 'Anonymous',
+              ip: parsed.ip || 'Unknown',
+              location: parsed.location || 'Unknown',
+              timestamp: parsed.timestamp
+            })
+          }
+        }
+      } catch (e) {
+        // Skip non-JSON lines
+      }
+    }
+  })
+  
+  if (recentUsers.size > 0) {
+    console.log(`\nFound ${recentUsers.size} users in the last hour:`)
+    recentUsers.forEach(user => {
+      console.log(`- ${user.userName} | ${user.ip} | ${user.location} | ${new Date(user.timestamp).toLocaleTimeString()}`)
+    })
+  }
+}
\ No newline at end of file
diff --git a/ecosystem.config.js b/ecosystem.config.js
new file mode 100644
index 0000000..bc2da05
--- /dev/null
+++ b/ecosystem.config.js
@@ -0,0 +1,24 @@
+module.exports = {
+  apps: [
+    {
+      name: 'dear-bubbe-admin',
+      script: 'backend/server.js',
+      cwd: '/root/Projects/dear-bubbe-admin',
+      instances: 1,
+      autorestart: true,
+      watch: false,
+      max_memory_restart: '1G',
+      env: {
+        NODE_ENV: 'production',
+        ADMIN_PORT: 3015,
+        MONGODB_URI: 'mongodb://localhost:27017/dear-bubbe-admin',
+        JWT_SECRET: 'dear-bubbe-admin-secret-key-change-in-production-2024',
+        BUBBE_API_URL: 'http://localhost:3011'
+      },
+      error_file: './logs/pm2-error.log',
+      out_file: './logs/pm2-out.log',
+      log_file: './logs/pm2-combined.log',
+      time: true
+    }
+  ]
+}
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..73ab476
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,2190 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Dear Bubbe Admin - Restricted Access</title>
+    <script src="https://accounts.google.com/gsi/client" async defer></script>
+    <script src="https://cdn.socket.io/4.7.4/socket.io.min.js"></script>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+        }
+
+        body {
+            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            min-height: 100vh;
+        }
+
+        /* Login Screen */
+        .login-container {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            min-height: 100vh;
+            padding: 20px;
+        }
+
+        .login-card {
+            background: white;
+            border-radius: 20px;
+            padding: 40px;
+            max-width: 400px;
+            width: 100%;
+            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
+            text-align: center;
+        }
+
+        .login-card h1 {
+            color: #333;
+            margin-bottom: 10px;
+        }
+
+        .restricted-notice {
+            color: #dc3545;
+            font-weight: bold;
+            margin: 20px 0;
+            padding: 15px;
+            background: #fee;
+            border-radius: 10px;
+            border: 2px solid #dc3545;
+        }
+
+        /* Dashboard */
+        .dashboard {
+            display: none;
+            min-height: 100vh;
+            background: #f5f5f5;
+        }
+
+        .dashboard.active {
+            display: block;
+        }
+
+        .header {
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            color: white;
+            padding: 20px;
+            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+        }
+
+        .header-content {
+            max-width: 1400px;
+            margin: 0 auto;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+        }
+
+        .user-info {
+            display: flex;
+            align-items: center;
+            gap: 10px;
+        }
+
+        .user-info img {
+            width: 40px;
+            height: 40px;
+            border-radius: 50%;
+        }
+
+        .container {
+            max-width: 1400px;
+            margin: 0 auto;
+            padding: 20px;
+        }
+
+        .stats-grid {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+            gap: 20px;
+            margin-bottom: 30px;
+        }
+
+        .stat-card {
+            background: white;
+            border-radius: 10px;
+            padding: 20px;
+            box-shadow: 0 2px 10px rgba(0,0,0,0.05);
+        }
+
+        .stat-card h3 {
+            color: #666;
+            font-size: 14px;
+            margin-bottom: 10px;
+        }
+
+        .stat-value {
+            font-size: 32px;
+            font-weight: bold;
+            color: #333;
+        }
+
+        .stat-card.alert {
+            border-left: 4px solid #dc3545;
+        }
+
+        .stat-card.success {
+            border-left: 4px solid #28a745;
+        }
+
+        /* Alerts Section */
+        .alerts-container {
+            background: white;
+            border-radius: 10px;
+            padding: 20px;
+            box-shadow: 0 2px 10px rgba(0,0,0,0.05);
+            margin-bottom: 30px;
+        }
+
+        .alert-item {
+            padding: 15px;
+            margin-bottom: 10px;
+            border-radius: 8px;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+        }
+
+        .alert-item.high {
+            background: #fee;
+            border-left: 4px solid #dc3545;
+        }
+
+        .alert-item.medium {
+            background: #fff3cd;
+            border-left: 4px solid #ffc107;
+        }
+
+        .alert-item.low {
+            background: #f0f0f0;
+            border-left: 4px solid #6c757d;
+        }
+
+        .alert-badge {
+            padding: 5px 10px;
+            border-radius: 5px;
+            font-size: 12px;
+            font-weight: bold;
+        }
+
+        .badge-antisemitic {
+            background: #dc3545;
+            color: white;
+        }
+
+        .badge-racist {
+            background: #fd7e14;
+            color: white;
+        }
+
+        /* Users Table */
+        .users-table {
+            background: white;
+            border-radius: 10px;
+            padding: 20px;
+            box-shadow: 0 2px 10px rgba(0,0,0,0.05);
+            overflow-x: auto;
+        }
+
+        table {
+            width: 100%;
+            border-collapse: collapse;
+        }
+
+        th {
+            background: #f8f9fa;
+            padding: 12px;
+            text-align: left;
+            font-weight: 600;
+            color: #666;
+        }
+
+        td {
+            padding: 12px;
+            border-bottom: 1px solid #eee;
+        }
+
+        .btn {
+            padding: 6px 12px;
+            border-radius: 5px;
+            border: none;
+            cursor: pointer;
+            font-size: 14px;
+        }
+
+        .btn-danger {
+            background: #dc3545;
+            color: white;
+        }
+
+        .btn-success {
+            background: #28a745;
+            color: white;
+        }
+
+        .status-badge {
+            padding: 4px 8px;
+            border-radius: 4px;
+            font-size: 12px;
+        }
+
+        .status-active {
+            background: #d4edda;
+            color: #155724;
+        }
+
+        .status-blocked {
+            background: #f8d7da;
+            color: #721c24;
+        }
+    </style>
+</head>
+<body>
+    <!-- Login Screen -->
+    <div class="login-container" id="loginScreen">
+        <div class="login-card">
+            <h1>🥯 Dear Bubbe Admin</h1>
+            <p style="color: #666; margin-bottom: 20px;">Administrative Dashboard</p>
+            
+            <div class="restricted-notice">
+                ⚠️ RESTRICTED ACCESS<br>
+                Only authorized administrators can access this system
+            </div>
+
+            <!-- Password Login Form -->
+            <div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #ddd;">
+                <p style="color: #666; margin-bottom: 15px; font-size: 14px;">Admin Login</p>
+                <form id="passwordLoginForm" onsubmit="handlePasswordLogin(event)">
+                    <input 
+                        type="email" 
+                        id="adminEmail"
+                        placeholder="Email address" 
+                        required
+                        style="width: 100%; padding: 12px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px;"
+                        value="steve@designerwallcoverings.com"
+                        readonly
+                    />
+                    <input 
+                        type="password" 
+                        id="adminPassword"
+                        placeholder="Password" 
+                        required
+                        style="width: 100%; padding: 12px; margin-bottom: 15px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px;"
+                    />
+                    <button 
+                        type="submit"
+                        style="width: 100%; padding: 12px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 5px; font-size: 16px; font-weight: bold; cursor: pointer;">
+                        Sign In
+                    </button>
+                </form>
+            </div>
+            
+            <!-- Google OAuth (disabled for now) -->
+            <div style="margin-top: 20px; opacity: 0.5; pointer-events: none;">
+                <p style="color: #999; font-size: 12px; margin-bottom: 10px;">Google Sign-In temporarily unavailable</p>
+                <div id="g_id_onload"
+                     data-client_id="493480746821-mhpjc0glkkogcf845ubi2hkhksoqj39k.apps.googleusercontent.com"
+                     data-callback="handleGoogleLogin"
+                     data-auto_prompt="false"
+                     data-ux_mode="popup"
+                     data-login_uri="http://45.61.58.125:5013">
+                </div>
+                <div class="g_id_signin" 
+                     data-type="standard"
+                     data-size="large"
+                     data-theme="outline"
+                     data-text="sign_in_with"
+                     data-shape="rectangular"
+                     data-logo_alignment="left">
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <!-- Dashboard -->
+    <div class="dashboard" id="dashboard">
+        <!-- Active Users Bottom Bar -->
+        <div id="activeUsersBar" style="position: fixed; bottom: 0; left: 0; right: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 10px 20px; display: flex; align-items: center; gap: 20px; box-shadow: 0 -2px 10px rgba(0,0,0,0.2); z-index: 1000; overflow-x: auto;">
+            <div style="display: flex; align-items: center; gap: 10px; font-weight: bold; min-width: fit-content;">
+                <span style="display: inline-block; width: 10px; height: 10px; background: #4caf50; border-radius: 50%; animation: pulse 2s infinite;"></span>
+                <span id="activeUsersLabel">Active Users (0)</span>
+            </div>
+            <div id="activeUsersList" style="display: flex; gap: 15px; flex: 1; overflow-x: auto;">
+                <!-- Active users will be dynamically added here -->
+            </div>
+        </div>
+        <div class="header">
+            <div class="header-content">
+                <h1>Dear Bubbe Admin Dashboard</h1>
+                <div class="user-info">
+                    <img id="userPicture" src="" alt="Admin">
+                    <div>
+                        <div id="userName" style="font-weight: bold;"></div>
+                        <div style="font-size: 12px;">Administrator</div>
+                    </div>
+                    <button class="btn" onclick="logout()" style="background: white; color: #333; margin-left: 20px;">
+                        Sign Out
+                    </button>
+                </div>
+            </div>
+        </div>
+
+        <div class="container">
+            <!-- Action Buttons Bar -->
+            <div style="background: white; border-radius: 10px; padding: 15px; margin-bottom: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); display: flex; gap: 10px; flex-wrap: wrap;">
+                <button onclick="refreshDashboard()" style="padding: 8px 15px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer;">
+                    🔄 Refresh Dashboard
+                </button>
+                <button onclick="generateReport()" style="padding: 8px 15px; background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer;">
+                    📊 Generate Report
+                </button>
+                <button onclick="exportUsers()" style="padding: 8px 15px; background: #17a2b8; color: white; border: none; border-radius: 5px; cursor: pointer;">
+                    📥 Export Users
+                </button>
+                <button onclick="exportAlerts()" style="padding: 8px 15px; background: #ffc107; color: black; border: none; border-radius: 5px; cursor: pointer;">
+                    📥 Export Alerts
+                </button>
+                <button onclick="exportChats()" style="padding: 8px 15px; background: #6f42c1; color: white; border: none; border-radius: 5px; cursor: pointer;">
+                    📥 Export Chats
+                </button>
+                <button onclick="clearAlerts()" style="padding: 8px 15px; background: #dc3545; color: white; border: none; border-radius: 5px; cursor: pointer;">
+                    🗑️ Clear Alerts
+                </button>
+                <button onclick="clearChats()" style="padding: 8px 15px; background: #dc3545; color: white; border: none; border-radius: 5px; cursor: pointer;">
+                    🗑️ Clear Chats
+                </button>
+                <button onclick="window.open('http://45.61.58.125:3011', '_blank')" style="padding: 8px 15px; background: #20c997; color: white; border: none; border-radius: 5px; cursor: pointer;">
+                    🌐 Open Bubbe Site
+                </button>
+            </div>
+            
+            <!-- Stats Cards -->
+            <div class="stats-grid">
+                <div class="stat-card success">
+                    <h3>TOTAL USERS</h3>
+                    <div class="stat-value" id="totalUsers">0</div>
+                </div>
+                <div class="stat-card">
+                    <h3>ACTIVE NOW</h3>
+                    <div class="stat-value" id="activeUsers">0</div>
+                </div>
+                <div class="stat-card alert">
+                    <h3>TOTAL ALERTS</h3>
+                    <div class="stat-value" id="totalAlerts">0</div>
+                </div>
+                <div class="stat-card alert">
+                    <h3>CRITICAL ALERTS</h3>
+                    <div class="stat-value" id="criticalAlerts">0</div>
+                </div>
+            </div>
+
+            <!-- Real-time Alerts -->
+            <div class="alerts-container">
+                <h2 style="margin-bottom: 20px;">🚨 Real-time Alerts</h2>
+                <div id="alertsList">
+                    <p style="color: #999;">No alerts to display</p>
+                </div>
+            </div>
+
+            <!-- Live Chat Monitoring -->
+            <div class="live-chat-container" style="background: white; border-radius: 10px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">
+                <h2 style="margin-bottom: 20px; display: flex; align-items: center; gap: 10px;">
+                    <span style="color: #28a745; animation: pulse 2s infinite;">🔴</span>
+                    Live User Activity
+                    <span id="liveUserCount" style="background: #28a745; color: white; padding: 2px 10px; border-radius: 20px; font-size: 14px;">0 active</span>
+                </h2>
+                <div id="liveChatsList" style="max-height: 400px; overflow-y: auto; border: 1px solid #eee; border-radius: 8px; padding: 10px;">
+                    <p style="color: #999; text-align: center;">No active conversations</p>
+                </div>
+            </div>
+
+            <!-- Users Table -->
+            <div class="users-table">
+                <h2 style="margin-bottom: 20px;">👥 User Management</h2>
+                <table>
+                    <thead>
+                        <tr>
+                            <th>Email</th>
+                            <th>Name</th>
+                            <th>Status</th>
+                            <th>Joined</th>
+                            <th>Last Active</th>
+                            <th>Actions</th>
+                        </tr>
+                    </thead>
+                    <tbody id="usersTableBody">
+                        <tr>
+                            <td colspan="6" style="text-align: center; color: #999;">Loading users...</td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+
+            <!-- Civil Rights Security - Banned Terms -->
+            <div class="banned-terms-container" style="background: white; border-radius: 10px; padding: 20px; margin-top: 30px; box-shadow: 0 2px 10px rgba(0,0,0,0.05);">
+                <h2 style="color: #dc3545; margin-bottom: 20px;">🚫 Civil Rights Security - Banned Terms</h2>
+                <div id="bannedTermsInfo" style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
+                    <div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
+                        <div><strong>Version:</strong> <span id="bannedVersion">-</span></div>
+                        <div><strong>Last Updated:</strong> <span id="bannedUpdated">-</span></div>
+                        <div><strong>Total Terms:</strong> <span id="bannedTotal" style="color: #dc3545; font-weight: bold;">0</span></div>
+                    </div>
+                    <div style="margin-top: 10px; color: #666;">
+                        <strong>Sources:</strong> <span id="bannedSources">-</span>
+                    </div>
+                </div>
+                
+                <!-- Category Filter -->
+                <div style="margin-bottom: 15px;">
+                    <label style="margin-right: 10px;">Filter by Category:</label>
+                    <select id="categoryFilter" onchange="filterBannedTerms()" style="padding: 8px; border-radius: 5px; border: 1px solid #ddd;">
+                        <option value="all">All Categories</option>
+                        <option value="ethnic_slurs">Ethnic Slurs</option>
+                        <option value="antisemitic_slurs">Antisemitic Slurs</option>
+                        <option value="racial_slurs">Racial Slurs</option>
+                        <option value="lgbtq_slurs">LGBTQ+ Slurs</option>
+                        <option value="holocaust_references">Holocaust References</option>
+                        <option value="offensive_phrases">Offensive Phrases</option>
+                    </select>
+                    
+                    <label style="margin-left: 20px; margin-right: 10px;">Severity:</label>
+                    <select id="severityFilter" onchange="filterBannedTerms()" style="padding: 8px; border-radius: 5px; border: 1px solid #ddd;">
+                        <option value="all">All Severities</option>
+                        <option value="critical">Critical</option>
+                        <option value="high">High</option>
+                        <option value="medium">Medium</option>
+                    </select>
+                </div>
+                
+                <!-- Terms List -->
+                <div id="bannedTermsList" style="overflow-y: auto; max-height: 500px; border: 1px solid #e0e0e0; border-radius: 8px; padding: 10px;">
+                    Loading banned terms...
+                </div>
+                
+                <!-- Add New Banned Term -->
+                <div style="margin-top: 20px; padding: 15px; background: #fff5f5; border-radius: 8px; border: 1px solid #ffcdd2;">
+                    <h4 style="color: #dc3545; margin-bottom: 15px;">➕ Add New Banned Term</h4>
+                    <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px;">
+                        <input type="text" id="newBannedTerm" placeholder="Term *" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                        <input type="text" id="newBannedVariants" placeholder="Variants (comma-separated)" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                        <select id="newBannedCategory" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                            <option value="">Select Category *</option>
+                            <option value="ethnic_slurs">Ethnic Slurs</option>
+                            <option value="antisemitic_slurs">Antisemitic Slurs</option>
+                            <option value="racial_slurs">Racial Slurs</option>
+                            <option value="lgbtq_slurs">LGBTQ+ Slurs</option>
+                            <option value="holocaust_references">Holocaust References</option>
+                            <option value="offensive_phrases">Offensive Phrases</option>
+                        </select>
+                        <select id="newBannedSeverity" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                            <option value="">Select Severity *</option>
+                            <option value="critical">Critical</option>
+                            <option value="high">High</option>
+                            <option value="medium">Medium</option>
+                        </select>
+                        <input type="text" id="newBannedReason" placeholder="Reason" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px; grid-column: 1 / -1;">
+                        <input type="text" id="newBannedReplacement" placeholder="Replacement" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                        <button onclick="addBannedTerm()" style="background: #dc3545; color: white; padding: 8px 15px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold;">Add Banned Term</button>
+                    </div>
+                </div>
+
+                <!-- Acceptable Yiddish Section -->
+                <div style="margin-top: 20px; background: #e8f5e9; padding: 15px; border-radius: 8px;">
+                    <h3 style="color: #28a745;">✅ Acceptable Yiddish Terms</h3>
+                    
+                    <!-- Add New Acceptable Term -->
+                    <div style="margin: 15px 0; padding: 15px; background: white; border-radius: 8px;">
+                        <h4 style="color: #4caf50; margin-bottom: 15px;">➕ Add New Acceptable Term</h4>
+                        <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px;">
+                            <input type="text" id="newAcceptableTerm" placeholder="Term *" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                            <input type="text" id="newAcceptableVariants" placeholder="Variants (comma-separated)" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                            <input type="text" id="newAcceptableMeaning" placeholder="Meaning *" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px; grid-column: 1 / -1;">
+                            <input type="text" id="newAcceptableUsage" placeholder="Usage" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px; grid-column: 1 / -1;">
+                            <input type="text" id="newAcceptableNotes" placeholder="Notes" style="padding: 8px; border: 1px solid #ddd; border-radius: 5px;">
+                            <button onclick="addAcceptableTerm()" style="background: #4caf50; color: white; padding: 8px 15px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold;">Add Acceptable Term</button>
+                        </div>
+                    </div>
+                    
+                    <!-- Display Acceptable Terms -->
+                    <div id="acceptableTermsList" style="max-height: 400px; overflow-y: auto; margin-top: 15px;">
+                        <!-- Will be populated dynamically -->
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        let authToken = null;
+        let socket = null;
+        const API_URL = 'http://45.61.58.125:5013';
+
+        // Handle Google Login
+        // Handle Password Login
+        window.handlePasswordLogin = async function(event) {
+            event.preventDefault();
+            
+            const email = document.getElementById('adminEmail').value;
+            const password = document.getElementById('adminPassword').value;
+            
+            try {
+                const res = await fetch(`${API_URL}/api/auth/password`, {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({
+                        email: email,
+                        password: password
+                    })
+                });
+
+                const data = await res.json();
+
+                if (res.ok) {
+                    // Login successful
+                    authToken = data.token;
+                    localStorage.setItem('adminToken', authToken);
+                    
+                    // Update UI
+                    document.getElementById('userName').textContent = data.user.name;
+                    if (data.user.picture) {
+                        document.getElementById('userPicture').src = data.user.picture;
+                    }
+                    
+                    // Show dashboard
+                    document.getElementById('loginScreen').style.display = 'none';
+                    document.getElementById('dashboard').classList.add('active');
+                    
+                    // Initialize dashboard
+                    initDashboard();
+                } else {
+                    // Access denied
+                    alert(data.message || 'Invalid password. Please try again.');
+                }
+            } catch (error) {
+                console.error('Login error:', error);
+                alert('Login failed. Please try again.');
+            }
+        }
+
+        // Make handleGoogleLogin globally accessible for Google OAuth callback
+        window.handleGoogleLogin = async function(response) {
+            try {
+                const res = await fetch(`${API_URL}/api/auth/google`, {
+                    method: 'POST',
+                    headers: {
+                        'Content-Type': 'application/json',
+                    },
+                    body: JSON.stringify({
+                        credential: response.credential
+                    })
+                });
+
+                const data = await res.json();
+
+                if (res.ok) {
+                    // Login successful
+                    authToken = data.token;
+                    localStorage.setItem('adminToken', authToken);
+                    
+                    // Update UI
+                    document.getElementById('userName').textContent = data.user.name;
+                    document.getElementById('userPicture').src = data.user.picture;
+                    
+                    // Show dashboard
+                    document.getElementById('loginScreen').style.display = 'none';
+                    document.getElementById('dashboard').classList.add('active');
+                    
+                    // Initialize dashboard
+                    initDashboard();
+                } else {
+                    // Access denied
+                    if (data.error && data.error.includes('restricted')) {
+                        alert('❌ ACCESS DENIED\n\nThis admin panel is restricted to authorized administrators only.\n\nOnly steve@designerwallcoverings.com can access this system.');
+                    } else {
+                        alert(data.message || 'Access denied. Only authorized administrators can access this system.');
+                    }
+                    // Sign out from Google to allow retry with different account
+                    if (google && google.accounts && google.accounts.id) {
+                        google.accounts.id.disableAutoSelect();
+                    }
+                }
+            } catch (error) {
+                console.error('Login error:', error);
+                alert('Login failed. Please try again.');
+            }
+        }
+
+        // Initialize Dashboard
+        async function initDashboard() {
+            // Connect WebSocket
+            socket = io(API_URL, {
+                auth: {
+                    token: authToken
+                }
+            });
+
+            socket.on('new-alert', (alert) => {
+                addAlertToList(alert);
+                updateAlertCount();
+            });
+
+            socket.on('stats-update', (stats) => {
+                document.getElementById('activeUsers').textContent = stats.activeUsers;
+            });
+
+            // Live chat updates
+            socket.on('live-chat-update', (data) => {
+                updateLiveChat(data);
+            });
+
+            socket.on('user-status-change', (data) => {
+                console.log('User status changed:', data);
+                loadUsers(); // Reload users table
+            });
+
+            socket.on('user-deleted', (data) => {
+                console.log('User deleted:', data);
+                loadUsers(); // Reload users table
+            });
+
+            // Load initial data
+            await loadDashboardStats();
+            await loadUsers();
+            await loadAlerts();
+            await loadBannedTerms();
+            await loadActiveSessions();
+        }
+
+        // Load Dashboard Stats
+        async function loadDashboardStats() {
+            try {
+                const response = await fetch(`${API_URL}/api/dashboard/stats`, {
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+
+                if (response.ok) {
+                    const stats = await response.json();
+                    document.getElementById('totalUsers').textContent = stats.totalUsers;
+                    document.getElementById('activeUsers').textContent = stats.activeToday;
+                    document.getElementById('totalAlerts').textContent = stats.totalAlerts;
+                    document.getElementById('criticalAlerts').textContent = stats.criticalAlerts;
+                }
+            } catch (error) {
+                console.error('Failed to load stats:', error);
+            }
+        }
+
+        // Load Users
+        async function loadUsers() {
+            try {
+                const response = await fetch(`${API_URL}/api/users`, {
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+
+                if (response.ok) {
+                    const users = await response.json();
+                    const tbody = document.getElementById('usersTableBody');
+                    
+                    if (users.length === 0) {
+                        tbody.innerHTML = '<tr><td colspan="6" style="text-align: center; color: #999;">No users found</td></tr>';
+                        return;
+                    }
+
+                    tbody.innerHTML = users.map(user => `
+                        <tr>
+                            <td>${user.email}</td>
+                            <td>${user.preferredName || user.name || 'Unknown'}</td>
+                            <td>
+                                <span class="status-badge ${user.blocked ? 'status-blocked' : 'status-active'}">
+                                    ${user.blocked ? 'Blocked' : 'Active'}
+                                </span>
+                            </td>
+                            <td>${new Date(user.createdAt).toLocaleDateString()}</td>
+                            <td>${user.lastActive ? new Date(user.lastActive).toLocaleString() : 'Never'}</td>
+                            <td>
+                                <div class="user-actions">
+                                    ${user.active === false
+                                        ? `<button class="action-btn btn-activate" onclick="activateUser('${user.email}')">Activate</button>`
+                                        : `<button class="action-btn btn-deactivate" onclick="deactivateUser('${user.email}')">Deactivate</button>`
+                                    }
+                                    ${user.blocked 
+                                        ? `<button class="action-btn btn-activate" onclick="unblockUser('${user.email}')">Unblock</button>`
+                                        : `<button class="action-btn btn-block" onclick="blockUser('${user.email}')">Block</button>`
+                                    }
+                                    <button class="action-btn btn-delete" onclick="deleteUser('${user.email}')">Delete</button>
+                                </div>
+                            </td>
+                        </tr>
+                    `).join('');
+                }
+            } catch (error) {
+                console.error('Failed to load users:', error);
+            }
+        }
+
+        // Load Alerts
+        async function loadAlerts() {
+            try {
+                const response = await fetch(`${API_URL}/api/alerts`, {
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+
+                if (response.ok) {
+                    const data = await response.json();
+                    const alertsList = document.getElementById('alertsList');
+                    
+                    const allAlerts = [...(data.antisemitic || []), ...(data.racist || []), ...(data.other || [])]
+                        .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
+                        .slice(0, 10);
+
+                    if (allAlerts.length === 0) {
+                        alertsList.innerHTML = '<p style="color: #999;">No alerts to display</p>';
+                        return;
+                    }
+
+                    alertsList.innerHTML = allAlerts.map(alert => `
+                        <div class="alert-item ${alert.severity.toLowerCase()}">
+                            <div>
+                                <span class="alert-badge badge-${alert.type}">${alert.type.toUpperCase()}</span>
+                                <strong style="margin-left: 10px;">${alert.userId}</strong>
+                                <div style="margin-top: 5px; color: #666;">${alert.message.substring(0, 100)}...</div>
+                            </div>
+                            <div style="text-align: right;">
+                                <div style="color: #999; font-size: 12px;">${new Date(alert.timestamp).toLocaleString()}</div>
+                                <div style="color: #666; font-size: 12px;">IP: ${alert.ip || 'Unknown'}</div>
+                            </div>
+                        </div>
+                    `).join('');
+                }
+            } catch (error) {
+                console.error('Failed to load alerts:', error);
+            }
+        }
+
+        // Block User
+        async function blockUser(email) {
+            if (!confirm(`Are you sure you want to block ${email}?`)) return;
+            
+            const reason = prompt('Enter reason for blocking:');
+            if (!reason) return;
+
+            try {
+                const response = await fetch(`${API_URL}/api/users/${email}/block`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({
+                        blocked: true,
+                        reason: reason
+                    })
+                });
+
+                if (response.ok) {
+                    alert('User blocked successfully');
+                    loadUsers();
+                }
+            } catch (error) {
+                console.error('Failed to block user:', error);
+                alert('Failed to block user');
+            }
+        }
+
+        // Unblock User
+        async function unblockUser(email) {
+            if (!confirm(`Are you sure you want to unblock ${email}?`)) return;
+
+            try {
+                const response = await fetch(`${API_URL}/api/users/${email}/block`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({
+                        blocked: false,
+                        reason: 'Unblocked by admin'
+                    })
+                });
+
+                if (response.ok) {
+                    alert('User unblocked successfully');
+                    loadUsers();
+                }
+            } catch (error) {
+                console.error('Failed to unblock user:', error);
+                alert('Failed to unblock user');
+            }
+        }
+
+        // Add Alert to List
+        function addAlertToList(alert) {
+            const alertsList = document.getElementById('alertsList');
+            const alertHtml = `
+                <div class="alert-item ${alert.severity.toLowerCase()}" style="animation: slideIn 0.5s;">
+                    <div>
+                        <span class="alert-badge badge-${alert.type}">🚨 NEW - ${alert.type.toUpperCase()}</span>
+                        <strong style="margin-left: 10px;">${alert.userId}</strong>
+                        <div style="margin-top: 5px; color: #666;">${alert.message.substring(0, 100)}...</div>
+                    </div>
+                    <div style="text-align: right;">
+                        <div style="color: #999; font-size: 12px;">${new Date(alert.timestamp).toLocaleString()}</div>
+                        <div style="color: #666; font-size: 12px;">IP: ${alert.ip || 'Unknown'}</div>
+                    </div>
+                </div>
+            `;
+            
+            // Remove "no alerts" message if present
+            if (alertsList.innerHTML.includes('No alerts to display')) {
+                alertsList.innerHTML = '';
+            }
+            
+            // Add new alert at the top
+            alertsList.insertAdjacentHTML('afterbegin', alertHtml);
+        }
+
+        // Load Banned Terms
+        let allBannedTerms = [];
+        async function loadBannedTerms() {
+            try {
+                const response = await fetch(`${API_URL}/api/banned-terms`, {
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+                
+                const data = await response.json();
+                allBannedTerms = data.terms || [];
+                
+                // Update info section
+                document.getElementById('bannedVersion').textContent = data.version || '-';
+                document.getElementById('bannedUpdated').textContent = data.lastUpdated || '-';
+                document.getElementById('bannedTotal').textContent = data.totalTerms || 0;
+                document.getElementById('bannedSources').textContent = data.sources ? data.sources.join(', ') : '-';
+                
+                // Display banned terms
+                displayBannedTerms(allBannedTerms);
+                
+                // Load acceptable terms separately
+                loadAcceptableTerms();
+                
+            } catch (error) {
+                console.error('Failed to load banned terms:', error);
+                document.getElementById('bannedTermsList').innerHTML = '<div style="color: red;">Failed to load banned terms</div>';
+            }
+        }
+        
+        // Display Banned Terms
+        function displayBannedTerms(terms) {
+            const container = document.getElementById('bannedTermsList');
+            
+            if (terms.length === 0) {
+                container.innerHTML = '<div style="color: #666; text-align: center;">No banned terms found</div>';
+                return;
+            }
+            
+            const html = terms.map(term => {
+                const severityColor = term.severity === 'critical' ? '#dc3545' : term.severity === 'high' ? '#ff9800' : '#ffc107';
+                const variantsDisplay = term.variants && term.variants.length > 0 
+                    ? `<div style="margin-top: 5px; font-size: 12px; color: #666;">Variants: ${term.variants.join(', ')}</div>` 
+                    : '';
+                
+                return `
+                    <div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 10px; margin-bottom: 10px; background: #fafafa; position: relative;">
+                        <button onclick="deleteBannedTerm('${encodeURIComponent(term.term)}')" 
+                                style="position: absolute; top: 5px; right: 5px; background: #dc3545; color: white; border: none; border-radius: 3px; padding: 2px 8px; cursor: pointer; font-size: 11px;">
+                            Delete
+                        </button>
+                        <div style="display: flex; justify-content: space-between; align-items: start;">
+                            <div style="flex: 1;">
+                                <strong style="color: ${severityColor}; font-size: 16px;">${term.term}</strong>
+                                ${variantsDisplay}
+                                <div style="margin-top: 5px; color: #333;">${term.reason || 'No reason provided'}</div>
+                                ${term.replacement ? `<div style="margin-top: 5px; color: #28a745;">Replacement: "${term.replacement}"</div>` : ''}
+                            </div>
+                            <div style="text-align: right; margin-right: 50px;">
+                                <span style="background: ${severityColor}; color: white; padding: 2px 8px; border-radius: 4px; font-size: 12px;">${term.severity.toUpperCase()}</span>
+                                <div style="margin-top: 5px; color: #666; font-size: 12px;">${term.category.replace(/_/g, ' ').toUpperCase()}</div>
+                            </div>
+                        </div>
+                    </div>
+                `;
+            }).join('');
+            
+            container.innerHTML = html;
+        }
+        
+        // Load acceptable terms
+        async function loadAcceptableTerms() {
+            try {
+                const response = await fetch(`${API_URL}/api/acceptable-terms`, {
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+                
+                const data = await response.json();
+                displayAcceptableTerms(data.terms || []);
+                
+            } catch (error) {
+                console.error('Failed to load acceptable terms:', error);
+                document.getElementById('acceptableTermsList').innerHTML = '<div style="color: red;">Failed to load acceptable terms</div>';
+            }
+        }
+
+        // Display acceptable terms
+        function displayAcceptableTerms(terms) {
+            const container = document.getElementById('acceptableTermsList');
+            
+            if (terms.length === 0) {
+                container.innerHTML = '<div style="color: #666; text-align: center; padding: 20px;">No acceptable terms defined</div>';
+                return;
+            }
+            
+            const html = terms.map(term => {
+                const variantsDisplay = term.variants && term.variants.length > 0 
+                    ? `<div style="margin-top: 5px; font-size: 12px; color: #666;">Variants: ${term.variants.join(', ')}</div>` 
+                    : '';
+                
+                return `
+                    <div style="border: 1px solid #4caf50; border-radius: 8px; padding: 12px; margin-bottom: 10px; background: white; position: relative;">
+                        <button onclick="deleteAcceptableTerm('${encodeURIComponent(term.term)}')" 
+                                style="position: absolute; top: 5px; right: 5px; background: #dc3545; color: white; border: none; border-radius: 3px; padding: 2px 8px; cursor: pointer; font-size: 12px;">
+                            Delete
+                        </button>
+                        <div>
+                            <strong style="color: #4caf50; font-size: 16px;">${term.term}</strong>
+                            ${variantsDisplay}
+                            <div style="margin-top: 5px; color: #333;"><strong>Meaning:</strong> ${term.meaning}</div>
+                            ${term.usage ? `<div style="margin-top: 3px; color: #666;"><strong>Usage:</strong> ${term.usage}</div>` : ''}
+                            ${term.notes ? `<div style="margin-top: 3px; color: #999; font-style: italic;">${term.notes}</div>` : ''}
+                        </div>
+                    </div>
+                `;
+            }).join('');
+            
+            container.innerHTML = html;
+        }
+
+        // Add new banned term
+        async function addBannedTerm() {
+            const term = document.getElementById('newBannedTerm').value.trim();
+            const category = document.getElementById('newBannedCategory').value;
+            const severity = document.getElementById('newBannedSeverity').value;
+            const variants = document.getElementById('newBannedVariants').value.trim();
+            const reason = document.getElementById('newBannedReason').value.trim();
+            const replacement = document.getElementById('newBannedReplacement').value.trim();
+            
+            if (!term || !category || !severity) {
+                alert('Term, category, and severity are required');
+                return;
+            }
+            
+            try {
+                const response = await fetch(`${API_URL}/api/banned-terms`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({
+                        term,
+                        category,
+                        severity,
+                        variants,
+                        reason,
+                        replacement
+                    })
+                });
+                
+                if (response.ok) {
+                    alert('Banned term added successfully');
+                    // Clear form
+                    document.getElementById('newBannedTerm').value = '';
+                    document.getElementById('newBannedVariants').value = '';
+                    document.getElementById('newBannedCategory').value = '';
+                    document.getElementById('newBannedSeverity').value = '';
+                    document.getElementById('newBannedReason').value = '';
+                    document.getElementById('newBannedReplacement').value = '';
+                    // Reload terms
+                    loadBannedTerms();
+                } else {
+                    const error = await response.json();
+                    alert('Failed to add term: ' + error.error);
+                }
+            } catch (error) {
+                console.error('Error adding banned term:', error);
+                alert('Failed to add banned term');
+            }
+        }
+
+        // Add new acceptable term
+        async function addAcceptableTerm() {
+            const term = document.getElementById('newAcceptableTerm').value.trim();
+            const meaning = document.getElementById('newAcceptableMeaning').value.trim();
+            const variants = document.getElementById('newAcceptableVariants').value.trim();
+            const usage = document.getElementById('newAcceptableUsage').value.trim();
+            const notes = document.getElementById('newAcceptableNotes').value.trim();
+            
+            if (!term || !meaning) {
+                alert('Term and meaning are required');
+                return;
+            }
+            
+            try {
+                const response = await fetch(`${API_URL}/api/acceptable-terms`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({
+                        term,
+                        meaning,
+                        variants,
+                        usage,
+                        notes
+                    })
+                });
+                
+                if (response.ok) {
+                    alert('Acceptable term added successfully');
+                    // Clear form
+                    document.getElementById('newAcceptableTerm').value = '';
+                    document.getElementById('newAcceptableMeaning').value = '';
+                    document.getElementById('newAcceptableVariants').value = '';
+                    document.getElementById('newAcceptableUsage').value = '';
+                    document.getElementById('newAcceptableNotes').value = '';
+                    // Reload terms
+                    loadAcceptableTerms();
+                } else {
+                    const error = await response.json();
+                    alert('Failed to add term: ' + error.error);
+                }
+            } catch (error) {
+                console.error('Error adding acceptable term:', error);
+                alert('Failed to add acceptable term');
+            }
+        }
+
+        // Delete banned term
+        async function deleteBannedTerm(encodedTerm) {
+            const term = decodeURIComponent(encodedTerm);
+            if (!confirm(`Delete banned term "${term}"?`)) return;
+            
+            try {
+                const response = await fetch(`${API_URL}/api/banned-terms/${encodeURIComponent(term)}`, {
+                    method: 'DELETE',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+                
+                if (response.ok) {
+                    alert('Term deleted successfully');
+                    loadBannedTerms();
+                } else {
+                    alert('Failed to delete term');
+                }
+            } catch (error) {
+                console.error('Error deleting term:', error);
+                alert('Failed to delete term');
+            }
+        }
+
+        // Delete acceptable term
+        async function deleteAcceptableTerm(encodedTerm) {
+            const term = decodeURIComponent(encodedTerm);
+            if (!confirm(`Delete acceptable term "${term}"?`)) return;
+            
+            try {
+                const response = await fetch(`${API_URL}/api/acceptable-terms/${encodeURIComponent(term)}`, {
+                    method: 'DELETE',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+                
+                if (response.ok) {
+                    alert('Term deleted successfully');
+                    loadAcceptableTerms();
+                } else {
+                    alert('Failed to delete term');
+                }
+            } catch (error) {
+                console.error('Error deleting term:', error);
+                alert('Failed to delete term');
+            }
+        }
+
+        // Filter Banned Terms
+        function filterBannedTerms() {
+            const categoryFilter = document.getElementById('categoryFilter').value;
+            const severityFilter = document.getElementById('severityFilter').value;
+            
+            let filtered = allBannedTerms;
+            
+            if (categoryFilter !== 'all') {
+                filtered = filtered.filter(term => term.category === categoryFilter);
+            }
+            
+            if (severityFilter !== 'all') {
+                filtered = filtered.filter(term => term.severity === severityFilter);
+            }
+            
+            displayBannedTerms(filtered);
+        }
+
+        // Load Active Sessions
+        async function loadActiveSessions() {
+            try {
+                const response = await fetch(`${API_URL}/api/live-chats`, {
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`
+                    }
+                });
+                
+                if (response.ok) {
+                    const chats = await response.json();
+                    
+                    // Populate active users from existing sessions
+                    for (const chat of chats) {
+                        if (!activeUsers.has(chat.userId)) {
+                            activeUsers.set(chat.userId, {
+                                userId: chat.userId,
+                                userName: chat.userName || 'Anonymous',
+                                userEmail: chat.userEmail || '',
+                                ip: chat.ip,
+                                mode: chat.mode,
+                                lastActivity: chat.lastActivity,
+                                isTyping: false,
+                                lastMessage: ''
+                            });
+                        }
+                        
+                        // Also populate live chats
+                        if (!liveChats.has(chat.userId)) {
+                            liveChats.set(chat.userId, {
+                                userId: chat.userId,
+                                userName: chat.userName || 'Anonymous',
+                                userEmail: chat.userEmail || '',
+                                ip: chat.ip,
+                                mode: chat.mode,
+                                messages: chat.messages || []
+                            });
+                        }
+                    }
+                    
+                    // Update displays
+                    updateActiveUsersBar();
+                    renderLiveChats();
+                }
+            } catch (error) {
+                console.error('Failed to load active sessions:', error);
+            }
+        }
+
+        // Logout
+        function logout() {
+            localStorage.removeItem('adminToken');
+            if (socket) socket.disconnect();
+            location.reload();
+        }
+
+        // Live Chat Functions
+        const liveChats = new Map();
+        const activeUsers = new Map();
+
+        function updateLiveChat(data) {
+            const { userId, userName, userEmail, ip, location, type, message, response, mode, timestamp } = data;
+            
+            // Update or create chat entry
+            if (!liveChats.has(userId)) {
+                liveChats.set(userId, {
+                    userId,
+                    userName: userName || 'Anonymous',
+                    userEmail: userEmail || '',
+                    ip,
+                    location: location || 'Unknown',
+                    mode,
+                    messages: []
+                });
+            }
+            
+            const chat = liveChats.get(userId);
+            // Update user info if provided
+            if (userName) chat.userName = userName;
+            if (userEmail) chat.userEmail = userEmail;
+            if (location) chat.location = location;
+            
+            // Update active users tracking
+            if (!activeUsers.has(userId)) {
+                activeUsers.set(userId, {
+                    userId,
+                    userName: userName || 'Anonymous',
+                    userEmail: userEmail || '',
+                    ip,
+                    location: location || 'Unknown',
+                    mode,
+                    lastActivity: timestamp,
+                    isTyping: false,
+                    lastMessage: ''
+                });
+            }
+            
+            const activeUser = activeUsers.get(userId);
+            activeUser.lastActivity = timestamp;
+            // Update user info if provided
+            if (userName) activeUser.userName = userName;
+            if (userEmail) activeUser.userEmail = userEmail;
+            if (location) activeUser.location = location;
+            
+            if (type === 'user_typing') {
+                chat.messages.push({
+                    type: 'user',
+                    text: message,
+                    timestamp
+                });
+                activeUser.isTyping = true;
+                activeUser.lastMessage = message;
+                // Clear typing after 3 seconds
+                setTimeout(() => {
+                    activeUser.isTyping = false;
+                    updateActiveUsersBar();
+                }, 3000);
+            } else if (type === 'bubbe_response') {
+                chat.messages.push({
+                    type: 'bubbe',
+                    text: response,
+                    timestamp
+                });
+                activeUser.isTyping = false;
+            }
+            
+            renderLiveChats();
+            updateActiveUsersBar();
+        }
+
+        function updateActiveUsersBar() {
+            const activeUsersLabel = document.getElementById('activeUsersLabel');
+            const activeUsersList = document.getElementById('activeUsersList');
+            
+            // Remove inactive users (no activity for 5 minutes)
+            const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
+            for (const [userId, user] of activeUsers.entries()) {
+                if (user.lastActivity < fiveMinutesAgo) {
+                    activeUsers.delete(userId);
+                }
+            }
+            
+            activeUsersLabel.textContent = `Active Users (${activeUsers.size})`;
+            
+            if (activeUsers.size === 0) {
+                activeUsersList.innerHTML = '<span style="opacity: 0.7; font-size: 14px;">No active users currently</span>';
+                return;
+            }
+            
+            let html = '';
+            for (const [userId, user] of activeUsers) {
+                const displayName = user.userName || 'Anonymous';
+                const initial = displayName.charAt(0).toUpperCase();
+                const timeSince = getTimeSince(user.lastActivity);
+                const displayEmail = user.userEmail ? ` (${user.userEmail})` : '';
+                
+                const locationInfo = user.location ? ` | 📍 ${user.location}` : '';
+                html += `
+                    <div class="active-user-badge" onclick="scrollToUserChat('${userId}')" title="${displayName}${displayEmail}${locationInfo} | IP: ${user.ip}">
+                        <div class="user-avatar">${initial}</div>
+                        <div class="user-info-text">
+                            <span class="user-email-text">${displayName}</span>
+                            <span class="user-status-text">
+                                ${user.isTyping ? 
+                                    'Typing<span class="typing-indicator"><span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span></span>' : 
+                                    timeSince}
+                            </span>
+                        </div>
+                    </div>
+                `;
+            }
+            
+            activeUsersList.innerHTML = html;
+        }
+
+        function getTimeSince(timestamp) {
+            const now = new Date();
+            const then = new Date(timestamp);
+            const diffMs = now - then;
+            const diffSecs = Math.floor(diffMs / 1000);
+            
+            if (diffSecs < 60) return 'Just now';
+            if (diffSecs < 120) return '1 min ago';
+            if (diffSecs < 3600) return `${Math.floor(diffSecs / 60)} mins ago`;
+            return `${Math.floor(diffSecs / 3600)} hours ago`;
+        }
+
+        window.scrollToUserChat = function(userId) {
+            // Scroll to the user's chat in the live chat container
+            const chatContainer = document.getElementById('liveChatsList');
+            if (chatContainer) {
+                chatContainer.scrollIntoView({ behavior: 'smooth' });
+            }
+        }
+
+        function renderLiveChats() {
+            const container = document.getElementById('liveChatsList');
+            const activeCount = liveChats.size;
+            
+            document.getElementById('liveUserCount').textContent = `${activeCount} active`;
+            
+            if (activeCount === 0) {
+                container.innerHTML = '<p style="color: #999; text-align: center;">No active conversations</p>';
+                return;
+            }
+            
+            let html = '';
+            for (const [userId, chat] of liveChats) {
+                const lastMessages = chat.messages.slice(-5); // Show last 5 messages
+                const displayName = chat.userName || 'Anonymous';
+                const displayEmail = chat.userEmail ? ` (${chat.userEmail})` : '';
+                
+                html += `
+                    <div style="margin-bottom: 20px; border-bottom: 1px solid #eee; padding-bottom: 10px;">
+                        <div style="font-weight: bold; margin-bottom: 10px; color: #667eea;">
+                            👤 ${displayName}${displayEmail} | IP: ${chat.ip} | 📍 ${chat.location || 'Unknown'} | Mode: ${chat.mode}
+                        </div>
+                `;
+                
+                for (const msg of lastMessages) {
+                    const className = msg.type === 'user' ? 'chat-user' : 'chat-bubbe';
+                    const label = msg.type === 'user' ? `👤 ${displayName}` : '👵 Bubbe';
+                    const messageId = `msg-${userId}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
+                    
+                    html += `
+                        <div class="chat-message ${className}" id="${messageId}">
+                            <div class="chat-header">
+                                <strong>${label}</strong>
+                                <span>${new Date(msg.timestamp).toLocaleTimeString()}</span>
+                            </div>
+                            <div class="chat-text">${msg.text}</div>
+                            <div class="social-share-buttons" style="margin-top: 8px; display: flex; gap: 8px;">
+                                <button onclick="shareToTwitter('${messageId}', '${label}', '${encodeURIComponent(msg.text).replace(/'/g, "\\'")}')" 
+                                        class="social-btn twitter-btn" title="Share to Twitter/X">
+                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
+                                        <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
+                                    </svg>
+                                </button>
+                                <button onclick="shareToFacebook('${messageId}', '${encodeURIComponent(msg.text).replace(/'/g, "\\'")}')" 
+                                        class="social-btn facebook-btn" title="Share to Facebook">
+                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
+                                        <path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
+                                    </svg>
+                                </button>
+                                <button onclick="shareToLinkedIn('${messageId}', '${encodeURIComponent(msg.text).replace(/'/g, "\\'")}')" 
+                                        class="social-btn linkedin-btn" title="Share to LinkedIn">
+                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
+                                        <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
+                                    </svg>
+                                </button>
+                                <button onclick="copyMessage('${messageId}', '${label}', '${encodeURIComponent(msg.text).replace(/'/g, "\\'")}')" 
+                                        class="social-btn copy-btn" title="Copy to clipboard">
+                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                                        <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
+                                        <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
+                                    </svg>
+                                </button>
+                                <button onclick="flagMessage('${messageId}', '${userId}', '${encodeURIComponent(msg.text).replace(/'/g, "\\'")}')" 
+                                        class="social-btn flag-btn" title="Flag as problematic">
+                                    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
+                                        <path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/>
+                                    </svg>
+                                </button>
+                            </div>
+                        </div>
+                    `;
+                }
+                
+                html += '</div>';
+            }
+            
+            container.innerHTML = html;
+            container.scrollTop = container.scrollHeight;
+        }
+
+        // User Management Functions
+        async function deactivateUser(email) {
+            if (!confirm(`Deactivate user ${email}?`)) return;
+            
+            const reason = prompt('Enter reason for deactivation:');
+            if (!reason) return;
+            
+            try {
+                const response = await fetch(`${API_URL}/api/users/${email}/status`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({ active: false, reason })
+                });
+                
+                if (response.ok) {
+                    alert(`User ${email} has been deactivated`);
+                    loadUsers();
+                } else {
+                    alert('Failed to deactivate user');
+                }
+            } catch (error) {
+                console.error('Error:', error);
+                alert('Failed to deactivate user');
+            }
+        }
+
+        async function activateUser(email) {
+            if (!confirm(`Reactivate user ${email}?`)) return;
+            
+            try {
+                const response = await fetch(`${API_URL}/api/users/${email}/status`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({ active: true })
+                });
+                
+                if (response.ok) {
+                    alert(`User ${email} has been reactivated`);
+                    loadUsers();
+                } else {
+                    alert('Failed to activate user');
+                }
+            } catch (error) {
+                console.error('Error:', error);
+                alert('Failed to activate user');
+            }
+        }
+
+        async function deleteUser(email) {
+            if (!confirm(`⚠️ PERMANENTLY delete user ${email}? This cannot be undone!`)) return;
+            if (!confirm(`Are you absolutely sure? All user data will be deleted.`)) return;
+            
+            const reason = prompt('Enter reason for deletion:');
+            if (!reason) return;
+            
+            try {
+                const response = await fetch(`${API_URL}/api/users/${email}`, {
+                    method: 'DELETE',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({ reason })
+                });
+                
+                if (response.ok) {
+                    alert(`User ${email} has been permanently deleted`);
+                    loadUsers();
+                } else {
+                    alert('Failed to delete user');
+                }
+            } catch (error) {
+                console.error('Error:', error);
+                alert('Failed to delete user');
+            }
+        }
+
+        async function blockUser(email) {
+            if (!confirm(`Block user ${email}?`)) return;
+            
+            const reason = prompt('Enter reason for blocking:');
+            if (!reason) return;
+            
+            try {
+                const response = await fetch(`${API_URL}/api/users/${email}/block`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({ blocked: true, reason })
+                });
+                
+                if (response.ok) {
+                    alert(`User ${email} has been blocked`);
+                    loadUsers();
+                } else {
+                    alert('Failed to block user');
+                }
+            } catch (error) {
+                console.error('Error:', error);
+                alert('Failed to block user');
+            }
+        }
+
+        async function unblockUser(email) {
+            if (!confirm(`Unblock user ${email}?`)) return;
+            
+            try {
+                const response = await fetch(`${API_URL}/api/users/${email}/block`, {
+                    method: 'POST',
+                    headers: {
+                        'Authorization': `Bearer ${authToken}`,
+                        'Content-Type': 'application/json'
+                    },
+                    body: JSON.stringify({ blocked: false })
+                });
+                
+                if (response.ok) {
+                    alert(`User ${email} has been unblocked`);
+                    loadUsers();
+                } else {
+                    alert('Failed to unblock user');
+                }
+            } catch (error) {
+                console.error('Error:', error);
+                alert('Failed to unblock user');
+            }
+        }
+
+        // Initialize Google Sign-In
+        function initializeGoogleSignIn() {
+            if (typeof google !== 'undefined' && google.accounts && google.accounts.id) {
+                google.accounts.id.initialize({
+                    client_id: '493480746821-mhpjc0glkkogcf845ubi2hkhksoqj39k.apps.googleusercontent.com',
+                    callback: handleGoogleLogin,
+                    auto_select: false,
+                    cancel_on_tap_outside: false,
+                    ux_mode: 'popup'
+                });
+                
+                google.accounts.id.renderButton(
+                    document.querySelector('.g_id_signin'),
+                    {
+                        theme: 'outline',
+                        size: 'large',
+                        text: 'signin_with',
+                        shape: 'rectangular',
+                        width: 250
+                    }
+                );
+            } else {
+                // Retry after a delay if Google library hasn't loaded yet
+                setTimeout(initializeGoogleSignIn, 500);
+            }
+        }
+
+        // Social Media Sharing Functions with unique Bubbe intros
+        const bubbeIntros = [
+            "Bubbe tells it like it is",
+            "Bubbe's got no filter",
+            "Bubbe says what you're thinking",
+            "Bubbe drops truth bombs",
+            "Bubbe keeps it real",
+            "Bubbe's wisdom hits different",
+            "Bubbe doesn't sugarcoat",
+            "Bubbe serves reality checks",
+            "Bubbe's brutal honesty",
+            "Bubbe speaks facts",
+            "Bubbe's savage advice",
+            "Bubbe pulls no punches",
+            "Bubbe's hot take",
+            "Bubbe's unfiltered thoughts",
+            "Bubbe lays down the law",
+            "Bubbe's tough love",
+            "Bubbe's reality therapy",
+            "Bubbe's truth serum",
+            "Bubbe's straight talk",
+            "Bubbe's no-nonsense wisdom"
+        ];
+        
+        let introIndex = 0;
+        
+        function getUniqueIntro() {
+            const intro = bubbeIntros[introIndex % bubbeIntros.length];
+            introIndex++;
+            return intro;
+        }
+
+        function shareToTwitter(messageId, label, text) {
+            const intro = getUniqueIntro();
+            const decodedText = decodeURIComponent(text);
+            const preview = decodedText.substring(0, 140);
+            const shareText = `${intro}: "${preview}..." #BubbeAI #Bubbe #NoFilter #JewishGrandma\n\nVisit Bubbe.AI`;
+            const url = `https://twitter.com/intent/tweet?text=${encodeURIComponent(shareText)}`;
+            window.open(url, '_blank', 'width=600,height=400');
+        }
+
+        function shareToFacebook(messageId, text) {
+            const intro = getUniqueIntro();
+            const decodedText = decodeURIComponent(text);
+            const preview = decodedText.substring(0, 250);
+            const shareText = `${intro}:\n\n"${preview}..."\n\n#BubbeAI #Bubbe #JewishWisdom #NoFilter\n\nVisit Bubbe.AI`;
+            const url = `https://www.facebook.com/sharer/sharer.php?quote=${encodeURIComponent(shareText)}`;
+            window.open(url, '_blank', 'width=600,height=400');
+        }
+
+        function shareToLinkedIn(messageId, text) {
+            const intro = getUniqueIntro();
+            const decodedText = decodeURIComponent(text);
+            const preview = decodedText.substring(0, 200);
+            const shareText = `${intro}:\n\n"${preview}..."\n\nAI-powered wisdom with zero sugar-coating. #BubbeAI #Bubbe #CustomerExperience #AIInnovation\n\nVisit Bubbe.AI`;
+            const url = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent('http://45.61.58.125:3011')}&summary=${encodeURIComponent(shareText)}`;
+            window.open(url, '_blank', 'width=600,height=400');
+        }
+
+        function copyMessage(messageId, label, text) {
+            const fullText = `${label}: ${decodeURIComponent(text)}`;
+            navigator.clipboard.writeText(fullText).then(() => {
+                const btn = event.target;
+                const originalText = btn.innerHTML;
+                btn.innerHTML = '✓ Copied!';
+                btn.style.background = '#28a745';
+                setTimeout(() => {
+                    btn.innerHTML = originalText;
+                    btn.style.background = '';
+                }, 2000);
+            }).catch(err => {
+                alert('Failed to copy text');
+            });
+        }
+
+        function flagMessage(messageId, userId, text) {
+            if (confirm('Flag this message as inappropriate or concerning?')) {
+                const flaggedMessages = JSON.parse(localStorage.getItem('flaggedMessages') || '[]');
+                
+                flaggedMessages.push({
+                    id: messageId,
+                    userId: userId,
+                    text: decodeURIComponent(text),
+                    timestamp: new Date().toISOString(),
+                    flaggedBy: 'Admin'
+                });
+                
+                localStorage.setItem('flaggedMessages', JSON.stringify(flaggedMessages));
+                
+                const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
+                if (messageElement) {
+                    messageElement.style.border = '2px solid #dc3545';
+                    messageElement.style.background = '#fff5f5';
+                }
+                
+                showNotification('Message flagged for review', 'warning');
+                updateFlaggedCount();
+            }
+        }
+
+        function showNotification(message, type = 'info') {
+            const notification = document.createElement('div');
+            notification.style.cssText = `
+                position: fixed;
+                top: 20px;
+                right: 20px;
+                padding: 15px 20px;
+                background: ${type === 'warning' ? '#ff9800' : type === 'error' ? '#dc3545' : '#28a745'};
+                color: white;
+                border-radius: 8px;
+                box-shadow: 0 2px 10px rgba(0,0,0,0.2);
+                z-index: 10000;
+                animation: slideIn 0.3s;
+            `;
+            notification.textContent = message;
+            document.body.appendChild(notification);
+            
+            setTimeout(() => {
+                notification.style.animation = 'slideOut 0.3s';
+                setTimeout(() => notification.remove(), 300);
+            }, 3000);
+        }
+
+        function updateFlaggedCount() {
+            const flaggedMessages = JSON.parse(localStorage.getItem('flaggedMessages') || '[]');
+            const badges = document.querySelectorAll('.flagged-count');
+            badges.forEach(badge => {
+                badge.textContent = flaggedMessages.length;
+                badge.style.display = flaggedMessages.length > 0 ? 'inline-block' : 'none';
+            });
+        }
+
+        function scrollToUserChat(userId) {
+            const chatSection = document.getElementById('liveChatsDisplay');
+            if (chatSection) {
+                const userChat = chatSection.querySelector(`[data-user-id="${userId}"]`);
+                if (userChat) {
+                    userChat.scrollIntoView({ behavior: 'smooth', block: 'start' });
+                    userChat.style.background = '#fffbf0';
+                    setTimeout(() => {
+                        userChat.style.background = '';
+                    }, 2000);
+                }
+            }
+        }
+
+        // Export functions
+        function exportAlerts() {
+            const alerts = [];
+            const alertElements = document.querySelectorAll('#alertsList .alert-item');
+            alertElements.forEach(el => {
+                alerts.push({
+                    type: el.querySelector('.alert-badge')?.textContent || '',
+                    user: el.querySelector('strong')?.textContent || '',
+                    message: el.querySelector('div[style*="color: #666"]')?.textContent || '',
+                    timestamp: el.querySelector('div[style*="color: #999"]')?.textContent || ''
+                });
+            });
+            
+            if (alerts.length === 0) {
+                showNotification('No alerts to export', 'warning');
+                return;
+            }
+            
+            const csv = convertToCSV(alerts);
+            downloadCSV(csv, 'alerts_export.csv');
+            showNotification('Alerts exported successfully', 'success');
+        }
+
+        function exportUsers() {
+            fetch(`${API_URL}/api/users`, {
+                headers: {
+                    'Authorization': `Bearer ${authToken}`
+                }
+            })
+            .then(res => res.json())
+            .then(users => {
+                const csv = convertToCSV(users);
+                downloadCSV(csv, 'users_export.csv');
+                showNotification('Users exported successfully', 'success');
+            })
+            .catch(err => {
+                showNotification('Failed to export users', 'error');
+            });
+        }
+
+        function exportChats() {
+            const chats = Array.from(liveChats.values());
+            if (chats.length === 0) {
+                showNotification('No chats to export', 'warning');
+                return;
+            }
+            
+            const exportData = chats.map(chat => ({
+                userId: chat.userId,
+                userName: chat.userName,
+                email: chat.userEmail,
+                ip: chat.ip,
+                location: chat.location,
+                messageCount: chat.messages.length,
+                lastMessage: chat.messages[chat.messages.length - 1]?.text || ''
+            }));
+            const csv = convertToCSV(exportData);
+            downloadCSV(csv, 'chats_export.csv');
+            showNotification('Chats exported successfully', 'success');
+        }
+
+        function convertToCSV(data) {
+            if (!data || data.length === 0) return '';
+            
+            const headers = Object.keys(data[0]);
+            const csvHeaders = headers.join(',');
+            
+            const csvRows = data.map(row => 
+                headers.map(header => {
+                    const value = row[header];
+                    const escaped = String(value || '').replace(/"/g, '""');
+                    return escaped.includes(',') || escaped.includes('\n') ? `"${escaped}"` : escaped;
+                }).join(',')
+            );
+            
+            return [csvHeaders, ...csvRows].join('\n');
+        }
+
+        function downloadCSV(csv, filename) {
+            const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
+            const link = document.createElement('a');
+            link.href = URL.createObjectURL(blob);
+            link.download = filename;
+            link.click();
+        }
+
+        function clearAlerts() {
+            if (confirm('Clear all alerts? This cannot be undone.')) {
+                document.getElementById('alertsList').innerHTML = '<div style="color: #666; text-align: center;">No alerts to display</div>';
+                document.getElementById('totalAlerts').textContent = '0';
+                showNotification('Alerts cleared', 'info');
+            }
+        }
+
+        function clearChats() {
+            if (confirm('Clear all chat history? This cannot be undone.')) {
+                liveChats.clear();
+                document.getElementById('liveChatsDisplay').innerHTML = '<div style="color: #666; text-align: center;">No active chats</div>';
+                showNotification('Chat history cleared', 'info');
+            }
+        }
+
+        function refreshDashboard() {
+            showNotification('Refreshing dashboard...', 'info');
+            loadDashboard();
+        }
+
+        function refreshAlerts() {
+            loadAlerts();
+            showNotification('Alerts refreshed', 'success');
+        }
+
+        function refreshUsers() {
+            loadUsers();
+            showNotification('Users refreshed', 'success');
+        }
+
+        function refreshChats() {
+            loadActiveSessions();
+            showNotification('Chats refreshed', 'success');
+        }
+
+        function generateReport() {
+            const report = {
+                generatedAt: new Date().toISOString(),
+                totalUsers: parseInt(document.getElementById('totalUsers').textContent) || 0,
+                activeUsers: parseInt(document.getElementById('activeUsers').textContent) || 0,
+                totalAlerts: parseInt(document.getElementById('totalAlerts').textContent) || 0,
+                blockedUsers: parseInt(document.getElementById('blockedUsers').textContent) || 0,
+                flaggedMessages: JSON.parse(localStorage.getItem('flaggedMessages') || '[]').length,
+                bannedTermsCount: allBannedTerms.length,
+                acceptableTermsCount: document.querySelectorAll('#acceptableTermsList > div').length
+            };
+            
+            const reportText = `
+DEAR BUBBE ADMIN REPORT
+Generated: ${new Date().toLocaleString()}
+========================================
+
+STATISTICS:
+- Total Users: ${report.totalUsers}
+- Active Users: ${report.activeUsers}
+- Total Alerts: ${report.totalAlerts}
+- Blocked Users: ${report.blockedUsers}
+- Flagged Messages: ${report.flaggedMessages}
+
+CONTENT MODERATION:
+- Banned Terms: ${report.bannedTermsCount}
+- Acceptable Terms: ${report.acceptableTermsCount}
+
+========================================
+            `.trim();
+            
+            const blob = new Blob([reportText], { type: 'text/plain;charset=utf-8;' });
+            const link = document.createElement('a');
+            link.href = URL.createObjectURL(blob);
+            link.download = `bubbe_report_${Date.now()}.txt`;
+            link.click();
+            
+            showNotification('Report generated successfully', 'success');
+        }
+
+        // Check for existing token on load
+        window.onload = function() {
+            const token = localStorage.getItem('adminToken');
+            if (token) {
+                authToken = token;
+                // Auto-login with stored token
+                // You might want to validate the token first
+                document.getElementById('loginScreen').style.display = 'none';
+                document.getElementById('dashboard').classList.add('active');
+                initDashboard();
+            }
+            
+            // Initialize Google Sign-In
+            initializeGoogleSignIn();
+        }
+    </script>
+
+    <style>
+        @keyframes slideIn {
+            from {
+                opacity: 0;
+                transform: translateY(-20px);
+            }
+            to {
+                opacity: 1;
+                transform: translateY(0);
+            }
+        }
+
+        /* Live Chat Styles */
+        @keyframes pulse {
+            0% { opacity: 1; }
+            50% { opacity: 0.5; }
+            100% { opacity: 1; }
+        }
+
+        .chat-message {
+            margin: 10px 0;
+            padding: 10px;
+            border-radius: 8px;
+            animation: slideIn 0.3s ease;
+        }
+
+        .chat-user {
+            background: #e3f2fd;
+            border-left: 3px solid #2196f3;
+        }
+
+        .chat-bubbe {
+            background: #fff3e0;
+            border-left: 3px solid #ff9800;
+        }
+
+        .chat-header {
+            display: flex;
+            justify-content: space-between;
+            margin-bottom: 5px;
+            font-size: 12px;
+            color: #666;
+        }
+
+        .chat-text {
+            color: #333;
+            word-wrap: break-word;
+        }
+
+        .user-actions {
+            display: flex;
+            gap: 5px;
+        }
+
+        .action-btn {
+            padding: 4px 8px;
+            border: none;
+            border-radius: 4px;
+            cursor: pointer;
+            font-size: 12px;
+            transition: all 0.3s;
+        }
+
+        .btn-deactivate {
+            background: #ffc107;
+            color: white;
+        }
+
+        .btn-delete {
+            background: #dc3545;
+            color: white;
+        }
+
+        .btn-activate {
+            background: #28a745;
+            color: white;
+        }
+
+        .btn-block {
+            background: #6c757d;
+            color: white;
+        }
+
+        .action-btn:hover {
+            opacity: 0.8;
+            transform: scale(1.05);
+        }
+
+        /* Active Users Bar Styles */
+        .active-user-badge {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+            background: rgba(255, 255, 255, 0.2);
+            padding: 5px 12px;
+            border-radius: 20px;
+            white-space: nowrap;
+            animation: slideIn 0.3s ease;
+        }
+
+        .active-user-badge:hover {
+            background: rgba(255, 255, 255, 0.3);
+            cursor: pointer;
+        }
+
+        .user-avatar {
+            width: 24px;
+            height: 24px;
+            border-radius: 50%;
+            background: white;
+            color: #667eea;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-weight: bold;
+            font-size: 12px;
+        }
+
+        .user-info-text {
+            display: flex;
+            flex-direction: column;
+            gap: 2px;
+        }
+
+        .user-email-text {
+            font-size: 12px;
+            font-weight: 500;
+        }
+
+        .user-status-text {
+            font-size: 10px;
+            opacity: 0.9;
+        }
+
+        @keyframes fadeInOut {
+            0%, 100% { opacity: 0.5; }
+            50% { opacity: 1; }
+        }
+
+        .typing-indicator {
+            display: inline-flex;
+            gap: 2px;
+            margin-left: 5px;
+        }
+
+        .typing-dot {
+            width: 3px;
+            height: 3px;
+            background: white;
+            border-radius: 50%;
+            animation: bounce 1.4s infinite;
+        }
+
+        .typing-dot:nth-child(2) {
+            animation-delay: 0.2s;
+        }
+
+        .typing-dot:nth-child(3) {
+            animation-delay: 0.4s;
+        }
+
+        @keyframes bounce {
+            0%, 60%, 100% {
+                transform: translateY(0);
+            }
+            30% {
+                transform: translateY(-5px);
+            }
+        }
+
+        /* Add padding to bottom of container to avoid overlap with active users bar */
+        .container {
+            padding-bottom: 80px;
+        }
+
+        /* Social Share Buttons */
+        .social-share-buttons {
+            opacity: 0.7;
+            transition: opacity 0.3s;
+        }
+
+        .chat-message:hover .social-share-buttons {
+            opacity: 1;
+        }
+
+        .social-btn {
+            padding: 4px 8px;
+            border: none;
+            border-radius: 4px;
+            cursor: pointer;
+            transition: all 0.3s;
+            display: inline-flex;
+            align-items: center;
+            justify-content: center;
+            font-size: 12px;
+        }
+
+        .twitter-btn {
+            background: #1DA1F2;
+            color: white;
+        }
+
+        .twitter-btn:hover {
+            background: #1a91da;
+            transform: scale(1.1);
+        }
+
+        .facebook-btn {
+            background: #4267B2;
+            color: white;
+        }
+
+        .facebook-btn:hover {
+            background: #365899;
+            transform: scale(1.1);
+        }
+
+        .linkedin-btn {
+            background: #0077B5;
+            color: white;
+        }
+
+        .linkedin-btn:hover {
+            background: #006097;
+            transform: scale(1.1);
+        }
+
+        .copy-btn {
+            background: #6c757d;
+            color: white;
+        }
+
+        .copy-btn:hover {
+            background: #5a6268;
+            transform: scale(1.1);
+        }
+
+        .flag-btn {
+            background: #dc3545;
+            color: white;
+        }
+
+        .flag-btn:hover {
+            background: #c82333;
+            transform: scale(1.1);
+        }
+
+        .toast-notification {
+            position: fixed;
+            bottom: 100px;
+            right: 20px;
+            background: #333;
+            color: white;
+            padding: 12px 20px;
+            border-radius: 8px;
+            z-index: 10000;
+            animation: slideInRight 0.3s ease;
+            display: none;
+        }
+
+        .toast-notification.show {
+            display: block;
+        }
+
+        @keyframes slideInRight {
+            from {
+                transform: translateX(100%);
+                opacity: 0;
+            }
+            to {
+                transform: translateX(0);
+                opacity: 1;
+            }
+        }
+    </style>
+</body>
+</html>
\ No newline at end of file
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000..23032ce
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,47 @@
+{
+  "name": "dear-bubbe-admin-frontend",
+  "version": "0.1.0",
+  "private": true,
+  "dependencies": {
+    "@testing-library/jest-dom": "^5.16.4",
+    "@testing-library/react": "^13.3.0",
+    "@testing-library/user-event": "^13.5.0",
+    "axios": "^1.6.0",
+    "chart.js": "^4.4.0",
+    "chartjs-adapter-date-fns": "^3.0.0",
+    "date-fns": "^2.30.0",
+    "react": "^18.2.0",
+    "react-chartjs-2": "^5.2.0",
+    "react-dom": "^18.2.0",
+    "react-router-dom": "^6.8.1",
+    "react-scripts": "5.0.1",
+    "socket.io-client": "^4.7.4",
+    "web-vitals": "^2.1.4",
+    "recharts": "^2.8.0"
+  },
+  "scripts": {
+    "start": "react-scripts start",
+    "build": "react-scripts build",
+    "test": "react-scripts test",
+    "eject": "react-scripts eject"
+  },
+  "eslintConfig": {
+    "extends": [
+      "react-app",
+      "react-app/jest"
+    ]
+  },
+  "browserslist": {
+    "production": [
+      ">0.2%",
+      "not dead",
+      "not op_mini all"
+    ],
+    "development": [
+      "last 1 chrome version",
+      "last 1 firefox version",
+      "last 1 safari version"
+    ]
+  },
+  "proxy": "http://localhost:3015"
+}
\ No newline at end of file
diff --git a/frontend/public/index.html b/frontend/public/index.html
new file mode 100644
index 0000000..b967ba3
--- /dev/null
+++ b/frontend/public/index.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <meta name="theme-color" content="#000000" />
+    <meta
+      name="description"
+      content="Dear Bubbe Admin Dashboard - Real-time Monitoring and Alert System"
+    />
+    <title>Dear Bubbe Admin Dashboard</title>
+    <style>
+      body {
+        margin: 0;
+        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+          'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+          sans-serif;
+        -webkit-font-smoothing: antialiased;
+        -moz-osx-font-smoothing: grayscale;
+        background-color: #f8fafc;
+      }
+      code {
+        font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+          monospace;
+      }
+      
+      /* Loading spinner */
+      .loading-spinner {
+        display: inline-block;
+        width: 20px;
+        height: 20px;
+        border: 3px solid #f3f3f3;
+        border-top: 3px solid #3498db;
+        border-radius: 50%;
+        animation: spin 1s linear infinite;
+      }
+      
+      @keyframes spin {
+        0% { transform: rotate(0deg); }
+        100% { transform: rotate(360deg); }
+      }
+      
+      /* Alert styles */
+      .alert {
+        padding: 12px 16px;
+        margin: 8px 0;
+        border-radius: 4px;
+        border-left: 4px solid;
+      }
+      
+      .alert-critical {
+        background-color: #fee;
+        border-left-color: #dc2626;
+        color: #dc2626;
+      }
+      
+      .alert-high {
+        background-color: #fef3cd;
+        border-left-color: #f59e0b;
+        color: #f59e0b;
+      }
+      
+      .alert-medium {
+        background-color: #e1f5fe;
+        border-left-color: #3b82f6;
+        color: #3b82f6;
+      }
+      
+      .alert-low {
+        background-color: #f0f9ff;
+        border-left-color: #6b7280;
+        color: #6b7280;
+      }
+    </style>
+  </head>
+  <body>
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+  </body>
+</html>
\ No newline at end of file
diff --git a/frontend/src/App.css b/frontend/src/App.css
new file mode 100644
index 0000000..f47abcd
--- /dev/null
+++ b/frontend/src/App.css
@@ -0,0 +1,579 @@
+/* Dear Bubbe Admin Dashboard Styles */
+
+* {
+  box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+}
+
+body {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+  background-color: #f8fafc;
+  color: #334155;
+  line-height: 1.6;
+}
+
+.app {
+  display: flex;
+  min-height: 100vh;
+}
+
+/* Loading */
+.loading-container {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  min-height: 100vh;
+  background-color: #f8fafc;
+}
+
+.loading-container p {
+  margin-top: 16px;
+  color: #64748b;
+  font-size: 16px;
+}
+
+/* Sidebar */
+.sidebar {
+  width: 250px;
+  background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
+  color: white;
+  flex-shrink: 0;
+  box-shadow: 4px 0 8px rgba(0, 0, 0, 0.1);
+  position: relative;
+  z-index: 10;
+}
+
+.sidebar-header {
+  padding: 24px 20px;
+  border-bottom: 1px solid #475569;
+}
+
+.sidebar-title {
+  font-size: 20px;
+  font-weight: 700;
+  color: #f1f5f9;
+  text-align: center;
+}
+
+.sidebar-subtitle {
+  font-size: 12px;
+  color: #94a3b8;
+  text-align: center;
+  margin-top: 4px;
+}
+
+.nav-menu {
+  padding: 20px 0;
+}
+
+.nav-item {
+  display: block;
+  padding: 12px 20px;
+  color: #cbd5e1;
+  text-decoration: none;
+  transition: all 0.2s;
+  border-left: 3px solid transparent;
+}
+
+.nav-item:hover {
+  background-color: rgba(255, 255, 255, 0.1);
+  color: white;
+  border-left-color: #3b82f6;
+}
+
+.nav-item.active {
+  background-color: rgba(59, 130, 246, 0.2);
+  color: white;
+  border-left-color: #3b82f6;
+}
+
+.nav-icon {
+  margin-right: 10px;
+  width: 16px;
+  display: inline-block;
+}
+
+/* Main Content */
+.main-content {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+/* Header */
+.header {
+  background: white;
+  padding: 16px 24px;
+  border-bottom: 1px solid #e2e8f0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.header-left h1 {
+  font-size: 24px;
+  font-weight: 600;
+  color: #1e293b;
+}
+
+.header-stats {
+  display: flex;
+  gap: 24px;
+  margin-top: 8px;
+}
+
+.stat-item {
+  font-size: 14px;
+  color: #64748b;
+}
+
+.stat-value {
+  font-weight: 600;
+  color: #1e293b;
+}
+
+.header-right {
+  display: flex;
+  align-items: center;
+  gap: 16px;
+}
+
+.user-info {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 8px 12px;
+  background-color: #f1f5f9;
+  border-radius: 8px;
+}
+
+.user-avatar {
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  background: linear-gradient(135deg, #3b82f6, #1d4ed8);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: white;
+  font-weight: 600;
+  font-size: 14px;
+}
+
+.user-name {
+  font-weight: 500;
+  color: #1e293b;
+}
+
+.logout-btn {
+  padding: 8px 16px;
+  background-color: #ef4444;
+  color: white;
+  border: none;
+  border-radius: 6px;
+  cursor: pointer;
+  font-size: 14px;
+  transition: background-color 0.2s;
+}
+
+.logout-btn:hover {
+  background-color: #dc2626;
+}
+
+/* Content */
+.content {
+  flex: 1;
+  padding: 24px;
+  overflow-y: auto;
+  background-color: #f8fafc;
+}
+
+/* Cards */
+.card {
+  background: white;
+  border-radius: 8px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  overflow: hidden;
+}
+
+.card-header {
+  padding: 20px 24px;
+  border-bottom: 1px solid #e2e8f0;
+  background-color: #f8fafc;
+}
+
+.card-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1e293b;
+  margin: 0;
+}
+
+.card-content {
+  padding: 24px;
+}
+
+/* Dashboard Grid */
+.dashboard-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+  gap: 24px;
+  margin-bottom: 24px;
+}
+
+.stat-cards {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+  gap: 20px;
+  margin-bottom: 24px;
+}
+
+.stat-card {
+  background: white;
+  padding: 24px;
+  border-radius: 8px;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  border-left: 4px solid #3b82f6;
+}
+
+.stat-card.critical {
+  border-left-color: #ef4444;
+}
+
+.stat-card.warning {
+  border-left-color: #f59e0b;
+}
+
+.stat-card.success {
+  border-left-color: #10b981;
+}
+
+.stat-label {
+  font-size: 14px;
+  color: #64748b;
+  margin-bottom: 8px;
+}
+
+.stat-value-large {
+  font-size: 32px;
+  font-weight: 700;
+  color: #1e293b;
+  line-height: 1;
+}
+
+/* Buttons */
+.btn {
+  padding: 8px 16px;
+  border-radius: 6px;
+  border: none;
+  cursor: pointer;
+  font-size: 14px;
+  font-weight: 500;
+  transition: all 0.2s;
+  text-decoration: none;
+  display: inline-block;
+  text-align: center;
+}
+
+.btn-primary {
+  background-color: #3b82f6;
+  color: white;
+}
+
+.btn-primary:hover {
+  background-color: #2563eb;
+}
+
+.btn-danger {
+  background-color: #ef4444;
+  color: white;
+}
+
+.btn-danger:hover {
+  background-color: #dc2626;
+}
+
+.btn-secondary {
+  background-color: #6b7280;
+  color: white;
+}
+
+.btn-secondary:hover {
+  background-color: #4b5563;
+}
+
+.btn-small {
+  padding: 6px 12px;
+  font-size: 12px;
+}
+
+/* Tables */
+.table-container {
+  overflow-x: auto;
+}
+
+.table {
+  width: 100%;
+  border-collapse: collapse;
+}
+
+.table th,
+.table td {
+  padding: 12px;
+  text-align: left;
+  border-bottom: 1px solid #e2e8f0;
+}
+
+.table th {
+  background-color: #f8fafc;
+  font-weight: 600;
+  color: #374151;
+  font-size: 14px;
+}
+
+.table td {
+  font-size: 14px;
+}
+
+.table tr:hover {
+  background-color: #f8fafc;
+}
+
+/* Status badges */
+.badge {
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  font-weight: 500;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+}
+
+.badge-critical {
+  background-color: #fef2f2;
+  color: #dc2626;
+}
+
+.badge-high {
+  background-color: #fef3c7;
+  color: #d97706;
+}
+
+.badge-medium {
+  background-color: #dbeafe;
+  color: #2563eb;
+}
+
+.badge-low {
+  background-color: #f0fdf4;
+  color: #16a34a;
+}
+
+.badge-active {
+  background-color: #dcfce7;
+  color: #16a34a;
+}
+
+.badge-blocked {
+  background-color: #fee2e2;
+  color: #dc2626;
+}
+
+/* Forms */
+.form-group {
+  margin-bottom: 16px;
+}
+
+.form-label {
+  display: block;
+  margin-bottom: 4px;
+  font-weight: 500;
+  color: #374151;
+}
+
+.form-input {
+  width: 100%;
+  padding: 8px 12px;
+  border: 1px solid #d1d5db;
+  border-radius: 6px;
+  font-size: 14px;
+  transition: border-color 0.2s;
+}
+
+.form-input:focus {
+  outline: none;
+  border-color: #3b82f6;
+  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
+}
+
+.form-select {
+  width: 100%;
+  padding: 8px 12px;
+  border: 1px solid #d1d5db;
+  border-radius: 6px;
+  font-size: 14px;
+  background-color: white;
+  cursor: pointer;
+}
+
+/* Live Alert Modal */
+.live-alert-modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+  animation: fadeIn 0.2s ease-out;
+}
+
+.live-alert-content {
+  background: white;
+  border-radius: 8px;
+  padding: 24px;
+  max-width: 600px;
+  width: 90%;
+  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
+  animation: slideIn 0.2s ease-out;
+}
+
+@keyframes fadeIn {
+  from { opacity: 0; }
+  to { opacity: 1; }
+}
+
+@keyframes slideIn {
+  from { transform: scale(0.9) translateY(-20px); opacity: 0; }
+  to { transform: scale(1) translateY(0); opacity: 1; }
+}
+
+.alert-header {
+  display: flex;
+  justify-content: between;
+  align-items: center;
+  margin-bottom: 16px;
+}
+
+.alert-title {
+  font-size: 20px;
+  font-weight: 600;
+  color: #dc2626;
+}
+
+.alert-close {
+  background: none;
+  border: none;
+  font-size: 24px;
+  cursor: pointer;
+  color: #6b7280;
+  padding: 0;
+  width: 24px;
+  height: 24px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.alert-details {
+  margin-bottom: 20px;
+}
+
+.alert-detail-row {
+  display: flex;
+  margin-bottom: 8px;
+}
+
+.alert-detail-label {
+  font-weight: 500;
+  color: #374151;
+  width: 120px;
+  flex-shrink: 0;
+}
+
+.alert-detail-value {
+  color: #1f2937;
+}
+
+.alert-actions {
+  display: flex;
+  gap: 12px;
+  justify-content: flex-end;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+  .app {
+    flex-direction: column;
+  }
+  
+  .sidebar {
+    width: 100%;
+    height: auto;
+  }
+  
+  .nav-menu {
+    display: flex;
+    padding: 0;
+    overflow-x: auto;
+  }
+  
+  .nav-item {
+    white-space: nowrap;
+    border-left: none;
+    border-bottom: 3px solid transparent;
+  }
+  
+  .nav-item:hover,
+  .nav-item.active {
+    border-left: none;
+    border-bottom-color: #3b82f6;
+  }
+  
+  .header {
+    flex-direction: column;
+    gap: 12px;
+    align-items: flex-start;
+  }
+  
+  .header-stats {
+    flex-wrap: wrap;
+  }
+  
+  .content {
+    padding: 16px;
+  }
+  
+  .stat-cards {
+    grid-template-columns: 1fr;
+  }
+  
+  .dashboard-grid {
+    grid-template-columns: 1fr;
+  }
+}
+
+/* Print styles */
+@media print {
+  .sidebar,
+  .header,
+  .btn {
+    display: none;
+  }
+  
+  .content {
+    padding: 0;
+  }
+  
+  .card {
+    box-shadow: none;
+    border: 1px solid #e2e8f0;
+  }
+}
\ No newline at end of file
diff --git a/frontend/src/App.js b/frontend/src/App.js
new file mode 100644
index 0000000..a75a77d
--- /dev/null
+++ b/frontend/src/App.js
@@ -0,0 +1,261 @@
+import React, { useState, useEffect } from 'react'
+import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
+import axios from 'axios'
+import io from 'socket.io-client'
+
+// Components
+import Login from './components/Login'
+import Dashboard from './components/Dashboard'
+import AlertsPage from './components/AlertsPage'
+import UsersPage from './components/UsersPage'
+import ReportsPage from './components/ReportsPage'
+import MonitoringPage from './components/MonitoringPage'
+import Sidebar from './components/Sidebar'
+import Header from './components/Header'
+import LiveAlertModal from './components/LiveAlertModal'
+
+// Styles
+import './App.css'
+
+// API configuration
+const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://45.61.58.125:3015'
+const SOCKET_URL = process.env.REACT_APP_SOCKET_URL || 'http://45.61.58.125:3015'
+
+class App extends React.Component {
+  constructor(props) {
+    super(props)
+    
+    this.state = {
+      isAuthenticated: false,
+      user: null,
+      loading: true,
+      socket: null,
+      liveAlert: null,
+      stats: {},
+      error: null
+    }
+    
+    // Set up axios defaults
+    axios.defaults.baseURL = API_BASE_URL
+    
+    // Add token to requests if available
+    const token = localStorage.getItem('adminToken')
+    if (token) {
+      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
+    }
+  }
+  
+  async componentDidMount() {
+    await this.checkAuthStatus()
+  }
+  
+  componentWillUnmount() {
+    if (this.state.socket) {
+      this.state.socket.disconnect()
+    }
+  }
+  
+  checkAuthStatus = async () => {
+    try {
+      const token = localStorage.getItem('adminToken')
+      
+      if (!token) {
+        this.setState({ loading: false, isAuthenticated: false })
+        return
+      }
+      
+      const response = await axios.get('/api/auth/me')
+      
+      if (response.data.success) {
+        this.setState({
+          isAuthenticated: true,
+          user: response.data.user,
+          loading: false
+        })
+        
+        this.connectWebSocket(token)
+      } else {
+        localStorage.removeItem('adminToken')
+        this.setState({ loading: false, isAuthenticated: false })
+      }
+    } catch (error) {
+      console.error('Auth check failed:', error)
+      localStorage.removeItem('adminToken')
+      this.setState({ 
+        loading: false, 
+        isAuthenticated: false,
+        error: 'Authentication failed'
+      })
+    }
+  }
+  
+  connectWebSocket = (token) => {
+    try {
+      const socket = io(SOCKET_URL, {
+        auth: { token },
+        transports: ['websocket', 'polling']
+      })
+      
+      socket.on('connect', () => {
+        console.log('Connected to admin socket')
+        socket.emit('authenticate', token)
+      })
+      
+      socket.on('auth_error', (error) => {
+        console.error('Socket auth error:', error)
+        this.handleLogout()
+      })
+      
+      socket.on('stats', (stats) => {
+        this.setState({ stats })
+      })
+      
+      socket.on('new_alert', (alert) => {
+        console.log('🚨 New alert received:', alert)
+        this.setState({ liveAlert: alert })
+        
+        // Play alert sound if critical
+        if (alert.severity === 'critical' || alert.severity === 'high') {
+          this.playAlertSound()
+        }
+      })
+      
+      socket.on('system_alert', (alert) => {
+        console.log('⚠️ System alert:', alert)
+      })
+      
+      socket.on('threat_response', (data) => {
+        console.log('🚨 Threat response sent:', data)
+      })
+      
+      socket.on('disconnect', () => {
+        console.log('Disconnected from admin socket')
+      })
+      
+      this.setState({ socket })
+      
+    } catch (error) {
+      console.error('Failed to connect WebSocket:', error)
+    }
+  }
+  
+  playAlertSound = () => {
+    // Create a simple alert tone
+    try {
+      const audioContext = new (window.AudioContext || window.webkitAudioContext)()
+      const oscillator = audioContext.createOscillator()
+      const gainNode = audioContext.createGain()
+      
+      oscillator.connect(gainNode)
+      gainNode.connect(audioContext.destination)
+      
+      oscillator.frequency.value = 800
+      oscillator.type = 'square'
+      
+      gainNode.gain.setValueAtTime(0.3, audioContext.currentTime)
+      gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5)
+      
+      oscillator.start(audioContext.currentTime)
+      oscillator.stop(audioContext.currentTime + 0.5)
+    } catch (error) {
+      console.log('Could not play alert sound:', error)
+    }
+  }
+  
+  handleLogin = async (credentials) => {
+    try {
+      const response = await axios.post('/api/auth/login', credentials)
+      
+      if (response.data.success) {
+        const { token, user } = response.data
+        
+        localStorage.setItem('adminToken', token)
+        axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
+        
+        this.setState({
+          isAuthenticated: true,
+          user,
+          error: null
+        })
+        
+        this.connectWebSocket(token)
+        
+        return { success: true }
+      } else {
+        return { success: false, error: response.data.error }
+      }
+    } catch (error) {
+      const errorMessage = error.response?.data?.error || 'Login failed'
+      return { success: false, error: errorMessage }
+    }
+  }
+  
+  handleLogout = () => {
+    localStorage.removeItem('adminToken')
+    delete axios.defaults.headers.common['Authorization']
+    
+    if (this.state.socket) {
+      this.state.socket.disconnect()
+    }
+    
+    this.setState({
+      isAuthenticated: false,
+      user: null,
+      socket: null,
+      stats: {},
+      error: null
+    })
+  }
+  
+  closeLiveAlert = () => {
+    this.setState({ liveAlert: null })
+  }
+  
+  render() {
+    const { isAuthenticated, user, loading, liveAlert, stats, error } = this.state
+    
+    if (loading) {
+      return (
+        <div className="loading-container">
+          <div className="loading-spinner"></div>
+          <p>Loading Dear Bubbe Admin Dashboard...</p>
+        </div>
+      )
+    }
+    
+    if (!isAuthenticated) {
+      return <Login onLogin={this.handleLogin} error={error} />
+    }
+    
+    return (
+      <Router>
+        <div className="app">
+          <Sidebar />
+          <div className="main-content">
+            <Header user={user} stats={stats} onLogout={this.handleLogout} />
+            <div className="content">
+              <Routes>
+                <Route path="/" element={<Dashboard stats={stats} />} />
+                <Route path="/dashboard" element={<Dashboard stats={stats} />} />
+                <Route path="/alerts" element={<AlertsPage />} />
+                <Route path="/users" element={<UsersPage />} />
+                <Route path="/monitoring" element={<MonitoringPage />} />
+                <Route path="/reports" element={<ReportsPage />} />
+                <Route path="*" element={<Navigate to="/dashboard" replace />} />
+              </Routes>
+            </div>
+          </div>
+          
+          {liveAlert && (
+            <LiveAlertModal 
+              alert={liveAlert} 
+              onClose={this.closeLiveAlert} 
+            />
+          )}
+        </div>
+      </Router>
+    )
+  }
+}
+
+export default App
\ No newline at end of file
diff --git a/frontend/src/components/AlertsPage.js b/frontend/src/components/AlertsPage.js
new file mode 100644
index 0000000..9cab940
--- /dev/null
+++ b/frontend/src/components/AlertsPage.js
@@ -0,0 +1,470 @@
+import React, { useState, useEffect } from 'react'
+import axios from 'axios'
+
+const AlertsPage = () => {
+  const [alerts, setAlerts] = useState([])
+  const [loading, setLoading] = useState(true)
+  const [error, setError] = useState(null)
+  const [filters, setFilters] = useState({
+    type: '',
+    severity: '',
+    status: '',
+    timeframe: '24h'
+  })
+  const [selectedAlert, setSelectedAlert] = useState(null)
+  const [stats, setStats] = useState({})
+
+  useEffect(() => {
+    fetchAlerts()
+    fetchAlertStats()
+  }, [filters])
+
+  const fetchAlerts = async () => {
+    try {
+      setError(null)
+      const params = new URLSearchParams()
+      Object.entries(filters).forEach(([key, value]) => {
+        if (value) params.append(key, value)
+      })
+
+      const response = await axios.get(`/api/alerts?${params.toString()}`)
+      
+      if (response.data.success) {
+        setAlerts(response.data.alerts || [])
+      }
+    } catch (error) {
+      console.error('Failed to fetch alerts:', error)
+      setError('Failed to load alerts')
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  const fetchAlertStats = async () => {
+    try {
+      const response = await axios.get(`/api/alerts/stats?timeframe=${filters.timeframe}`)
+      if (response.data.success) {
+        setStats(response.data)
+      }
+    } catch (error) {
+      console.error('Failed to fetch alert stats:', error)
+    }
+  }
+
+  const handleStatusUpdate = async (alertId, status, reviewNotes = '') => {
+    try {
+      await axios.put(`/api/alerts/${alertId}/status`, {
+        status,
+        reviewNotes
+      })
+      
+      // Refresh alerts
+      fetchAlerts()
+      setSelectedAlert(null)
+    } catch (error) {
+      console.error('Failed to update alert status:', error)
+      alert('Failed to update alert status')
+    }
+  }
+
+  const handleBlockUser = async (alertId) => {
+    if (!confirm('Are you sure you want to block this user?')) return
+    
+    try {
+      await axios.post(`/api/alerts/${alertId}/block-user`, {
+        reason: 'Blocked due to security violation'
+      })
+      
+      alert('User has been blocked successfully')
+      fetchAlerts()
+      setSelectedAlert(null)
+    } catch (error) {
+      console.error('Failed to block user:', error)
+      alert('Failed to block user')
+    }
+  }
+
+  const getSeverityColor = (severity) => {
+    const colors = {
+      critical: '#ef4444',
+      high: '#f59e0b',
+      medium: '#3b82f6',
+      low: '#6b7280'
+    }
+    return colors[severity] || '#6b7280'
+  }
+
+  const getTypeIcon = (type) => {
+    const icons = {
+      antisemitism: '✡️',
+      racism: '⚠️',
+      harassment: '🚫',
+      spam: '📧',
+      system: '⚙️'
+    }
+    return icons[type] || '❗'
+  }
+
+  const formatTimestamp = (timestamp) => {
+    return new Date(timestamp).toLocaleString()
+  }
+
+  if (loading) {
+    return (
+      <div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
+        <div className="loading-spinner"></div>
+        <span style={{ marginLeft: '12px' }}>Loading alerts...</span>
+      </div>
+    )
+  }
+
+  return (
+    <div>
+      <div style={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        marginBottom: '24px'
+      }}>
+        <h2 style={{ fontSize: '24px', fontWeight: '600', color: '#1e293b' }}>
+          Security Alerts
+        </h2>
+        <button 
+          onClick={() => { fetchAlerts(); fetchAlertStats(); }}
+          className="btn btn-primary"
+        >
+          Refresh
+        </button>
+      </div>
+
+      {/* Alert Stats */}
+      <div className="stat-cards" style={{ marginBottom: '24px' }}>
+        <div className="stat-card critical">
+          <div className="stat-label">Critical Alerts</div>
+          <div className="stat-value-large" style={{ color: '#ef4444' }}>
+            {stats.summary?.critical || 0}
+          </div>
+        </div>
+        <div className="stat-card warning">
+          <div className="stat-label">High Priority</div>
+          <div className="stat-value-large" style={{ color: '#f59e0b' }}>
+            {stats.summary?.high || 0}
+          </div>
+        </div>
+        <div className="stat-card">
+          <div className="stat-label">Antisemitism</div>
+          <div className="stat-value-large" style={{ color: '#dc2626' }}>
+            {stats.summary?.antisemitism || 0}
+          </div>
+        </div>
+        <div className="stat-card">
+          <div className="stat-label">Racism</div>
+          <div className="stat-value-large" style={{ color: '#dc2626' }}>
+            {stats.summary?.racism || 0}
+          </div>
+        </div>
+      </div>
+
+      {/* Filters */}
+      <div className="card" style={{ marginBottom: '24px' }}>
+        <div className="card-header">
+          <h3 className="card-title">Filter Alerts</h3>
+        </div>
+        <div className="card-content">
+          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: '16px' }}>
+            <div>
+              <label className="form-label">Type</label>
+              <select
+                className="form-select"
+                value={filters.type}
+                onChange={(e) => setFilters({...filters, type: e.target.value})}
+              >
+                <option value="">All Types</option>
+                <option value="antisemitism">Antisemitism</option>
+                <option value="racism">Racism</option>
+                <option value="harassment">Harassment</option>
+                <option value="spam">Spam</option>
+                <option value="system">System</option>
+              </select>
+            </div>
+            
+            <div>
+              <label className="form-label">Severity</label>
+              <select
+                className="form-select"
+                value={filters.severity}
+                onChange={(e) => setFilters({...filters, severity: e.target.value})}
+              >
+                <option value="">All Severities</option>
+                <option value="critical">Critical</option>
+                <option value="high">High</option>
+                <option value="medium">Medium</option>
+                <option value="low">Low</option>
+              </select>
+            </div>
+            
+            <div>
+              <label className="form-label">Status</label>
+              <select
+                className="form-select"
+                value={filters.status}
+                onChange={(e) => setFilters({...filters, status: e.target.value})}
+              >
+                <option value="">All Status</option>
+                <option value="active">Active</option>
+                <option value="reviewed">Reviewed</option>
+                <option value="resolved">Resolved</option>
+                <option value="false_positive">False Positive</option>
+              </select>
+            </div>
+            
+            <div>
+              <label className="form-label">Timeframe</label>
+              <select
+                className="form-select"
+                value={filters.timeframe}
+                onChange={(e) => setFilters({...filters, timeframe: e.target.value})}
+              >
+                <option value="1h">Last Hour</option>
+                <option value="24h">Last 24 Hours</option>
+                <option value="7d">Last 7 Days</option>
+                <option value="30d">Last 30 Days</option>
+              </select>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Alerts Table */}
+      <div className="card">
+        <div className="card-header">
+          <h3 className="card-title">
+            Alerts ({alerts.length})
+          </h3>
+        </div>
+        <div className="card-content">
+          {error && (
+            <div style={{ color: '#ef4444', textAlign: 'center', padding: '20px' }}>
+              {error}
+            </div>
+          )}
+          
+          {alerts.length === 0 ? (
+            <div style={{ textAlign: 'center', padding: '40px', color: '#64748b' }}>
+              {filters.type || filters.severity || filters.status ? 
+                'No alerts match the current filters' : 
+                'No alerts found'
+              }
+            </div>
+          ) : (
+            <div className="table-container">
+              <table className="table">
+                <thead>
+                  <tr>
+                    <th>Type</th>
+                    <th>Severity</th>
+                    <th>User/IP</th>
+                    <th>Message</th>
+                    <th>Location</th>
+                    <th>Time</th>
+                    <th>Status</th>
+                    <th>Actions</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  {alerts.map((alert) => (
+                    <tr key={alert._id}>
+                      <td>
+                        <div style={{ display: 'flex', alignItems: 'center' }}>
+                          <span style={{ marginRight: '8px' }}>
+                            {getTypeIcon(alert.type)}
+                          </span>
+                          {alert.type}
+                        </div>
+                      </td>
+                      <td>
+                        <span 
+                          className={`badge badge-${alert.severity}`}
+                          style={{ 
+                            backgroundColor: getSeverityColor(alert.severity) + '20',
+                            color: getSeverityColor(alert.severity),
+                            border: `1px solid ${getSeverityColor(alert.severity)}40`
+                          }}
+                        >
+                          {alert.severity}
+                        </span>
+                      </td>
+                      <td>
+                        <div style={{ fontSize: '12px' }}>
+                          <div>{alert.userInfo?.userId || 'Unknown'}</div>
+                          <div style={{ color: '#64748b' }}>
+                            {alert.userInfo?.ip || 'Unknown IP'}
+                          </div>
+                        </div>
+                      </td>
+                      <td>
+                        <div style={{ 
+                          maxWidth: '200px', 
+                          overflow: 'hidden', 
+                          textOverflow: 'ellipsis',
+                          whiteSpace: 'nowrap',
+                          fontSize: '14px'
+                        }}>
+                          {alert.content?.message || 'No message'}
+                        </div>
+                        {alert.violation?.match && (
+                          <div style={{ 
+                            fontSize: '12px', 
+                            color: '#ef4444', 
+                            fontWeight: '500',
+                            marginTop: '4px'
+                          }}>
+                            Matched: "{alert.violation.match}"
+                          </div>
+                        )}
+                      </td>
+                      <td style={{ fontSize: '12px', color: '#64748b' }}>
+                        {alert.location?.city && alert.location?.country ? 
+                          `${alert.location.city}, ${alert.location.country}` : 
+                          'Unknown'
+                        }
+                      </td>
+                      <td style={{ fontSize: '12px', color: '#64748b' }}>
+                        {formatTimestamp(alert.timestamp)}
+                      </td>
+                      <td>
+                        <span className={`badge badge-${alert.status === 'active' ? 'critical' : 'medium'}`}>
+                          {alert.status}
+                        </span>
+                      </td>
+                      <td>
+                        <div style={{ display: 'flex', gap: '8px' }}>
+                          <button
+                            onClick={() => setSelectedAlert(alert)}
+                            className="btn btn-small btn-secondary"
+                          >
+                            View
+                          </button>
+                          {alert.status === 'active' && (
+                            <button
+                              onClick={() => handleStatusUpdate(alert._id, 'reviewed')}
+                              className="btn btn-small btn-primary"
+                            >
+                              Mark Reviewed
+                            </button>
+                          )}
+                        </div>
+                      </td>
+                    </tr>
+                  ))}
+                </tbody>
+              </table>
+            </div>
+          )}
+        </div>
+      </div>
+
+      {/* Alert Detail Modal */}
+      {selectedAlert && (
+        <div className="live-alert-modal" onClick={() => setSelectedAlert(null)}>
+          <div className="live-alert-content" onClick={e => e.stopPropagation()}>
+            <div className="alert-header">
+              <h3 className="alert-title">
+                {getTypeIcon(selectedAlert.type)} Alert Details
+              </h3>
+              <button 
+                className="alert-close"
+                onClick={() => setSelectedAlert(null)}
+              >
+                ×
+              </button>
+            </div>
+            
+            <div className="alert-details">
+              <div className="alert-detail-row">
+                <span className="alert-detail-label">Type:</span>
+                <span className="alert-detail-value">{selectedAlert.type}</span>
+              </div>
+              <div className="alert-detail-row">
+                <span className="alert-detail-label">Severity:</span>
+                <span className={`badge badge-${selectedAlert.severity}`}>
+                  {selectedAlert.severity}
+                </span>
+              </div>
+              <div className="alert-detail-row">
+                <span className="alert-detail-label">User ID:</span>
+                <span className="alert-detail-value">{selectedAlert.userInfo?.userId || 'Unknown'}</span>
+              </div>
+              <div className="alert-detail-row">
+                <span className="alert-detail-label">IP Address:</span>
+                <span className="alert-detail-value">{selectedAlert.userInfo?.ip || 'Unknown'}</span>
+              </div>
+              <div className="alert-detail-row">
+                <span className="alert-detail-label">Location:</span>
+                <span className="alert-detail-value">
+                  {selectedAlert.location?.city && selectedAlert.location?.country ? 
+                    `${selectedAlert.location.city}, ${selectedAlert.location.state}, ${selectedAlert.location.country}` : 
+                    'Unknown'
+                  }
+                </span>
+              </div>
+              <div className="alert-detail-row">
+                <span className="alert-detail-label">Message:</span>
+                <span className="alert-detail-value" style={{ 
+                  fontFamily: 'monospace',
+                  backgroundColor: '#f8fafc',
+                  padding: '8px',
+                  borderRadius: '4px',
+                  display: 'block',
+                  marginTop: '4px'
+                }}>
+                  {selectedAlert.content?.message || 'No message'}
+                </span>
+              </div>
+              {selectedAlert.violation?.match && (
+                <div className="alert-detail-row">
+                  <span className="alert-detail-label">Violation:</span>
+                  <span className="alert-detail-value" style={{ 
+                    color: '#ef4444',
+                    fontWeight: '500'
+                  }}>
+                    "{selectedAlert.violation.match}"
+                  </span>
+                </div>
+              )}
+              <div className="alert-detail-row">
+                <span className="alert-detail-label">Timestamp:</span>
+                <span className="alert-detail-value">{formatTimestamp(selectedAlert.timestamp)}</span>
+              </div>
+            </div>
+            
+            <div className="alert-actions">
+              <button
+                onClick={() => handleStatusUpdate(selectedAlert._id, 'false_positive', 'Marked as false positive')}
+                className="btn btn-secondary"
+              >
+                False Positive
+              </button>
+              <button
+                onClick={() => handleStatusUpdate(selectedAlert._id, 'resolved', 'Resolved by admin')}
+                className="btn btn-primary"
+              >
+                Resolve
+              </button>
+              {selectedAlert.userInfo?.userId && (
+                <button
+                  onClick={() => handleBlockUser(selectedAlert._id)}
+                  className="btn btn-danger"
+                >
+                  Block User
+                </button>
+              )}
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  )
+}
+
+export default AlertsPage
\ No newline at end of file
diff --git a/frontend/src/components/Dashboard.js b/frontend/src/components/Dashboard.js
new file mode 100644
index 0000000..b574360
--- /dev/null
+++ b/frontend/src/components/Dashboard.js
@@ -0,0 +1,328 @@
+import React, { useState, useEffect } from 'react'
+import axios from 'axios'
+
+const Dashboard = ({ stats }) => {
+  const [dashboardData, setDashboardData] = useState({
+    userActivity: {},
+    conversations: {},
+    alerts: {},
+    system: {}
+  })
+  const [loading, setLoading] = useState(true)
+  const [error, setError] = useState(null)
+  const [refreshInterval, setRefreshInterval] = useState(null)
+
+  useEffect(() => {
+    fetchDashboardData()
+    
+    // Set up auto-refresh every 30 seconds
+    const interval = setInterval(fetchDashboardData, 30000)
+    setRefreshInterval(interval)
+    
+    return () => {
+      if (interval) clearInterval(interval)
+    }
+  }, [])
+
+  const fetchDashboardData = async () => {
+    try {
+      setError(null)
+      const response = await axios.get('/api/monitoring/dashboard')
+      
+      if (response.data.success) {
+        setDashboardData(response.data)
+      }
+    } catch (error) {
+      console.error('Failed to fetch dashboard data:', error)
+      setError('Failed to load dashboard data')
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  const formatUptime = (seconds) => {
+    const days = Math.floor(seconds / 86400)
+    const hours = Math.floor((seconds % 86400) / 3600)
+    const minutes = Math.floor((seconds % 3600) / 60)
+    
+    if (days > 0) return `${days}d ${hours}h ${minutes}m`
+    if (hours > 0) return `${hours}h ${minutes}m`
+    return `${minutes}m`
+  }
+
+  const getSystemHealthColor = (health) => {
+    switch (health) {
+      case 'healthy': return '#10b981'
+      case 'warning': return '#f59e0b'
+      case 'critical': return '#ef4444'
+      default: return '#6b7280'
+    }
+  }
+
+  const getSystemHealthText = (memUsage, cpuUsage) => {
+    if (memUsage > 90 || cpuUsage > 90) return 'Critical'
+    if (memUsage > 70 || cpuUsage > 70) return 'Warning'
+    return 'Healthy'
+  }
+
+  if (loading) {
+    return (
+      <div style={{ 
+        display: 'flex', 
+        justifyContent: 'center', 
+        alignItems: 'center', 
+        height: '400px' 
+      }}>
+        <div className="loading-spinner"></div>
+        <span style={{ marginLeft: '12px' }}>Loading dashboard...</span>
+      </div>
+    )
+  }
+
+  if (error) {
+    return (
+      <div className="card">
+        <div className="card-content">
+          <div style={{ 
+            color: '#ef4444', 
+            textAlign: 'center', 
+            padding: '40px' 
+          }}>
+            <h3>⚠️ {error}</h3>
+            <button 
+              onClick={fetchDashboardData}
+              className="btn btn-primary"
+              style={{ marginTop: '16px' }}
+            >
+              Retry
+            </button>
+          </div>
+        </div>
+      </div>
+    )
+  }
+
+  const { userActivity, conversations, alerts, system } = dashboardData
+  const systemHealth = getSystemHealthText(system.memoryUsage, system.cpuUsage)
+
+  return (
+    <div>
+      <div style={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        marginBottom: '24px'
+      }}>
+        <h2 style={{ fontSize: '24px', fontWeight: '600', color: '#1e293b' }}>
+          Dashboard Overview
+        </h2>
+        <div style={{ fontSize: '14px', color: '#64748b' }}>
+          Last updated: {dashboardData.timestamp ? new Date(dashboardData.timestamp).toLocaleTimeString() : 'Unknown'}
+        </div>
+      </div>
+
+      {/* Key Metrics */}
+      <div className="stat-cards">
+        <div className="stat-card">
+          <div className="stat-label">Active Users</div>
+          <div className="stat-value-large" style={{ color: '#3b82f6' }}>
+            {userActivity.activeUsers || 0}
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            {userActivity.activeSessions || 0} active sessions
+          </div>
+        </div>
+
+        <div className="stat-card">
+          <div className="stat-label">Conversations Today</div>
+          <div className="stat-value-large" style={{ color: '#10b981' }}>
+            {conversations.today || 0}
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            {conversations.lastHour || 0} in last hour
+          </div>
+        </div>
+
+        <div className={`stat-card ${alerts.critical > 0 ? 'critical' : alerts.today > 0 ? 'warning' : 'success'}`}>
+          <div className="stat-label">Security Alerts</div>
+          <div className="stat-value-large" style={{ 
+            color: alerts.critical > 0 ? '#ef4444' : alerts.today > 0 ? '#f59e0b' : '#10b981'
+          }}>
+            {alerts.today || 0}
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            {alerts.critical || 0} critical alerts
+          </div>
+        </div>
+
+        <div className={`stat-card ${systemHealth === 'Critical' ? 'critical' : systemHealth === 'Warning' ? 'warning' : 'success'}`}>
+          <div className="stat-label">System Health</div>
+          <div className="stat-value-large" style={{ 
+            color: getSystemHealthColor(systemHealth.toLowerCase())
+          }}>
+            {systemHealth}
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            {system.memoryUsage || 0}% memory, {system.cpuUsage || 0}% CPU
+          </div>
+        </div>
+      </div>
+
+      {/* Detailed Cards */}
+      <div className="dashboard-grid">
+        {/* User Activity */}
+        <div className="card">
+          <div className="card-header">
+            <h3 className="card-title">User Activity</h3>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '12px' }}>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Total Users:</span>
+                <strong>{userActivity.totalUsers || 0}</strong>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>New Users Today:</span>
+                <strong style={{ color: '#10b981' }}>{userActivity.newUsersToday || 0}</strong>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Blocked Users:</span>
+                <strong style={{ color: '#ef4444' }}>{userActivity.blockedUsers || 0}</strong>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Active Sessions:</span>
+                <strong style={{ color: '#3b82f6' }}>{userActivity.activeSessions || 0}</strong>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {/* System Status */}
+        <div className="card">
+          <div className="card-header">
+            <h3 className="card-title">System Status</h3>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '12px' }}>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Memory Usage:</span>
+                <strong style={{ 
+                  color: system.memoryUsage > 80 ? '#ef4444' : system.memoryUsage > 60 ? '#f59e0b' : '#10b981'
+                }}>
+                  {system.memoryUsage || 0}%
+                </strong>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>CPU Usage:</span>
+                <strong style={{ 
+                  color: system.cpuUsage > 80 ? '#ef4444' : system.cpuUsage > 60 ? '#f59e0b' : '#10b981'
+                }}>
+                  {system.cpuUsage || 0}%
+                </strong>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>System Uptime:</span>
+                <strong>{system.uptime ? formatUptime(system.uptime) : 'Unknown'}</strong>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Load Average:</span>
+                <strong>{system.loadAverage ? system.loadAverage.toFixed(2) : '0.00'}</strong>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Process Memory:</span>
+                <strong>{system.processMemoryMB || 0} MB</strong>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {/* Recent Alerts Preview */}
+        <div className="card">
+          <div className="card-header">
+            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+              <h3 className="card-title">Security Overview</h3>
+              <a href="/alerts" className="btn btn-small btn-secondary">View All</a>
+            </div>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '12px' }}>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Critical Alerts:</span>
+                <span className="badge badge-critical">{alerts.critical || 0}</span>
+              </div>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Total Today:</span>
+                <span className="badge badge-medium">{alerts.today || 0}</span>
+              </div>
+              <div style={{ fontSize: '12px', color: '#64748b', textAlign: 'center', marginTop: '8px' }}>
+                {alerts.today === 0 ? '✅ All clear!' : '⚠️ Active monitoring in progress'}
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {/* Quick Actions */}
+        <div className="card">
+          <div className="card-header">
+            <h3 className="card-title">Quick Actions</h3>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '12px' }}>
+              <a href="/alerts" className="btn btn-primary" style={{ textDecoration: 'none' }}>
+                🚨 View All Alerts
+              </a>
+              <a href="/users" className="btn btn-secondary" style={{ textDecoration: 'none' }}>
+                👥 Manage Users
+              </a>
+              <a href="/monitoring" className="btn btn-secondary" style={{ textDecoration: 'none' }}>
+                📊 Live Monitoring
+              </a>
+              <a href="/reports" className="btn btn-secondary" style={{ textDecoration: 'none' }}>
+                📋 Generate Reports
+              </a>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Status Banner */}
+      <div style={{
+        marginTop: '24px',
+        padding: '16px 24px',
+        borderRadius: '8px',
+        background: systemHealth === 'Healthy' ? '#f0f9ff' : 
+                   systemHealth === 'Warning' ? '#fffbeb' : '#fef2f2',
+        border: `1px solid ${
+          systemHealth === 'Healthy' ? '#3b82f6' : 
+          systemHealth === 'Warning' ? '#f59e0b' : '#ef4444'
+        }`,
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center'
+      }}>
+        <div>
+          <strong style={{
+            color: systemHealth === 'Healthy' ? '#1d4ed8' : 
+                   systemHealth === 'Warning' ? '#d97706' : '#dc2626'
+          }}>
+            {systemHealth === 'Healthy' ? '✅' : 
+             systemHealth === 'Warning' ? '⚠️' : '🚨'} System Status: {systemHealth}
+          </strong>
+          <div style={{ fontSize: '14px', color: '#64748b', marginTop: '4px' }}>
+            Dear Bubbe AI is {systemHealth === 'Healthy' ? 'operating normally' : 
+                              systemHealth === 'Warning' ? 'experiencing minor issues' : 
+                              'experiencing critical issues'}
+          </div>
+        </div>
+        <button 
+          onClick={fetchDashboardData}
+          className="btn btn-small btn-secondary"
+        >
+          Refresh
+        </button>
+      </div>
+    </div>
+  )
+}
+
+export default Dashboard
\ No newline at end of file
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
new file mode 100644
index 0000000..0e7ce7f
--- /dev/null
+++ b/frontend/src/components/Header.js
@@ -0,0 +1,57 @@
+import React from 'react'
+
+const Header = ({ user, stats, onLogout }) => {
+  const getHealthIcon = (stats) => {
+    const memUsage = stats.system?.memoryUsage || 0
+    const cpuUsage = stats.system?.cpuUsage || 0
+    
+    if (memUsage > 90 || cpuUsage > 90) return '🔴'
+    if (memUsage > 70 || cpuUsage > 70) return '🟡'
+    return '🟢'
+  }
+
+  return (
+    <div className="header">
+      <div className="header-left">
+        <h1>Admin Dashboard</h1>
+        <div className="header-stats">
+          <div className="stat-item">
+            <span className="stat-value">{stats.userActivity?.activeUsers || 0}</span> active users
+          </div>
+          <div className="stat-item">
+            <span className="stat-value">{stats.alerts?.today || 0}</span> alerts today
+          </div>
+          <div className="stat-item">
+            {getHealthIcon(stats)} system status
+          </div>
+          <div className="stat-item">
+            <span className="stat-value">{stats.conversations?.today || 0}</span> conversations
+          </div>
+        </div>
+      </div>
+      
+      <div className="header-right">
+        <div className="user-info">
+          <div className="user-avatar">
+            {user?.username?.charAt(0)?.toUpperCase() || 'A'}
+          </div>
+          <div>
+            <div className="user-name">{user?.username || 'Admin'}</div>
+            <div style={{ fontSize: '12px', color: '#64748b' }}>
+              {user?.role || 'Administrator'}
+            </div>
+          </div>
+        </div>
+        
+        <button 
+          onClick={onLogout}
+          className="logout-btn"
+        >
+          Logout
+        </button>
+      </div>
+    </div>
+  )
+}
+
+export default Header
\ No newline at end of file
diff --git a/frontend/src/components/LiveAlertModal.js b/frontend/src/components/LiveAlertModal.js
new file mode 100644
index 0000000..44c766c
--- /dev/null
+++ b/frontend/src/components/LiveAlertModal.js
@@ -0,0 +1,241 @@
+import React from 'react'
+
+const LiveAlertModal = ({ alert, onClose }) => {
+  if (!alert) return null
+
+  const getSeverityColor = (severity) => {
+    const colors = {
+      critical: '#dc2626',
+      high: '#d97706',
+      medium: '#2563eb',
+      low: '#6b7280'
+    }
+    return colors[severity] || '#6b7280'
+  }
+
+  const getTypeIcon = (type) => {
+    const icons = {
+      antisemitism: '✡️🚨',
+      racism: '⚠️🚨',
+      harassment: '🚫🚨',
+      spam: '📧🚨',
+      system: '⚙️🚨'
+    }
+    return icons[type] || '❗🚨'
+  }
+
+  const formatLocation = (location) => {
+    if (!location) return 'Unknown Location'
+    
+    const parts = []
+    if (location.city) parts.push(location.city)
+    if (location.state) parts.push(location.state)
+    if (location.country) parts.push(location.country)
+    
+    return parts.length > 0 ? parts.join(', ') : 'Unknown Location'
+  }
+
+  return (
+    <div className="live-alert-modal">
+      <div className="live-alert-content" style={{
+        border: `3px solid ${getSeverityColor(alert.severity)}`,
+        animation: 'pulse 1s infinite'
+      }}>
+        <div className="alert-header">
+          <h2 style={{
+            color: getSeverityColor(alert.severity),
+            margin: 0,
+            fontSize: '24px',
+            fontWeight: '700'
+          }}>
+            {getTypeIcon(alert.type)} LIVE ALERT
+          </h2>
+          <button 
+            className="alert-close"
+            onClick={onClose}
+            style={{ fontSize: '28px' }}
+          >
+            ×
+          </button>
+        </div>
+
+        <div style={{
+          background: '#fef2f2',
+          padding: '16px',
+          borderRadius: '8px',
+          border: '1px solid #fecaca',
+          marginBottom: '20px'
+        }}>
+          <div style={{
+            fontSize: '18px',
+            fontWeight: '600',
+            color: '#dc2626',
+            marginBottom: '8px',
+            textTransform: 'uppercase',
+            letterSpacing: '1px'
+          }}>
+            {alert.severity} {alert.type} DETECTED
+          </div>
+          <div style={{ fontSize: '14px', color: '#7f1d1d' }}>
+            Immediate attention required • Real-time detection
+          </div>
+        </div>
+
+        <div className="alert-details">
+          <div className="alert-detail-row">
+            <span className="alert-detail-label">🕒 Time:</span>
+            <span className="alert-detail-value">
+              {new Date(alert.timestamp).toLocaleString()}
+            </span>
+          </div>
+          
+          <div className="alert-detail-row">
+            <span className="alert-detail-label">👤 User ID:</span>
+            <span className="alert-detail-value" style={{ fontFamily: 'monospace' }}>
+              {alert.userInfo?.userId || 'Unknown'}
+            </span>
+          </div>
+          
+          <div className="alert-detail-row">
+            <span className="alert-detail-label">🌐 IP Address:</span>
+            <span className="alert-detail-value" style={{ fontFamily: 'monospace' }}>
+              {alert.userInfo?.ip || 'Unknown'}
+            </span>
+          </div>
+          
+          <div className="alert-detail-row">
+            <span className="alert-detail-label">📍 Location:</span>
+            <span className="alert-detail-value">
+              {formatLocation(alert.location)}
+            </span>
+          </div>
+
+          <div className="alert-detail-row">
+            <span className="alert-detail-label">💬 Message:</span>
+            <div style={{
+              background: '#f8fafc',
+              border: '1px solid #e2e8f0',
+              borderRadius: '6px',
+              padding: '12px',
+              marginTop: '8px',
+              fontFamily: 'monospace',
+              fontSize: '14px',
+              wordBreak: 'break-word'
+            }}>
+              {alert.message || alert.content?.message || 'No message available'}
+            </div>
+          </div>
+
+          {alert.violation?.match && (
+            <div className="alert-detail-row">
+              <span className="alert-detail-label">🎯 Violation:</span>
+              <div style={{
+                background: '#fef2f2',
+                border: '1px solid #fecaca',
+                borderRadius: '6px',
+                padding: '12px',
+                marginTop: '8px',
+                color: '#dc2626',
+                fontWeight: '600',
+                fontFamily: 'monospace'
+              }}>
+                "{alert.violation.match}"
+              </div>
+            </div>
+          )}
+
+          {alert.violation?.category && (
+            <div className="alert-detail-row">
+              <span className="alert-detail-label">🏷️ Category:</span>
+              <span className="alert-detail-value" style={{
+                background: getSeverityColor(alert.severity) + '20',
+                color: getSeverityColor(alert.severity),
+                padding: '4px 8px',
+                borderRadius: '4px',
+                fontSize: '12px',
+                fontWeight: '600',
+                textTransform: 'uppercase'
+              }}>
+                {alert.violation.category}
+              </span>
+            </div>
+          )}
+        </div>
+
+        <div style={{
+          background: '#fffbeb',
+          border: '1px solid #fed7aa',
+          borderRadius: '8px',
+          padding: '16px',
+          marginBottom: '20px'
+        }}>
+          <div style={{
+            fontSize: '14px',
+            fontWeight: '600',
+            color: '#92400e',
+            marginBottom: '8px'
+          }}>
+            ⚡ Automated Actions
+          </div>
+          <ul style={{
+            margin: 0,
+            paddingLeft: '20px',
+            color: '#78350f',
+            fontSize: '13px'
+          }}>
+            <li>User session flagged for review</li>
+            <li>Alert logged in security database</li>
+            {alert.severity === 'critical' && (
+              <li style={{ fontWeight: '600', color: '#dc2626' }}>
+                Threat response message sent to user
+              </li>
+            )}
+            <li>Real-time notification sent to admin dashboard</li>
+          </ul>
+        </div>
+
+        <div className="alert-actions">
+          <button
+            onClick={onClose}
+            className="btn btn-secondary"
+          >
+            Acknowledge
+          </button>
+          <button
+            onClick={() => {
+              window.open('/alerts', '_blank')
+              onClose()
+            }}
+            className="btn btn-primary"
+          >
+            View All Alerts
+          </button>
+          {alert.userInfo?.userId && (
+            <button
+              onClick={() => {
+                window.open(`/users?search=${alert.userInfo.userId}`, '_blank')
+                onClose()
+              }}
+              className="btn btn-danger"
+            >
+              View User
+            </button>
+          )}
+        </div>
+
+        <div style={{
+          marginTop: '16px',
+          fontSize: '12px',
+          color: '#6b7280',
+          textAlign: 'center',
+          fontStyle: 'italic'
+        }}>
+          Alert ID: {alert.id || alert._id} • 
+          Detection confidence: {(alert.violation?.confidence * 100) || 100}%
+        </div>
+      </div>
+    </div>
+  )
+}
+
+export default LiveAlertModal
\ No newline at end of file
diff --git a/frontend/src/components/Login.js b/frontend/src/components/Login.js
new file mode 100644
index 0000000..99db8a4
--- /dev/null
+++ b/frontend/src/components/Login.js
@@ -0,0 +1,205 @@
+import React, { useState } from 'react'
+
+const Login = ({ onLogin, error }) => {
+  const [credentials, setCredentials] = useState({
+    username: '',
+    password: ''
+  })
+  const [loading, setLoading] = useState(false)
+  const [localError, setLocalError] = useState('')
+
+  const handleSubmit = async (e) => {
+    e.preventDefault()
+    
+    if (!credentials.username || !credentials.password) {
+      setLocalError('Please enter both username and password')
+      return
+    }
+
+    setLoading(true)
+    setLocalError('')
+
+    try {
+      const result = await onLogin(credentials)
+      
+      if (!result.success) {
+        setLocalError(result.error || 'Login failed')
+      }
+    } catch (err) {
+      setLocalError('Login failed. Please try again.')
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  const handleChange = (e) => {
+    setCredentials({
+      ...credentials,
+      [e.target.name]: e.target.value
+    })
+    // Clear errors when user starts typing
+    if (localError) setLocalError('')
+    if (error && e.target.value) setLocalError('')
+  }
+
+  return (
+    <div style={{
+      minHeight: '100vh',
+      display: 'flex',
+      alignItems: 'center',
+      justifyContent: 'center',
+      background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+      padding: '20px'
+    }}>
+      <div style={{
+        background: 'white',
+        padding: '40px',
+        borderRadius: '12px',
+        boxShadow: '0 20px 40px rgba(0, 0, 0, 0.1)',
+        width: '100%',
+        maxWidth: '400px'
+      }}>
+        <div style={{ textAlign: 'center', marginBottom: '32px' }}>
+          <h1 style={{ 
+            fontSize: '28px', 
+            fontWeight: '700', 
+            color: '#1e293b', 
+            margin: '0 0 8px 0' 
+          }}>
+            Dear Bubbe Admin
+          </h1>
+          <p style={{ 
+            color: '#64748b', 
+            fontSize: '16px', 
+            margin: 0 
+          }}>
+            Real-time Monitoring & Alert System
+          </p>
+        </div>
+
+        <form onSubmit={handleSubmit}>
+          <div style={{ marginBottom: '20px' }}>
+            <label style={{
+              display: 'block',
+              marginBottom: '6px',
+              fontWeight: '500',
+              color: '#374151'
+            }}>
+              Username
+            </label>
+            <input
+              type="text"
+              name="username"
+              value={credentials.username}
+              onChange={handleChange}
+              style={{
+                width: '100%',
+                padding: '12px 16px',
+                border: '1px solid #d1d5db',
+                borderRadius: '6px',
+                fontSize: '16px',
+                boxSizing: 'border-box'
+              }}
+              placeholder="Enter your username"
+              disabled={loading}
+            />
+          </div>
+
+          <div style={{ marginBottom: '24px' }}>
+            <label style={{
+              display: 'block',
+              marginBottom: '6px',
+              fontWeight: '500',
+              color: '#374151'
+            }}>
+              Password
+            </label>
+            <input
+              type="password"
+              name="password"
+              value={credentials.password}
+              onChange={handleChange}
+              style={{
+                width: '100%',
+                padding: '12px 16px',
+                border: '1px solid #d1d5db',
+                borderRadius: '6px',
+                fontSize: '16px',
+                boxSizing: 'border-box'
+              }}
+              placeholder="Enter your password"
+              disabled={loading}
+            />
+          </div>
+
+          {(localError || error) && (
+            <div style={{
+              background: '#fef2f2',
+              color: '#dc2626',
+              padding: '12px 16px',
+              borderRadius: '6px',
+              marginBottom: '20px',
+              fontSize: '14px',
+              border: '1px solid #fecaca'
+            }}>
+              {localError || error}
+            </div>
+          )}
+
+          <button
+            type="submit"
+            disabled={loading}
+            style={{
+              width: '100%',
+              padding: '12px 16px',
+              background: loading ? '#9ca3af' : '#3b82f6',
+              color: 'white',
+              border: 'none',
+              borderRadius: '6px',
+              fontSize: '16px',
+              fontWeight: '500',
+              cursor: loading ? 'not-allowed' : 'pointer',
+              transition: 'background-color 0.2s',
+              display: 'flex',
+              alignItems: 'center',
+              justifyContent: 'center'
+            }}
+          >
+            {loading ? (
+              <>
+                <div className="loading-spinner" style={{ marginRight: '8px' }}></div>
+                Signing in...
+              </>
+            ) : (
+              'Sign In'
+            )}
+          </button>
+        </form>
+
+        <div style={{
+          marginTop: '24px',
+          padding: '16px',
+          background: '#f8fafc',
+          borderRadius: '6px',
+          fontSize: '14px',
+          color: '#64748b'
+        }}>
+          <strong>Demo Credentials:</strong><br />
+          Username: admin<br />
+          Password: password
+        </div>
+
+        <div style={{
+          marginTop: '24px',
+          textAlign: 'center',
+          fontSize: '12px',
+          color: '#9ca3af'
+        }}>
+          🔒 Secure access to monitor Dear Bubbe AI system
+        </div>
+      </div>
+    </div>
+  )
+}
+
+export default Login
\ No newline at end of file
diff --git a/frontend/src/components/MonitoringPage.js b/frontend/src/components/MonitoringPage.js
new file mode 100644
index 0000000..b32aa03
--- /dev/null
+++ b/frontend/src/components/MonitoringPage.js
@@ -0,0 +1,380 @@
+import React, { useState, useEffect } from 'react'
+import axios from 'axios'
+
+const MonitoringPage = () => {
+  const [liveData, setLiveData] = useState({})
+  const [bubbeHealth, setBubbeHealth] = useState({})
+  const [loading, setLoading] = useState(true)
+  const [autoRefresh, setAutoRefresh] = useState(true)
+
+  useEffect(() => {
+    fetchLiveData()
+    checkBubbeHealth()
+
+    let interval
+    if (autoRefresh) {
+      interval = setInterval(() => {
+        fetchLiveData()
+        checkBubbeHealth()
+      }, 10000) // Refresh every 10 seconds
+    }
+
+    return () => {
+      if (interval) clearInterval(interval)
+    }
+  }, [autoRefresh])
+
+  const fetchLiveData = async () => {
+    try {
+      const response = await axios.get('/api/monitoring/dashboard')
+      setLiveData(response.data)
+      setLoading(false)
+    } catch (error) {
+      console.error('Failed to fetch live data:', error)
+    }
+  }
+
+  const checkBubbeHealth = async () => {
+    try {
+      const response = await axios.get('/api/monitoring/bubbe-health')
+      setBubbeHealth(response.data)
+    } catch (error) {
+      console.error('Failed to check Bubbe health:', error)
+    }
+  }
+
+  if (loading) {
+    return (
+      <div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
+        <div className="loading-spinner"></div>
+        <span style={{ marginLeft: '12px' }}>Loading monitoring data...</span>
+      </div>
+    )
+  }
+
+  return (
+    <div>
+      <div style={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        marginBottom: '24px'
+      }}>
+        <h2>Live Monitoring</h2>
+        <div style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
+          <label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
+            <input
+              type="checkbox"
+              checked={autoRefresh}
+              onChange={(e) => setAutoRefresh(e.target.checked)}
+            />
+            Auto-refresh (10s)
+          </label>
+          <button onClick={() => { fetchLiveData(); checkBubbeHealth(); }} className="btn btn-primary">
+            Refresh Now
+          </button>
+        </div>
+      </div>
+
+      {/* Health Status Banner */}
+      <div className={`card ${bubbeHealth.overall === 'healthy' ? 'success' : bubbeHealth.overall === 'degraded' ? 'warning' : 'critical'}`} style={{ marginBottom: '24px' }}>
+        <div className="card-content">
+          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+            <div>
+              <h3 style={{ margin: 0, fontSize: '18px' }}>
+                {bubbeHealth.overall === 'healthy' ? '✅' : bubbeHealth.overall === 'degraded' ? '⚠️' : '🚨'} 
+                Dear Bubbe API Status: {bubbeHealth.overall || 'Unknown'}
+              </h3>
+              <div style={{ fontSize: '14px', marginTop: '4px', opacity: 0.8 }}>
+                {bubbeHealth.api?.responseTime && `Response time: ${bubbeHealth.api.responseTime}ms`}
+                {bubbeHealth.pm2?.status && ` • PM2 Status: ${bubbeHealth.pm2.status}`}
+              </div>
+            </div>
+            <div style={{ fontSize: '12px', opacity: 0.7 }}>
+              Last checked: {bubbeHealth.timestamp ? new Date(bubbeHealth.timestamp).toLocaleTimeString() : 'Unknown'}
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Real-time Stats */}
+      <div className="stat-cards" style={{ marginBottom: '24px' }}>
+        <div className="stat-card">
+          <div className="stat-label">Active Users (5 min)</div>
+          <div className="stat-value-large" style={{ color: '#3b82f6' }}>
+            {liveData.userActivity?.activeUsers || 0}
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            {liveData.userActivity?.activeSessions || 0} sessions
+          </div>
+        </div>
+
+        <div className="stat-card">
+          <div className="stat-label">Conversations/Hour</div>
+          <div className="stat-value-large" style={{ color: '#10b981' }}>
+            {liveData.conversations?.lastHour || 0}
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            {liveData.conversations?.today || 0} today
+          </div>
+        </div>
+
+        <div className="stat-card critical">
+          <div className="stat-label">Active Alerts</div>
+          <div className="stat-value-large" style={{ color: '#ef4444' }}>
+            {liveData.alerts?.today || 0}
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            {liveData.alerts?.critical || 0} critical
+          </div>
+        </div>
+
+        <div className="stat-card">
+          <div className="stat-label">System Load</div>
+          <div className="stat-value-large" style={{ 
+            color: liveData.system?.cpuUsage > 80 ? '#ef4444' : 
+                   liveData.system?.cpuUsage > 60 ? '#f59e0b' : '#10b981'
+          }}>
+            {liveData.system?.cpuUsage || 0}%
+          </div>
+          <div style={{ fontSize: '12px', color: '#64748b', marginTop: '4px' }}>
+            CPU usage
+          </div>
+        </div>
+      </div>
+
+      {/* Detailed Monitoring */}
+      <div className="dashboard-grid">
+        {/* System Resources */}
+        <div className="card">
+          <div className="card-header">
+            <h3 className="card-title">System Resources</h3>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '16px' }}>
+              <div>
+                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
+                  <span>Memory Usage</span>
+                  <span style={{ fontWeight: '600' }}>{liveData.system?.memoryUsage || 0}%</span>
+                </div>
+                <div style={{ 
+                  background: '#e2e8f0', 
+                  height: '8px', 
+                  borderRadius: '4px',
+                  overflow: 'hidden'
+                }}>
+                  <div style={{
+                    width: `${liveData.system?.memoryUsage || 0}%`,
+                    height: '100%',
+                    background: liveData.system?.memoryUsage > 80 ? '#ef4444' : 
+                               liveData.system?.memoryUsage > 60 ? '#f59e0b' : '#10b981',
+                    transition: 'width 0.3s ease'
+                  }}></div>
+                </div>
+              </div>
+
+              <div>
+                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '8px' }}>
+                  <span>CPU Usage</span>
+                  <span style={{ fontWeight: '600' }}>{liveData.system?.cpuUsage || 0}%</span>
+                </div>
+                <div style={{ 
+                  background: '#e2e8f0', 
+                  height: '8px', 
+                  borderRadius: '4px',
+                  overflow: 'hidden'
+                }}>
+                  <div style={{
+                    width: `${liveData.system?.cpuUsage || 0}%`,
+                    height: '100%',
+                    background: liveData.system?.cpuUsage > 80 ? '#ef4444' : 
+                               liveData.system?.cpuUsage > 60 ? '#f59e0b' : '#10b981',
+                    transition: 'width 0.3s ease'
+                  }}></div>
+                </div>
+              </div>
+
+              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', marginTop: '16px' }}>
+                <div style={{ textAlign: 'center' }}>
+                  <div style={{ fontSize: '20px', fontWeight: '600', color: '#1e293b' }}>
+                    {liveData.system?.processMemoryMB || 0}
+                  </div>
+                  <div style={{ fontSize: '12px', color: '#64748b' }}>Process Memory (MB)</div>
+                </div>
+                <div style={{ textAlign: 'center' }}>
+                  <div style={{ fontSize: '20px', fontWeight: '600', color: '#1e293b' }}>
+                    {liveData.system?.loadAverage?.toFixed(2) || '0.00'}
+                  </div>
+                  <div style={{ fontSize: '12px', color: '#64748b' }}>Load Average</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {/* API Health */}
+        <div className="card">
+          <div className="card-header">
+            <h3 className="card-title">API Health Check</h3>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '12px' }}>
+              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                <span>API Status:</span>
+                <span style={{
+                  padding: '4px 8px',
+                  borderRadius: '4px',
+                  fontSize: '12px',
+                  fontWeight: '600',
+                  background: bubbeHealth.api?.status === 'healthy' ? '#dcfce7' : '#fee2e2',
+                  color: bubbeHealth.api?.status === 'healthy' ? '#16a34a' : '#dc2626'
+                }}>
+                  {bubbeHealth.api?.status || 'Unknown'}
+                </span>
+              </div>
+
+              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                <span>Response Time:</span>
+                <span style={{ fontWeight: '600' }}>
+                  {bubbeHealth.api?.responseTime || 0}ms
+                </span>
+              </div>
+
+              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+                <span>PM2 Status:</span>
+                <span style={{
+                  padding: '4px 8px',
+                  borderRadius: '4px',
+                  fontSize: '12px',
+                  fontWeight: '600',
+                  background: bubbeHealth.pm2?.status === 'online' ? '#dcfce7' : '#fee2e2',
+                  color: bubbeHealth.pm2?.status === 'online' ? '#16a34a' : '#dc2626'
+                }}>
+                  {bubbeHealth.pm2?.status || 'Unknown'}
+                </span>
+              </div>
+
+              {bubbeHealth.api?.data && (
+                <div style={{ 
+                  marginTop: '16px',
+                  padding: '12px',
+                  background: '#f8fafc',
+                  borderRadius: '6px',
+                  fontSize: '12px'
+                }}>
+                  <strong>API Info:</strong><br />
+                  Uptime: {bubbeHealth.api.data.uptime ? (bubbeHealth.api.data.uptime / 3600).toFixed(1) + 'h' : 'Unknown'}<br />
+                  Memory: {bubbeHealth.api.data.memory ? Math.round(bubbeHealth.api.data.memory.rss / 1024 / 1024) + 'MB' : 'Unknown'}
+                </div>
+              )}
+            </div>
+          </div>
+        </div>
+
+        {/* Live Activity */}
+        <div className="card">
+          <div className="card-header">
+            <h3 className="card-title">Live Activity</h3>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '12px' }}>
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>New Users Today:</span>
+                <strong style={{ color: '#10b981' }}>{liveData.userActivity?.newUsersToday || 0}</strong>
+              </div>
+              
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Blocked Users:</span>
+                <strong style={{ color: '#ef4444' }}>{liveData.userActivity?.blockedUsers || 0}</strong>
+              </div>
+
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Total Users:</span>
+                <strong>{liveData.userActivity?.totalUsers || 0}</strong>
+              </div>
+
+              <hr style={{ margin: '12px 0', border: 'none', borderTop: '1px solid #e2e8f0' }} />
+
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Conversations Today:</span>
+                <strong style={{ color: '#3b82f6' }}>{liveData.conversations?.today || 0}</strong>
+              </div>
+
+              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>Last Hour:</span>
+                <strong>{liveData.conversations?.lastHour || 0}</strong>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {/* Quick Actions */}
+        <div className="card">
+          <div className="card-header">
+            <h3 className="card-title">Quick Actions</h3>
+          </div>
+          <div className="card-content">
+            <div style={{ display: 'grid', gap: '12px' }}>
+              <button 
+                onClick={() => window.open('/alerts', '_blank')}
+                className="btn btn-danger btn-small"
+              >
+                🚨 View Critical Alerts
+              </button>
+              
+              <button 
+                onClick={() => window.open('/users?blocked=true', '_blank')}
+                className="btn btn-secondary btn-small"
+              >
+                🚫 View Blocked Users
+              </button>
+              
+              <button 
+                onClick={() => {
+                  fetchLiveData()
+                  checkBubbeHealth()
+                }}
+                className="btn btn-primary btn-small"
+              >
+                🔄 Force Refresh
+              </button>
+              
+              <button 
+                onClick={() => window.open('http://45.61.58.125:3011', '_blank')}
+                className="btn btn-secondary btn-small"
+              >
+                🌐 Open Dear Bubbe
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Status Footer */}
+      <div style={{
+        marginTop: '24px',
+        padding: '16px',
+        background: '#f8fafc',
+        borderRadius: '8px',
+        border: '1px solid #e2e8f0',
+        textAlign: 'center',
+        fontSize: '14px',
+        color: '#64748b'
+      }}>
+        {autoRefresh && (
+          <div>
+            🔄 Auto-refreshing every 10 seconds • 
+            Last update: {liveData.timestamp ? new Date(liveData.timestamp).toLocaleTimeString() : 'Unknown'}
+          </div>
+        )}
+        
+        <div style={{ marginTop: '8px' }}>
+          Monitoring {liveData.userActivity?.activeUsers || 0} active users • 
+          System uptime: {liveData.system?.uptime ? Math.floor(liveData.system.uptime / 3600) + 'h' : 'Unknown'}
+        </div>
+      </div>
+    </div>
+  )
+}
+
+export default MonitoringPage
\ No newline at end of file
diff --git a/frontend/src/components/ReportsPage.js b/frontend/src/components/ReportsPage.js
new file mode 100644
index 0000000..a226ff7
--- /dev/null
+++ b/frontend/src/components/ReportsPage.js
@@ -0,0 +1,406 @@
+import React, { useState, useEffect } from 'react'
+import axios from 'axios'
+
+const ReportsPage = () => {
+  const [reports, setReports] = useState([])
+  const [loading, setLoading] = useState(false)
+  const [selectedReport, setSelectedReport] = useState(null)
+  const [reportData, setReportData] = useState(null)
+
+  const generateReport = async (type, timeframe = '24h') => {
+    try {
+      setLoading(true)
+      let endpoint = `/api/reports/${type}`
+      if (timeframe) endpoint += `?timeframe=${timeframe}`
+      
+      const response = await axios.get(endpoint)
+      
+      if (response.data.success) {
+        setReportData(response.data.report)
+        setSelectedReport(type)
+      }
+    } catch (error) {
+      console.error(`Failed to generate ${type} report:`, error)
+      alert(`Failed to generate ${type} report`)
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  const exportReport = async () => {
+    try {
+      await axios.post('/api/reports/export', {
+        reportType: selectedReport,
+        data: reportData
+      })
+      alert('Report exported successfully')
+    } catch (error) {
+      console.error('Failed to export report:', error)
+      alert('Failed to export report')
+    }
+  }
+
+  if (loading) {
+    return (
+      <div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
+        <div className="loading-spinner"></div>
+        <span style={{ marginLeft: '12px' }}>Generating report...</span>
+      </div>
+    )
+  }
+
+  return (
+    <div>
+      <div style={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        marginBottom: '24px'
+      }}>
+        <h2>Reports & Analytics</h2>
+        {selectedReport && reportData && (
+          <button onClick={exportReport} className="btn btn-primary">
+            Export Report
+          </button>
+        )}
+      </div>
+
+      {!selectedReport ? (
+        <div className="dashboard-grid">
+          <div className="card">
+            <div className="card-header">
+              <h3 className="card-title">Daily Report</h3>
+            </div>
+            <div className="card-content">
+              <p>Comprehensive daily activity summary including user engagement, conversations, and security events.</p>
+              <div style={{ marginTop: '16px' }}>
+                <button 
+                  onClick={() => generateReport('daily')}
+                  className="btn btn-primary"
+                  style={{ marginRight: '8px' }}
+                >
+                  Generate Today
+                </button>
+                <button 
+                  onClick={() => {
+                    const date = prompt('Enter date (YYYY-MM-DD):')
+                    if (date) generateReport('daily', date)
+                  }}
+                  className="btn btn-secondary"
+                >
+                  Custom Date
+                </button>
+              </div>
+            </div>
+          </div>
+
+          <div className="card">
+            <div className="card-header">
+              <h3 className="card-title">Security Report</h3>
+            </div>
+            <div className="card-content">
+              <p>Security incidents, threat analysis, and user behavior monitoring.</p>
+              <div style={{ marginTop: '16px', display: 'grid', gap: '8px' }}>
+                <button 
+                  onClick={() => generateReport('security', '24h')}
+                  className="btn btn-danger"
+                >
+                  Last 24 Hours
+                </button>
+                <button 
+                  onClick={() => generateReport('security', '7d')}
+                  className="btn btn-danger"
+                >
+                  Last 7 Days
+                </button>
+                <button 
+                  onClick={() => generateReport('security', '30d')}
+                  className="btn btn-danger"
+                >
+                  Last 30 Days
+                </button>
+              </div>
+            </div>
+          </div>
+
+          <div className="card">
+            <div className="card-header">
+              <h3 className="card-title">Weekly Summary</h3>
+            </div>
+            <div className="card-content">
+              <p>Weekly trends, user growth, and engagement patterns.</p>
+              <div style={{ marginTop: '16px' }}>
+                <button 
+                  onClick={() => generateReport('weekly')}
+                  className="btn btn-primary"
+                >
+                  This Week
+                </button>
+              </div>
+            </div>
+          </div>
+        </div>
+      ) : (
+        <div>
+          <div style={{ marginBottom: '20px' }}>
+            <button 
+              onClick={() => { setSelectedReport(null); setReportData(null); }}
+              className="btn btn-secondary"
+            >
+              ← Back to Reports
+            </button>
+          </div>
+
+          {reportData && (
+            <div className="card">
+              <div className="card-header">
+                <h3 className="card-title">
+                  {selectedReport.charAt(0).toUpperCase() + selectedReport.slice(1)} Report
+                </h3>
+                <div style={{ fontSize: '14px', color: '#64748b' }}>
+                  Generated: {new Date(reportData.generatedAt).toLocaleString()}
+                </div>
+              </div>
+              <div className="card-content">
+                {selectedReport === 'daily' && (
+                  <DailyReportView data={reportData} />
+                )}
+                {selectedReport === 'security' && (
+                  <SecurityReportView data={reportData} />
+                )}
+                {selectedReport === 'weekly' && (
+                  <WeeklyReportView data={reportData} />
+                )}
+              </div>
+            </div>
+          )}
+        </div>
+      )}
+    </div>
+  )
+}
+
+const DailyReportView = ({ data }) => (
+  <div>
+    <div className="stat-cards" style={{ marginBottom: '24px' }}>
+      <div className="stat-card">
+        <div className="stat-label">Total Interactions</div>
+        <div className="stat-value-large">{data.summary?.totalInteractions || 0}</div>
+      </div>
+      <div className="stat-card">
+        <div className="stat-label">New Users</div>
+        <div className="stat-value-large">{data.userActivity?.newRegistrations || 0}</div>
+      </div>
+      <div className="stat-card critical">
+        <div className="stat-label">Security Incidents</div>
+        <div className="stat-value-large">{data.summary?.securityIncidents || 0}</div>
+      </div>
+      <div className="stat-card">
+        <div className="stat-label">Engagement Rate</div>
+        <div className="stat-value-large">{(data.summary?.engagement || 0).toFixed(1)}</div>
+      </div>
+    </div>
+
+    <div className="dashboard-grid">
+      <div className="card">
+        <div className="card-header">
+          <h4>User Activity</h4>
+        </div>
+        <div className="card-content">
+          <div style={{ display: 'grid', gap: '8px' }}>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <span>Active Users:</span>
+              <strong>{data.userActivity?.activeUsers || 0}</strong>
+            </div>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <span>Total Sessions:</span>
+              <strong>{data.userActivity?.totalSessions || 0}</strong>
+            </div>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <span>Unique Users:</span>
+              <strong>{data.userActivity?.uniqueActiveUsers || 0}</strong>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div className="card">
+        <div className="card-header">
+          <h4>Conversations</h4>
+        </div>
+        <div className="card-content">
+          <div style={{ display: 'grid', gap: '8px' }}>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <span>Total:</span>
+              <strong>{data.conversations?.totalConversations || 0}</strong>
+            </div>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <span>Voice Used:</span>
+              <strong>{data.conversations?.voiceUsage || 0}</strong>
+            </div>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+              <span>Avg Response Time:</span>
+              <strong>{(data.conversations?.avgResponseTime || 0).toFixed(0)}ms</strong>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    {data.topTopics && data.topTopics.length > 0 && (
+      <div className="card" style={{ marginTop: '24px' }}>
+        <div className="card-header">
+          <h4>Top Discussion Topics</h4>
+        </div>
+        <div className="card-content">
+          <div style={{ display: 'grid', gap: '8px' }}>
+            {data.topTopics.map((topic, index) => (
+              <div key={index} style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>{topic._id}</span>
+                <span className="badge badge-medium">{topic.count}</span>
+              </div>
+            ))}
+          </div>
+        </div>
+      </div>
+    )}
+  </div>
+)
+
+const SecurityReportView = ({ data }) => (
+  <div>
+    <div className="stat-cards" style={{ marginBottom: '24px' }}>
+      <div className="stat-card critical">
+        <div className="stat-label">Total Alerts</div>
+        <div className="stat-value-large">{data.summary?.totalAlerts || 0}</div>
+      </div>
+      <div className="stat-card critical">
+        <div className="stat-label">Critical Incidents</div>
+        <div className="stat-value-large">{data.summary?.criticalIncidents || 0}</div>
+      </div>
+      <div className="stat-card warning">
+        <div className="stat-label">Repeat Offenders</div>
+        <div className="stat-value-large">{data.summary?.repeatOffenderCount || 0}</div>
+      </div>
+      <div className="stat-card">
+        <div className="stat-label">Users Blocked</div>
+        <div className="stat-value-large">{data.summary?.blockedUserCount || 0}</div>
+      </div>
+    </div>
+
+    {data.repeatOffenders && data.repeatOffenders.length > 0 && (
+      <div className="card" style={{ marginBottom: '24px' }}>
+        <div className="card-header">
+          <h4>Repeat Offenders</h4>
+        </div>
+        <div className="card-content">
+          <div className="table-container">
+            <table className="table">
+              <thead>
+                <tr>
+                  <th>IP Address</th>
+                  <th>Alert Count</th>
+                  <th>Severities</th>
+                  <th>Types</th>
+                </tr>
+              </thead>
+              <tbody>
+                {data.repeatOffenders.slice(0, 10).map((offender, index) => (
+                  <tr key={index}>
+                    <td style={{ fontFamily: 'monospace' }}>{offender._id}</td>
+                    <td><span className="badge badge-critical">{offender.alertCount}</span></td>
+                    <td style={{ fontSize: '12px' }}>
+                      {offender.severities.join(', ')}
+                    </td>
+                    <td style={{ fontSize: '12px' }}>
+                      {offender.types.join(', ')}
+                    </td>
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+    )}
+
+    {data.geoThreats && data.geoThreats.length > 0 && (
+      <div className="card">
+        <div className="card-header">
+          <h4>Geographic Threat Distribution</h4>
+        </div>
+        <div className="card-content">
+          <div style={{ display: 'grid', gap: '8px' }}>
+            {data.geoThreats.slice(0, 10).map((geo, index) => (
+              <div key={index} style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <span>{geo._id || 'Unknown'}</span>
+                <span className="badge badge-high">{geo.alerts} alerts</span>
+              </div>
+            ))}
+          </div>
+        </div>
+      </div>
+    )}
+  </div>
+)
+
+const WeeklyReportView = ({ data }) => (
+  <div>
+    <div className="stat-cards" style={{ marginBottom: '24px' }}>
+      <div className="stat-card">
+        <div className="stat-label">Total Conversations</div>
+        <div className="stat-value-large">{data.weekTotals?.conversations || 0}</div>
+      </div>
+      <div className="stat-card">
+        <div className="stat-label">New Users</div>
+        <div className="stat-value-large">{data.weekTotals?.newUsers || 0}</div>
+      </div>
+      <div className="stat-card warning">
+        <div className="stat-label">Total Alerts</div>
+        <div className="stat-value-large">{data.weekTotals?.alerts || 0}</div>
+      </div>
+      <div className="stat-card">
+        <div className="stat-label">Sessions</div>
+        <div className="stat-value-large">{data.weekTotals?.sessions || 0}</div>
+      </div>
+    </div>
+
+    <div className="card">
+      <div className="card-header">
+        <h4>Daily Breakdown</h4>
+      </div>
+      <div className="card-content">
+        <div className="table-container">
+          <table className="table">
+            <thead>
+              <tr>
+                <th>Date</th>
+                <th>Conversations</th>
+                <th>Sessions</th>
+                <th>Alerts</th>
+              </tr>
+            </thead>
+            <tbody>
+              {data.dailyBreakdown?.map((day, index) => (
+                <tr key={index}>
+                  <td>{new Date(day.date).toLocaleDateString()}</td>
+                  <td>{day.conversations}</td>
+                  <td>{day.sessions}</td>
+                  <td>
+                    {day.alerts > 0 ? (
+                      <span className="badge badge-warning">{day.alerts}</span>
+                    ) : (
+                      day.alerts
+                    )}
+                  </td>
+                </tr>
+              ))}
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  </div>
+)
+
+export default ReportsPage
\ No newline at end of file
diff --git a/frontend/src/components/Sidebar.js b/frontend/src/components/Sidebar.js
new file mode 100644
index 0000000..68c3cd8
--- /dev/null
+++ b/frontend/src/components/Sidebar.js
@@ -0,0 +1,53 @@
+import React from 'react'
+import { NavLink } from 'react-router-dom'
+
+const Sidebar = () => {
+  const navItems = [
+    { path: '/dashboard', label: 'Dashboard', icon: '📊' },
+    { path: '/alerts', label: 'Alerts', icon: '🚨' },
+    { path: '/users', label: 'Users', icon: '👥' },
+    { path: '/monitoring', label: 'Monitoring', icon: '📈' },
+    { path: '/reports', label: 'Reports', icon: '📋' }
+  ]
+
+  return (
+    <div className="sidebar">
+      <div className="sidebar-header">
+        <div className="sidebar-title">Dear Bubbe Admin</div>
+        <div className="sidebar-subtitle">Security Dashboard</div>
+      </div>
+      
+      <nav className="nav-menu">
+        {navItems.map((item) => (
+          <NavLink
+            key={item.path}
+            to={item.path}
+            className={({ isActive }) => 
+              `nav-item ${isActive ? 'active' : ''}`
+            }
+          >
+            <span className="nav-icon">{item.icon}</span>
+            {item.label}
+          </NavLink>
+        ))}
+      </nav>
+
+      <div style={{
+        position: 'absolute',
+        bottom: '20px',
+        left: '20px',
+        right: '20px',
+        fontSize: '12px',
+        color: '#94a3b8',
+        textAlign: 'center',
+        borderTop: '1px solid #475569',
+        paddingTop: '16px'
+      }}>
+        <div>Version 1.0.0</div>
+        <div>Real-time Protection Active</div>
+      </div>
+    </div>
+  )
+}
+
+export default Sidebar
\ No newline at end of file
diff --git a/frontend/src/components/UsersPage.js b/frontend/src/components/UsersPage.js
new file mode 100644
index 0000000..ba410da
--- /dev/null
+++ b/frontend/src/components/UsersPage.js
@@ -0,0 +1,323 @@
+import React, { useState, useEffect } from 'react'
+import axios from 'axios'
+
+const UsersPage = () => {
+  const [users, setUsers] = useState([])
+  const [loading, setLoading] = useState(true)
+  const [selectedUser, setSelectedUser] = useState(null)
+  const [filters, setFilters] = useState({
+    search: '',
+    blocked: '',
+    riskLevel: '',
+    country: ''
+  })
+
+  useEffect(() => {
+    fetchUsers()
+  }, [filters])
+
+  const fetchUsers = async () => {
+    try {
+      setLoading(true)
+      const params = new URLSearchParams()
+      Object.entries(filters).forEach(([key, value]) => {
+        if (value) params.append(key, value)
+      })
+
+      const response = await axios.get(`/api/users?${params.toString()}`)
+      setUsers(response.data.users || [])
+    } catch (error) {
+      console.error('Failed to fetch users:', error)
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  const fetchUserDetails = async (userId) => {
+    try {
+      const response = await axios.get(`/api/users/${userId}`)
+      setSelectedUser(response.data)
+    } catch (error) {
+      console.error('Failed to fetch user details:', error)
+    }
+  }
+
+  const blockUser = async (userId, reason) => {
+    try {
+      await axios.post(`/api/users/${userId}/block`, { reason })
+      fetchUsers()
+      setSelectedUser(null)
+      alert('User blocked successfully')
+    } catch (error) {
+      console.error('Failed to block user:', error)
+      alert('Failed to block user')
+    }
+  }
+
+  const unblockUser = async (userId) => {
+    try {
+      await axios.post(`/api/users/${userId}/unblock`)
+      fetchUsers()
+      setSelectedUser(null)
+      alert('User unblocked successfully')
+    } catch (error) {
+      console.error('Failed to unblock user:', error)
+      alert('Failed to unblock user')
+    }
+  }
+
+  if (loading) {
+    return (
+      <div style={{ display: 'flex', justifyContent: 'center', padding: '40px' }}>
+        <div className="loading-spinner"></div>
+        <span style={{ marginLeft: '12px' }}>Loading users...</span>
+      </div>
+    )
+  }
+
+  return (
+    <div>
+      <div style={{
+        display: 'flex',
+        justifyContent: 'space-between',
+        alignItems: 'center',
+        marginBottom: '24px'
+      }}>
+        <h2>User Management</h2>
+        <button onClick={fetchUsers} className="btn btn-primary">
+          Refresh
+        </button>
+      </div>
+
+      {/* Filters */}
+      <div className="card" style={{ marginBottom: '24px' }}>
+        <div className="card-content">
+          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '16px' }}>
+            <input
+              type="text"
+              placeholder="Search users..."
+              className="form-input"
+              value={filters.search}
+              onChange={(e) => setFilters({...filters, search: e.target.value})}
+            />
+            <select
+              className="form-select"
+              value={filters.blocked}
+              onChange={(e) => setFilters({...filters, blocked: e.target.value})}
+            >
+              <option value="">All Users</option>
+              <option value="false">Active Users</option>
+              <option value="true">Blocked Users</option>
+            </select>
+            <select
+              className="form-select"
+              value={filters.riskLevel}
+              onChange={(e) => setFilters({...filters, riskLevel: e.target.value})}
+            >
+              <option value="">All Risk Levels</option>
+              <option value="critical">Critical</option>
+              <option value="high">High</option>
+              <option value="medium">Medium</option>
+              <option value="low">Low</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      {/* Users Table */}
+      <div className="card">
+        <div className="card-content">
+          <div className="table-container">
+            <table className="table">
+              <thead>
+                <tr>
+                  <th>User</th>
+                  <th>Email</th>
+                  <th>Location</th>
+                  <th>Messages</th>
+                  <th>Risk Level</th>
+                  <th>Status</th>
+                  <th>Last Visit</th>
+                  <th>Actions</th>
+                </tr>
+              </thead>
+              <tbody>
+                {users.map((user) => (
+                  <tr key={user.userId}>
+                    <td>
+                      <div>
+                        <strong>{user.profile?.name || user.profile?.preferredName || 'Unknown'}</strong>
+                        <div style={{ fontSize: '12px', color: '#64748b' }}>
+                          {user.userId}
+                        </div>
+                      </div>
+                    </td>
+                    <td>{user.email || 'No email'}</td>
+                    <td>
+                      {user.location?.city && user.location?.country ? 
+                        `${user.location.city}, ${user.location.country}` : 
+                        'Unknown'
+                      }
+                    </td>
+                    <td>{user.stats?.totalMessages || 0}</td>
+                    <td>
+                      <span className={`badge badge-${user.security?.riskLevel || 'low'}`}>
+                        {user.security?.riskLevel || 'low'}
+                      </span>
+                    </td>
+                    <td>
+                      <span className={`badge ${user.security?.blocked ? 'badge-blocked' : 'badge-active'}`}>
+                        {user.security?.blocked ? 'Blocked' : 'Active'}
+                      </span>
+                    </td>
+                    <td style={{ fontSize: '12px', color: '#64748b' }}>
+                      {user.stats?.lastVisit ? 
+                        new Date(user.stats.lastVisit).toLocaleString() : 
+                        'Never'
+                      }
+                    </td>
+                    <td>
+                      <button
+                        onClick={() => fetchUserDetails(user.userId)}
+                        className="btn btn-small btn-primary"
+                        style={{ marginRight: '8px' }}
+                      >
+                        View
+                      </button>
+                      {user.security?.blocked ? (
+                        <button
+                          onClick={() => unblockUser(user.userId)}
+                          className="btn btn-small btn-secondary"
+                        >
+                          Unblock
+                        </button>
+                      ) : (
+                        <button
+                          onClick={() => {
+                            const reason = prompt('Reason for blocking:')
+                            if (reason) blockUser(user.userId, reason)
+                          }}
+                          className="btn btn-small btn-danger"
+                        >
+                          Block
+                        </button>
+                      )}
+                    </td>
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+
+      {/* User Details Modal */}
+      {selectedUser && (
+        <div className="live-alert-modal" onClick={() => setSelectedUser(null)}>
+          <div 
+            className="live-alert-content" 
+            style={{ maxWidth: '800px', maxHeight: '80vh', overflow: 'auto' }}
+            onClick={e => e.stopPropagation()}
+          >
+            <div className="alert-header">
+              <h3>User Details</h3>
+              <button onClick={() => setSelectedUser(null)}>×</button>
+            </div>
+            
+            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
+              <div>
+                <h4>Profile Information</h4>
+                <div style={{ background: '#f8fafc', padding: '16px', borderRadius: '8px' }}>
+                  <div><strong>Name:</strong> {selectedUser.user?.profile?.name || 'Unknown'}</div>
+                  <div><strong>Email:</strong> {selectedUser.user?.email || 'No email'}</div>
+                  <div><strong>Age:</strong> {selectedUser.user?.profile?.age || 'Unknown'}</div>
+                  <div><strong>Location:</strong> {
+                    selectedUser.user?.location?.city ? 
+                    `${selectedUser.user.location.city}, ${selectedUser.user.location.country}` : 
+                    'Unknown'
+                  }</div>
+                  <div><strong>Relationship:</strong> {selectedUser.user?.profile?.relationshipStatus || 'Unknown'}</div>
+                  <div><strong>Job:</strong> {selectedUser.user?.profile?.jobTitle || 'Unknown'}</div>
+                </div>
+              </div>
+              
+              <div>
+                <h4>Security & Activity</h4>
+                <div style={{ background: '#f8fafc', padding: '16px', borderRadius: '8px' }}>
+                  <div><strong>Risk Level:</strong> 
+                    <span className={`badge badge-${selectedUser.user?.security?.riskLevel || 'low'}`} style={{ marginLeft: '8px' }}>
+                      {selectedUser.user?.security?.riskLevel || 'low'}
+                    </span>
+                  </div>
+                  <div><strong>Status:</strong> 
+                    <span className={`badge ${selectedUser.user?.security?.blocked ? 'badge-blocked' : 'badge-active'}`} style={{ marginLeft: '8px' }}>
+                      {selectedUser.user?.security?.blocked ? 'Blocked' : 'Active'}
+                    </span>
+                  </div>
+                  <div><strong>Total Messages:</strong> {selectedUser.user?.stats?.totalMessages || 0}</div>
+                  <div><strong>Total Sessions:</strong> {selectedUser.sessions?.length || 0}</div>
+                  <div><strong>Alerts:</strong> {selectedUser.alerts?.length || 0}</div>
+                </div>
+              </div>
+            </div>
+
+            {selectedUser.alerts && selectedUser.alerts.length > 0 && (
+              <div style={{ marginTop: '20px' }}>
+                <h4>Recent Alerts</h4>
+                <div style={{ maxHeight: '200px', overflow: 'auto' }}>
+                  {selectedUser.alerts.slice(0, 5).map((alert, index) => (
+                    <div key={index} style={{
+                      padding: '12px',
+                      margin: '8px 0',
+                      background: '#fef2f2',
+                      border: '1px solid #fecaca',
+                      borderRadius: '6px'
+                    }}>
+                      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                        <span className={`badge badge-${alert.severity}`}>{alert.type}</span>
+                        <span style={{ fontSize: '12px', color: '#64748b' }}>
+                          {new Date(alert.timestamp).toLocaleString()}
+                        </span>
+                      </div>
+                      {alert.violation?.match && (
+                        <div style={{ marginTop: '8px', fontSize: '14px', fontFamily: 'monospace' }}>
+                          "{alert.violation.match}"
+                        </div>
+                      )}
+                    </div>
+                  ))}
+                </div>
+              </div>
+            )}
+
+            <div style={{ marginTop: '20px', display: 'flex', gap: '12px', justifyContent: 'flex-end' }}>
+              <button onClick={() => setSelectedUser(null)} className="btn btn-secondary">
+                Close
+              </button>
+              {selectedUser.user?.security?.blocked ? (
+                <button
+                  onClick={() => unblockUser(selectedUser.user.userId)}
+                  className="btn btn-primary"
+                >
+                  Unblock User
+                </button>
+              ) : (
+                <button
+                  onClick={() => {
+                    const reason = prompt('Reason for blocking:')
+                    if (reason) blockUser(selectedUser.user.userId, reason)
+                  }}
+                  className="btn btn-danger"
+                >
+                  Block User
+                </button>
+              )}
+            </div>
+          </div>
+        </div>
+      )}
+    </div>
+  )
+}
+
+export default UsersPage
\ No newline at end of file
diff --git a/frontend/src/index.js b/frontend/src/index.js
new file mode 100644
index 0000000..95cfe8a
--- /dev/null
+++ b/frontend/src/index.js
@@ -0,0 +1,10 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App'
+
+const root = ReactDOM.createRoot(document.getElementById('root'))
+root.render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>
+)
\ No newline at end of file
diff --git a/frontend/twitter-monitor.html b/frontend/twitter-monitor.html
new file mode 100644
index 0000000..2c27b04
--- /dev/null
+++ b/frontend/twitter-monitor.html
@@ -0,0 +1,271 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Twitter Monitor - Dear Bubbe Admin</title>
+    <style>
+        body {
+            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            margin: 0;
+            padding: 20px;
+            min-height: 100vh;
+        }
+        .container {
+            max-width: 1400px;
+            margin: 0 auto;
+        }
+        .header {
+            background: white;
+            border-radius: 10px;
+            padding: 20px;
+            margin-bottom: 20px;
+            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+        }
+        .stats-grid {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+            gap: 15px;
+            margin-bottom: 20px;
+        }
+        .stat-card {
+            background: white;
+            padding: 15px;
+            border-radius: 8px;
+            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+        }
+        .stat-number {
+            font-size: 2em;
+            font-weight: bold;
+            color: #667eea;
+        }
+        .stat-label {
+            color: #666;
+            font-size: 0.9em;
+        }
+        .posts-container {
+            background: white;
+            border-radius: 10px;
+            padding: 20px;
+            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+        }
+        .post-item {
+            border: 1px solid #e1e1e1;
+            border-radius: 8px;
+            padding: 15px;
+            margin-bottom: 15px;
+            transition: all 0.2s;
+        }
+        .post-item:hover {
+            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+        }
+        .post-header {
+            display: flex;
+            justify-content: space-between;
+            margin-bottom: 10px;
+        }
+        .post-time {
+            color: #666;
+            font-size: 0.9em;
+        }
+        .post-status {
+            display: inline-block;
+            padding: 3px 8px;
+            border-radius: 4px;
+            font-size: 0.85em;
+            font-weight: bold;
+        }
+        .status-posted {
+            background: #10b981;
+            color: white;
+        }
+        .status-queued {
+            background: #f59e0b;
+            color: white;
+        }
+        .status-failed {
+            background: #ef4444;
+            color: white;
+        }
+        .post-content {
+            margin: 10px 0;
+            line-height: 1.5;
+        }
+        .post-link {
+            color: #667eea;
+            text-decoration: none;
+            font-size: 0.9em;
+        }
+        .post-link:hover {
+            text-decoration: underline;
+        }
+        .source-badge {
+            display: inline-block;
+            padding: 2px 6px;
+            background: #e5e7eb;
+            border-radius: 3px;
+            font-size: 0.8em;
+            margin-right: 5px;
+        }
+        .source-real {
+            background: #dcfce7;
+            color: #166534;
+        }
+        .source-bot {
+            background: #fef3c7;
+            color: #92400e;
+        }
+        .refresh-btn {
+            background: #667eea;
+            color: white;
+            border: none;
+            padding: 10px 20px;
+            border-radius: 5px;
+            cursor: pointer;
+            font-size: 1em;
+        }
+        .refresh-btn:hover {
+            background: #5a67d8;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <div class="header">
+            <h1>🐦 Twitter Monitor - Dear Bubbe</h1>
+            <button class="refresh-btn" onclick="loadData()">🔄 Refresh</button>
+        </div>
+
+        <div class="stats-grid">
+            <div class="stat-card">
+                <div class="stat-number" id="todayCount">0</div>
+                <div class="stat-label">Posts Today</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-number" id="weekCount">0</div>
+                <div class="stat-label">This Week</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-number" id="realUserCount">0</div>
+                <div class="stat-label">Real User Posts</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-number" id="botGenCount">0</div>
+                <div class="stat-label">Bot Generated</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-number" id="remainingToday">100</div>
+                <div class="stat-label">Remaining Today</div>
+            </div>
+        </div>
+
+        <div class="posts-container">
+            <h2>Recent Posts</h2>
+            <div id="postsList"></div>
+        </div>
+    </div>
+
+    <script>
+        const API_URL = 'http://45.61.58.125:5013';
+        
+        async function loadData() {
+            try {
+                // Load immediate posts
+                const response = await fetch('/root/Projects/dear-bubbe-admin/logs/immediate-posts.json');
+                const posts = await response.json();
+                
+                // Load stats
+                const statsResponse = await fetch('/root/Projects/dear-bubbe-admin/logs/twitter-stats.json');
+                const stats = await statsResponse.json();
+                
+                // Calculate counts
+                const now = new Date();
+                const today = now.toDateString();
+                const weekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
+                
+                let todayCount = 0;
+                let weekCount = 0;
+                let realUserCount = 0;
+                let botGenCount = 0;
+                
+                const recentPosts = posts.slice(-50).reverse(); // Last 50 posts
+                
+                recentPosts.forEach(post => {
+                    const postDate = new Date(post.timestamp);
+                    
+                    if (postDate.toDateString() === today) {
+                        todayCount++;
+                    }
+                    
+                    if (postDate > weekAgo) {
+                        weekCount++;
+                    }
+                    
+                    if (post.userId && post.userId.includes('twitter-bot')) {
+                        botGenCount++;
+                    } else {
+                        realUserCount++;
+                    }
+                });
+                
+                // Update stats
+                document.getElementById('todayCount').textContent = todayCount;
+                document.getElementById('weekCount').textContent = weekCount;
+                document.getElementById('realUserCount').textContent = realUserCount;
+                document.getElementById('botGenCount').textContent = botGenCount;
+                document.getElementById('remainingToday').textContent = Math.max(0, 100 - todayCount);
+                
+                // Display posts
+                const postsList = document.getElementById('postsList');
+                let html = '';
+                
+                recentPosts.forEach(post => {
+                    const isRealUser = !post.userId?.includes('twitter-bot');
+                    const status = post.twitterPosted ? 'posted' : 'queued';
+                    const postDate = new Date(post.timestamp);
+                    
+                    html += `
+                        <div class="post-item">
+                            <div class="post-header">
+                                <div>
+                                    <span class="source-badge ${isRealUser ? 'source-real' : 'source-bot'}">
+                                        ${isRealUser ? '👤 Real User' : '🤖 Bot'}
+                                    </span>
+                                    <span class="post-time">${postDate.toLocaleString()}</span>
+                                </div>
+                                <span class="post-status status-${status}">${status.toUpperCase()}</span>
+                            </div>
+                            <div class="post-content">
+                                <strong>User:</strong> ${post.userSnippet || 'N/A'}<br>
+                                <strong>Bubbe:</strong> ${post.bubbeSnippet || 'N/A'}...
+                            </div>
+                            ${post.twitterText ? `
+                                <div style="background:#f3f4f6; padding:10px; border-radius:5px; margin:10px 0;">
+                                    <strong>Tweet:</strong> ${post.twitterText.substring(0, 150)}...
+                                </div>
+                            ` : ''}
+                            ${post.url ? `
+                                <a href="${post.url}" target="_blank" class="post-link">
+                                    View on Twitter →
+                                </a>
+                            ` : ''}
+                        </div>
+                    `;
+                });
+                
+                postsList.innerHTML = html || '<p>No posts yet</p>';
+                
+            } catch (error) {
+                console.error('Error loading data:', error);
+            }
+        }
+        
+        // Load data on page load
+        loadData();
+        
+        // Auto-refresh every 30 seconds
+        setInterval(loadData, 30000);
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/log-all-users.js b/log-all-users.js
new file mode 100644
index 0000000..f39040d
--- /dev/null
+++ b/log-all-users.js
@@ -0,0 +1,271 @@
+#!/usr/bin/env node
+
+const fs = require('fs');
+const path = require('path');
+
+// Color codes for terminal output
+const colors = {
+  reset: '\x1b[0m',
+  bright: '\x1b[1m',
+  red: '\x1b[31m',
+  green: '\x1b[32m',
+  yellow: '\x1b[33m',
+  blue: '\x1b[34m',
+  magenta: '\x1b[35m',
+  cyan: '\x1b[36m'
+};
+
+// Track all unique users
+const allUsers = new Map();
+const userSessions = new Map();
+
+// Read all log sources
+const logSources = [
+  {
+    path: '/root/Projects/dear-bubbe-admin/logs/combined.log',
+    name: 'Admin Dashboard Logs'
+  },
+  {
+    path: '/root/Projects/dear-bubbe-nextjs/logs/chat.log',
+    name: 'Bubbe Chat Logs'
+  },
+  {
+    path: '/root/Projects/dear-bubbe-admin/logs/access.log',
+    name: 'Access Logs'
+  }
+];
+
+// Parse different log formats
+function parseLogLine(line) {
+  // Try to parse as JSON first
+  try {
+    const parsed = JSON.parse(line);
+    return {
+      timestamp: parsed.timestamp || parsed.time,
+      ip: parsed.ip || parsed.userIp,
+      userName: parsed.userName || parsed.name || 'Anonymous',
+      email: parsed.userEmail || parsed.email || '',
+      location: parsed.location || 'Unknown',
+      message: parsed.message || parsed.userMessage || '',
+      mode: parsed.mode || 'unknown',
+      userId: parsed.userId || parsed.sessionId || null
+    };
+  } catch (e) {
+    // Try to extract from text format
+    const ipMatch = line.match(/IP:\s*([\d\.:a-f]+)/i);
+    const userMatch = line.match(/userName[":]+\s*([^",}]+)/i);
+    const emailMatch = line.match(/email[":]+\s*([^",}]+)/i);
+    const timeMatch = line.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
+    
+    if (ipMatch || userMatch) {
+      return {
+        timestamp: timeMatch ? timeMatch[0] : null,
+        ip: ipMatch ? ipMatch[1] : 'Unknown',
+        userName: userMatch ? userMatch[1] : 'Anonymous',
+        email: emailMatch ? emailMatch[1] : '',
+        location: 'Unknown',
+        message: '',
+        mode: 'unknown',
+        userId: null
+      };
+    }
+    return null;
+  }
+}
+
+// Process all log files
+console.log(`${colors.cyan}${'='.repeat(80)}${colors.reset}`);
+console.log(`${colors.bright}${colors.blue}📊 COMPREHENSIVE USER LOG ANALYSIS${colors.reset}`);
+console.log(`${colors.cyan}${'='.repeat(80)}${colors.reset}\n`);
+
+logSources.forEach(source => {
+  if (!fs.existsSync(source.path)) {
+    console.log(`${colors.yellow}⚠️  ${source.name}: File not found${colors.reset}`);
+    return;
+  }
+  
+  console.log(`${colors.green}📁 Processing: ${source.name}${colors.reset}`);
+  const content = fs.readFileSync(source.path, 'utf-8');
+  const lines = content.split('\n');
+  
+  lines.forEach(line => {
+    if (!line.trim()) return;
+    
+    const parsed = parseLogLine(line);
+    if (!parsed || !parsed.ip) return;
+    
+    // Create unique key for user
+    const userKey = parsed.email || parsed.ip;
+    
+    if (!allUsers.has(userKey)) {
+      allUsers.set(userKey, {
+        userName: parsed.userName,
+        email: parsed.email,
+        ips: new Set(),
+        locations: new Set(),
+        firstSeen: parsed.timestamp,
+        lastSeen: parsed.timestamp,
+        messageCount: 0,
+        modes: new Set(),
+        sessions: []
+      });
+    }
+    
+    const user = allUsers.get(userKey);
+    
+    // Update user data
+    if (parsed.ip && parsed.ip !== 'Unknown') {
+      user.ips.add(parsed.ip);
+    }
+    if (parsed.location && parsed.location !== 'Unknown') {
+      user.locations.add(parsed.location);
+    }
+    if (parsed.mode && parsed.mode !== 'unknown') {
+      user.modes.add(parsed.mode);
+    }
+    if (parsed.message) {
+      user.messageCount++;
+    }
+    
+    // Update timestamps
+    if (parsed.timestamp) {
+      if (!user.firstSeen || parsed.timestamp < user.firstSeen) {
+        user.firstSeen = parsed.timestamp;
+      }
+      if (!user.lastSeen || parsed.timestamp > user.lastSeen) {
+        user.lastSeen = parsed.timestamp;
+      }
+      
+      // Track session
+      user.sessions.push({
+        timestamp: parsed.timestamp,
+        ip: parsed.ip,
+        location: parsed.location
+      });
+    }
+    
+    // Update name if better one found
+    if (parsed.userName && parsed.userName !== 'Anonymous' && user.userName === 'Anonymous') {
+      user.userName = parsed.userName;
+    }
+  });
+  
+  console.log(`   ✓ Processed ${lines.length} lines\n`);
+});
+
+// Sort users by last activity
+const sortedUsers = Array.from(allUsers.entries()).sort((a, b) => {
+  const timeA = new Date(a[1].lastSeen || 0).getTime();
+  const timeB = new Date(b[1].lastSeen || 0).getTime();
+  return timeB - timeA;
+});
+
+// Display results
+console.log(`${colors.cyan}${'='.repeat(80)}${colors.reset}`);
+console.log(`${colors.bright}${colors.magenta}👥 ALL USERS (${sortedUsers.length} Total)${colors.reset}`);
+console.log(`${colors.cyan}${'='.repeat(80)}${colors.reset}\n`);
+
+sortedUsers.forEach((([key, user], index) => {
+  const isActive = user.lastSeen && 
+    (new Date() - new Date(user.lastSeen)) < 3600000; // Active in last hour
+  
+  const statusColor = isActive ? colors.green : colors.reset;
+  const status = isActive ? '🟢 ACTIVE' : '⚪ INACTIVE';
+  
+  console.log(`${colors.bright}${index + 1}. ${user.userName}${colors.reset} ${statusColor}${status}${colors.reset}`);
+  
+  if (user.email) {
+    console.log(`   ${colors.blue}📧 Email:${colors.reset} ${user.email}`);
+  }
+  
+  console.log(`   ${colors.yellow}🌐 IPs:${colors.reset} ${Array.from(user.ips).join(', ') || 'None'}`);
+  console.log(`   ${colors.cyan}📍 Locations:${colors.reset} ${Array.from(user.locations).join(', ') || 'Unknown'}`);
+  
+  if (user.firstSeen) {
+    const first = new Date(user.firstSeen);
+    const last = new Date(user.lastSeen);
+    console.log(`   ${colors.magenta}⏱️ First Seen:${colors.reset} ${first.toLocaleString()}`);
+    console.log(`   ${colors.magenta}⏱️ Last Seen:${colors.reset} ${last.toLocaleString()}`);
+  }
+  
+  console.log(`   ${colors.green}💬 Messages:${colors.reset} ${user.messageCount}`);
+  
+  if (user.modes.size > 0) {
+    console.log(`   ${colors.blue}🎭 Modes Used:${colors.reset} ${Array.from(user.modes).join(', ')}`);
+  }
+  
+  // Show recent activity
+  const recentSessions = user.sessions
+    .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
+    .slice(0, 3);
+  
+  if (recentSessions.length > 0) {
+    console.log(`   ${colors.yellow}📅 Recent Activity:${colors.reset}`);
+    recentSessions.forEach(session => {
+      const time = new Date(session.timestamp).toLocaleString();
+      console.log(`      • ${time} from ${session.ip}`);
+    });
+  }
+  
+  console.log(`   ${colors.cyan}${'-'.repeat(60)}${colors.reset}`);
+}));
+
+// Summary statistics
+const activeCount = sortedUsers.filter(([k, u]) => 
+  u.lastSeen && (new Date() - new Date(u.lastSeen)) < 3600000
+).length;
+
+const todayCount = sortedUsers.filter(([k, u]) => 
+  u.lastSeen && (new Date() - new Date(u.lastSeen)) < 86400000
+).length;
+
+console.log(`\n${colors.cyan}${'='.repeat(80)}${colors.reset}`);
+console.log(`${colors.bright}${colors.green}📈 SUMMARY STATISTICS${colors.reset}`);
+console.log(`${colors.cyan}${'='.repeat(80)}${colors.reset}\n`);
+
+console.log(`${colors.bright}Total Users:${colors.reset} ${sortedUsers.length}`);
+console.log(`${colors.green}Active (Last Hour):${colors.reset} ${activeCount}`);
+console.log(`${colors.yellow}Active Today:${colors.reset} ${todayCount}`);
+console.log(`${colors.blue}With Email:${colors.reset} ${sortedUsers.filter(([k, u]) => u.email).length}`);
+console.log(`${colors.magenta}Anonymous:${colors.reset} ${sortedUsers.filter(([k, u]) => u.userName === 'Anonymous').length}`);
+
+// Geographic distribution
+const locations = new Map();
+sortedUsers.forEach(([k, user]) => {
+  user.locations.forEach(loc => {
+    if (loc && loc !== 'Unknown') {
+      locations.set(loc, (locations.get(loc) || 0) + 1);
+    }
+  });
+});
+
+if (locations.size > 0) {
+  console.log(`\n${colors.bright}${colors.cyan}🌍 GEOGRAPHIC DISTRIBUTION:${colors.reset}`);
+  Array.from(locations.entries())
+    .sort((a, b) => b[1] - a[1])
+    .slice(0, 10)
+    .forEach(([loc, count]) => {
+      console.log(`   • ${loc}: ${count} users`);
+    });
+}
+
+// Save detailed report
+const reportPath = '/root/Projects/dear-bubbe-admin/logs/user-report.json';
+const report = {
+  generatedAt: new Date().toISOString(),
+  totalUsers: sortedUsers.length,
+  activeInLastHour: activeCount,
+  activeToday: todayCount,
+  users: sortedUsers.map(([key, user]) => ({
+    key,
+    ...user,
+    ips: Array.from(user.ips),
+    locations: Array.from(user.locations),
+    modes: Array.from(user.modes)
+  }))
+};
+
+fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
+
+console.log(`\n${colors.green}✅ Detailed report saved to: ${reportPath}${colors.reset}`);
+console.log(`${colors.cyan}${'='.repeat(80)}${colors.reset}\n`);
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..7d3fd00
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3512 @@
+{
+  "name": "dear-bubbe-admin",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "dear-bubbe-admin",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "axios": "^1.6.0",
+        "bcryptjs": "^2.4.3",
+        "compression": "^1.8.1",
+        "cors": "^2.8.5",
+        "dotenv": "^16.6.1",
+        "express": "^4.21.2",
+        "express-rate-limit": "^7.1.2",
+        "google-auth-library": "^10.5.0",
+        "helmet": "^7.2.0",
+        "jsonwebtoken": "^9.0.2",
+        "moment": "^2.29.4",
+        "mongoose": "^7.5.2",
+        "multer": "^1.4.5-lts.1",
+        "node-cron": "^3.0.2",
+        "socket.io": "^4.8.1",
+        "winston": "^3.18.3"
+      },
+      "devDependencies": {
+        "concurrently": "^8.2.2",
+        "nodemon": "^3.0.1"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+      "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@colors/colors": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
+      "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/@dabh/diagnostics": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz",
+      "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@so-ric/colorspace": "^1.1.6",
+        "enabled": "2.0.x",
+        "kuler": "^2.0.0"
+      }
+    },
+    "node_modules/@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "license": "MIT"
+    },
+    "node_modules/@isaacs/cliui/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@mongodb-js/saslprep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz",
+      "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "sparse-bitfield": "^3.0.3"
+      }
+    },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@so-ric/colorspace": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz",
+      "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==",
+      "license": "MIT",
+      "dependencies": {
+        "color": "^5.0.2",
+        "text-hex": "1.0.x"
+      }
+    },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/cors": {
+      "version": "2.8.19",
+      "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+      "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "24.10.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
+      "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~7.16.0"
+      }
+    },
+    "node_modules/@types/triple-beam": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
+      "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/webidl-conversions": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
+      "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/whatwg-url": {
+      "version": "8.2.2",
+      "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
+      "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/webidl-conversions": "*"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/accepts/node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/append-field": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+      "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
+      "license": "MIT"
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+      "license": "MIT"
+    },
+    "node_modules/async": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+      "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
+      "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.4",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "license": "MIT"
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/base64id": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+      "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+      "license": "MIT",
+      "engines": {
+        "node": "^4.5.0 || >= 5.9"
+      }
+    },
+    "node_modules/bcryptjs": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+      "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
+      "license": "MIT"
+    },
+    "node_modules/bignumber.js": {
+      "version": "9.3.1",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+      "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.3",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+      "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.13.0",
+        "raw-body": "2.5.2",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/bson": {
+      "version": "5.5.1",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
+      "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14.20.1"
+      }
+    },
+    "node_modules/buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "license": "MIT"
+    },
+    "node_modules/busboy": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+      "dependencies": {
+        "streamsearch": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.16.0"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chalk/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/color": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz",
+      "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^3.1.3",
+        "color-string": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "license": "MIT"
+    },
+    "node_modules/color-string": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
+      "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/color-string/node_modules/color-name": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+      "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.20"
+      }
+    },
+    "node_modules/color/node_modules/color-convert": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz",
+      "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.6"
+      }
+    },
+    "node_modules/color/node_modules/color-name": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+      "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.20"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/compressible": {
+      "version": "2.0.18",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+      "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": ">= 1.43.0 < 2"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/compression": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
+      "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "compressible": "~2.0.18",
+        "debug": "2.6.9",
+        "negotiator": "~0.6.4",
+        "on-headers": "~1.1.0",
+        "safe-buffer": "5.2.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "engines": [
+        "node >= 0.8"
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "node_modules/concurrently": {
+      "version": "8.2.2",
+      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
+      "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.2",
+        "date-fns": "^2.30.0",
+        "lodash": "^4.17.21",
+        "rxjs": "^7.8.1",
+        "shell-quote": "^1.8.1",
+        "spawn-command": "0.0.2",
+        "supports-color": "^8.1.1",
+        "tree-kill": "^1.2.2",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "conc": "dist/bin/concurrently.js",
+        "concurrently": "dist/bin/concurrently.js"
+      },
+      "engines": {
+        "node": "^14.13.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+      }
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+      "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
+      "license": "MIT"
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "license": "MIT"
+    },
+    "node_modules/cors": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "license": "MIT",
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/data-uri-to-buffer": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+      "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/date-fns": {
+      "version": "2.30.0",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+      "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.21.0"
+      },
+      "engines": {
+        "node": ">=0.11"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/date-fns"
+      }
+    },
+    "node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "16.6.1",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+      "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "license": "MIT"
+    },
+    "node_modules/ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "license": "MIT"
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT"
+    },
+    "node_modules/enabled": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
+      "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
+      "license": "MIT"
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/engine.io": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
+      "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/cors": "^2.8.12",
+        "@types/node": ">=10.0.0",
+        "accepts": "~1.3.4",
+        "base64id": "2.0.0",
+        "cookie": "~0.7.2",
+        "cors": "~2.8.5",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1"
+      },
+      "engines": {
+        "node": ">=10.2.0"
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/engine.io/node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/engine.io/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/engine.io/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "license": "MIT"
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/express": {
+      "version": "4.21.2",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+      "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.3",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.7.1",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.3.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.3",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.12",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.13.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.19.0",
+        "serve-static": "1.16.2",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/express-rate-limit": {
+      "version": "7.5.1",
+      "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
+      "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/express-rate-limit"
+      },
+      "peerDependencies": {
+        "express": ">= 4.11"
+      }
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "license": "MIT"
+    },
+    "node_modules/fecha": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
+      "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
+      "license": "MIT"
+    },
+    "node_modules/fetch-blob": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+      "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "paypal",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "node-domexception": "^1.0.0",
+        "web-streams-polyfill": "^3.0.3"
+      },
+      "engines": {
+        "node": "^12.20 || >= 14.13"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+      "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/fn.name": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
+      "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
+      "license": "MIT"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/foreground-child": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+      "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+      "license": "ISC",
+      "dependencies": {
+        "cross-spawn": "^7.0.6",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/formdata-polyfill": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+      "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+      "license": "MIT",
+      "dependencies": {
+        "fetch-blob": "^3.1.2"
+      },
+      "engines": {
+        "node": ">=12.20.0"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gaxios": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
+      "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^7.0.1",
+        "node-fetch": "^3.3.2",
+        "rimraf": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/gcp-metadata": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+      "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "gaxios": "^7.0.0",
+        "google-logging-utils": "^1.0.0",
+        "json-bigint": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/glob": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+      "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/glob/node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/glob/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/google-auth-library": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz",
+      "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "gaxios": "^7.0.0",
+        "gcp-metadata": "^8.0.0",
+        "google-logging-utils": "^1.0.0",
+        "gtoken": "^8.0.0",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/google-auth-library/node_modules/jwa": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+      "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer-equal-constant-time": "^1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/google-auth-library/node_modules/jws": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+      "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+      "license": "MIT",
+      "dependencies": {
+        "jwa": "^2.0.0",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/google-logging-utils": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+      "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gtoken": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz",
+      "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
+      "license": "MIT",
+      "dependencies": {
+        "gaxios": "^7.0.0",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/gtoken/node_modules/jwa": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+      "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer-equal-constant-time": "^1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/gtoken/node_modules/jws": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
+      "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+      "license": "MIT",
+      "dependencies": {
+        "jwa": "^2.0.0",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/helmet": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz",
+      "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/https-proxy-agent/node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/https-proxy-agent/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ignore-by-default": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+      "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/ip-address": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+      "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "license": "MIT"
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "license": "ISC"
+    },
+    "node_modules/jackspeak": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/cliui": "^8.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      },
+      "optionalDependencies": {
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
+    "node_modules/json-bigint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+      "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bignumber.js": "^9.0.0"
+      }
+    },
+    "node_modules/jsonwebtoken": {
+      "version": "9.0.2",
+      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+      "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+      "license": "MIT",
+      "dependencies": {
+        "jws": "^3.2.2",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.1.1",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=12",
+        "npm": ">=6"
+      }
+    },
+    "node_modules/jsonwebtoken/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/jwa": {
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+      "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer-equal-constant-time": "^1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jws": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+      "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+      "license": "MIT",
+      "dependencies": {
+        "jwa": "^1.4.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/kareem": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
+      "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/kuler": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
+      "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
+      "license": "MIT"
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+      "license": "MIT"
+    },
+    "node_modules/logform": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
+      "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@colors/colors": "1.6.0",
+        "@types/triple-beam": "^1.3.2",
+        "fecha": "^4.2.0",
+        "ms": "^2.1.1",
+        "safe-stable-stringify": "^2.3.1",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/logform/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "license": "ISC"
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/memory-pager": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+      "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/moment": {
+      "version": "2.30.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+      "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/mongodb": {
+      "version": "5.9.2",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz",
+      "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bson": "^5.5.0",
+        "mongodb-connection-string-url": "^2.6.0",
+        "socks": "^2.7.1"
+      },
+      "engines": {
+        "node": ">=14.20.1"
+      },
+      "optionalDependencies": {
+        "@mongodb-js/saslprep": "^1.1.0"
+      },
+      "peerDependencies": {
+        "@aws-sdk/credential-providers": "^3.188.0",
+        "@mongodb-js/zstd": "^1.0.0",
+        "kerberos": "^1.0.0 || ^2.0.0",
+        "mongodb-client-encryption": ">=2.3.0 <3",
+        "snappy": "^7.2.2"
+      },
+      "peerDependenciesMeta": {
+        "@aws-sdk/credential-providers": {
+          "optional": true
+        },
+        "@mongodb-js/zstd": {
+          "optional": true
+        },
+        "kerberos": {
+          "optional": true
+        },
+        "mongodb-client-encryption": {
+          "optional": true
+        },
+        "snappy": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/mongodb-connection-string-url": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
+      "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/whatwg-url": "^8.2.1",
+        "whatwg-url": "^11.0.0"
+      }
+    },
+    "node_modules/mongoose": {
+      "version": "7.8.7",
+      "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.8.7.tgz",
+      "integrity": "sha512-5Bo4CrUxrPITrhMKsqUTOkXXo2CoRC5tXxVQhnddCzqDMwRXfyStrxj1oY865g8gaekSBhxAeNkYyUSJvGm9Hw==",
+      "license": "MIT",
+      "dependencies": {
+        "bson": "^5.5.0",
+        "kareem": "2.5.1",
+        "mongodb": "5.9.2",
+        "mpath": "0.9.0",
+        "mquery": "5.0.0",
+        "ms": "2.1.3",
+        "sift": "16.0.1"
+      },
+      "engines": {
+        "node": ">=14.20.1"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mongoose"
+      }
+    },
+    "node_modules/mongoose/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/mpath": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
+      "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/mquery": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
+      "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "4.x"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/mquery/node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/mquery/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+      "license": "MIT"
+    },
+    "node_modules/multer": {
+      "version": "1.4.5-lts.2",
+      "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
+      "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
+      "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.",
+      "license": "MIT",
+      "dependencies": {
+        "append-field": "^1.0.0",
+        "busboy": "^1.0.0",
+        "concat-stream": "^1.5.2",
+        "mkdirp": "^0.5.4",
+        "object-assign": "^4.1.1",
+        "type-is": "^1.6.4",
+        "xtend": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/negotiator": {
+      "version": "0.6.4",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+      "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/node-cron": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz",
+      "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==",
+      "license": "ISC",
+      "dependencies": {
+        "uuid": "8.3.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/node-domexception": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+      "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+      "deprecated": "Use your platform's native DOMException instead",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "github",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.5.0"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+      "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+      "license": "MIT",
+      "dependencies": {
+        "data-uri-to-buffer": "^4.0.0",
+        "fetch-blob": "^3.1.4",
+        "formdata-polyfill": "^4.0.10"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/node-fetch"
+      }
+    },
+    "node_modules/nodemon": {
+      "version": "3.1.11",
+      "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz",
+      "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chokidar": "^3.5.2",
+        "debug": "^4",
+        "ignore-by-default": "^1.0.1",
+        "minimatch": "^3.1.2",
+        "pstree.remy": "^1.1.8",
+        "semver": "^7.5.3",
+        "simple-update-notifier": "^2.0.0",
+        "supports-color": "^5.5.0",
+        "touch": "^3.1.0",
+        "undefsafe": "^2.0.5"
+      },
+      "bin": {
+        "nodemon": "bin/nodemon.js"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nodemon"
+      }
+    },
+    "node_modules/nodemon/node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/nodemon/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/nodemon/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nodemon/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/on-headers": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+      "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/one-time": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
+      "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
+      "license": "MIT",
+      "dependencies": {
+        "fn.name": "1.x.x"
+      }
+    },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+      "license": "BlueOak-1.0.0"
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-scurry": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "0.1.12",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+      "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+      "license": "MIT"
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "license": "MIT"
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/pstree.remy": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+      "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.13.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+      "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.0.6"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "license": "MIT",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/readable-stream/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "license": "MIT"
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "5.0.10",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+      "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^10.3.7"
+      },
+      "bin": {
+        "rimraf": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+      "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/semver": {
+      "version": "7.7.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+      "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/send": {
+      "version": "0.19.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+      "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/send/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/serve-static": {
+      "version": "1.16.2",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+      "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
+      "license": "MIT",
+      "dependencies": {
+        "encodeurl": "~2.0.0",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.19.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shell-quote": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
+      "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/sift": {
+      "version": "16.0.1",
+      "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz",
+      "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==",
+      "license": "MIT"
+    },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/simple-update-notifier": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+      "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.3"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/smart-buffer": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/socket.io": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
+      "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
+      "license": "MIT",
+      "dependencies": {
+        "accepts": "~1.3.4",
+        "base64id": "~2.0.0",
+        "cors": "~2.8.5",
+        "debug": "~4.3.2",
+        "engine.io": "~6.6.0",
+        "socket.io-adapter": "~2.5.2",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.2.0"
+      }
+    },
+    "node_modules/socket.io-adapter": {
+      "version": "2.5.5",
+      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
+      "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "~4.3.4",
+        "ws": "~8.17.1"
+      }
+    },
+    "node_modules/socket.io-adapter/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io-adapter/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+      "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-parser/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io-parser/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/socket.io/node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/socket.io/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/socks": {
+      "version": "2.8.7",
+      "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
+      "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
+      "license": "MIT",
+      "dependencies": {
+        "ip-address": "^10.0.1",
+        "smart-buffer": "^4.2.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/sparse-bitfield": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+      "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "memory-pager": "^1.0.2"
+      }
+    },
+    "node_modules/spawn-command": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
+      "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==",
+      "dev": true
+    },
+    "node_modules/stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/streamsearch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/string_decoder/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "license": "MIT"
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width-cjs": {
+      "name": "string-width",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi-cjs": {
+      "name": "strip-ansi",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/text-hex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
+      "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
+      "license": "MIT"
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/touch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+      "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "nodetouch": "bin/nodetouch.js"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+      "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+      "license": "MIT",
+      "dependencies": {
+        "punycode": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "tree-kill": "cli.js"
+      }
+    },
+    "node_modules/triple-beam": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
+      "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "dev": true,
+      "license": "0BSD"
+    },
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "license": "MIT",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+      "license": "MIT"
+    },
+    "node_modules/undefsafe": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+      "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/undici-types": {
+      "version": "7.16.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+      "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+      "license": "MIT"
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT"
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/web-streams-polyfill": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+      "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/whatwg-url": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+      "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "^3.0.0",
+        "webidl-conversions": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/winston": {
+      "version": "3.18.3",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz",
+      "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==",
+      "license": "MIT",
+      "dependencies": {
+        "@colors/colors": "^1.6.0",
+        "@dabh/diagnostics": "^2.0.8",
+        "async": "^3.2.3",
+        "is-stream": "^2.0.0",
+        "logform": "^2.7.0",
+        "one-time": "^1.0.0",
+        "readable-stream": "^3.4.0",
+        "safe-stable-stringify": "^2.3.1",
+        "stack-trace": "0.0.x",
+        "triple-beam": "^1.3.0",
+        "winston-transport": "^4.9.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/winston-transport": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
+      "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
+      "license": "MIT",
+      "dependencies": {
+        "logform": "^2.7.0",
+        "readable-stream": "^3.6.2",
+        "triple-beam": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      }
+    },
+    "node_modules/winston-transport/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/winston/node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs": {
+      "name": "wrap-ansi",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7896ca5
--- /dev/null
+++ b/package.json
@@ -0,0 +1,48 @@
+{
+  "name": "dear-bubbe-admin",
+  "version": "1.0.0",
+  "description": "Admin Dashboard for Dear Bubbe AI with Real-time Monitoring and Alert Systems",
+  "main": "backend/server.js",
+  "scripts": {
+    "dev": "concurrently \"npm run backend\" \"npm run frontend\"",
+    "backend": "nodemon backend/server.js",
+    "frontend": "cd frontend && npm start",
+    "build": "cd frontend && npm run build",
+    "start": "node backend/server.js",
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "setup": "npm run setup:backend && npm run setup:frontend",
+    "setup:backend": "cd backend && npm install",
+    "setup:frontend": "cd frontend && npm install"
+  },
+  "dependencies": {
+    "axios": "^1.6.0",
+    "bcryptjs": "^2.4.3",
+    "compression": "^1.8.1",
+    "cors": "^2.8.5",
+    "dotenv": "^16.6.1",
+    "express": "^4.21.2",
+    "express-rate-limit": "^7.1.2",
+    "google-auth-library": "^10.5.0",
+    "helmet": "^7.2.0",
+    "jsonwebtoken": "^9.0.2",
+    "moment": "^2.29.4",
+    "mongoose": "^7.5.2",
+    "multer": "^1.4.5-lts.1",
+    "node-cron": "^3.0.2",
+    "socket.io": "^4.8.1",
+    "winston": "^3.18.3"
+  },
+  "devDependencies": {
+    "concurrently": "^8.2.2",
+    "nodemon": "^3.0.1"
+  },
+  "keywords": [
+    "admin",
+    "dashboard",
+    "monitoring",
+    "alerts",
+    "dear-bubbe"
+  ],
+  "author": "DW-Agents Team",
+  "license": "ISC"
+}
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..df58742
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
+<rect width="32" height="32" rx="6" fill="#10b981"/>
+<text x="50%" y="55%" text-anchor="middle" dominant-baseline="middle" font-size="20" font-family="Apple Color Emoji, Segoe UI Emoji, sans-serif" fill="white">D</text>
+</svg>
\ No newline at end of file
diff --git a/public/instagram-1763680784110.png b/public/instagram-1763680784110.png
new file mode 100644
index 0000000..0b8f426
Binary files /dev/null and b/public/instagram-1763680784110.png differ
diff --git a/public/instagram-1763680819710.png b/public/instagram-1763680819710.png
new file mode 100644
index 0000000..0b8f426
Binary files /dev/null and b/public/instagram-1763680819710.png differ
diff --git a/public/instagram-1763680822723.png b/public/instagram-1763680822723.png
new file mode 100644
index 0000000..18dc87e
Binary files /dev/null and b/public/instagram-1763680822723.png differ
diff --git a/public/instagram-1763680825914.png b/public/instagram-1763680825914.png
new file mode 100644
index 0000000..f9aeb51
Binary files /dev/null and b/public/instagram-1763680825914.png differ
diff --git a/public/instagram-1763680829181.png b/public/instagram-1763680829181.png
new file mode 100644
index 0000000..af8682a
Binary files /dev/null and b/public/instagram-1763680829181.png differ
diff --git a/public/instagram-1763680832325.png b/public/instagram-1763680832325.png
new file mode 100644
index 0000000..5408f1a
Binary files /dev/null and b/public/instagram-1763680832325.png differ
diff --git a/public/instagram-1763680835466.png b/public/instagram-1763680835466.png
new file mode 100644
index 0000000..a2ef56f
Binary files /dev/null and b/public/instagram-1763680835466.png differ
diff --git a/public/instagram-1763680838580.png b/public/instagram-1763680838580.png
new file mode 100644
index 0000000..1353cae
Binary files /dev/null and b/public/instagram-1763680838580.png differ
diff --git a/public/instagram-1763680841833.png b/public/instagram-1763680841833.png
new file mode 100644
index 0000000..5408f1a
Binary files /dev/null and b/public/instagram-1763680841833.png differ
diff --git a/public/instagram-1763680844930.png b/public/instagram-1763680844930.png
new file mode 100644
index 0000000..d998283
Binary files /dev/null and b/public/instagram-1763680844930.png differ
diff --git a/public/instagram-1763680848213.png b/public/instagram-1763680848213.png
new file mode 100644
index 0000000..d6189ec
Binary files /dev/null and b/public/instagram-1763680848213.png differ
diff --git a/public/instagram-1763680895477.png b/public/instagram-1763680895477.png
new file mode 100644
index 0000000..106f035
Binary files /dev/null and b/public/instagram-1763680895477.png differ
diff --git a/public/instagram-1763681128801.png b/public/instagram-1763681128801.png
new file mode 100644
index 0000000..1353cae
Binary files /dev/null and b/public/instagram-1763681128801.png differ
diff --git a/public/instagram-1763681171228.png b/public/instagram-1763681171228.png
new file mode 100644
index 0000000..8624c83
Binary files /dev/null and b/public/instagram-1763681171228.png differ
diff --git a/public/instagram-1763682870613.png b/public/instagram-1763682870613.png
new file mode 100644
index 0000000..106f035
Binary files /dev/null and b/public/instagram-1763682870613.png differ
diff --git a/public/instagram-1763684539865.png b/public/instagram-1763684539865.png
new file mode 100644
index 0000000..8ae6b8f
Binary files /dev/null and b/public/instagram-1763684539865.png differ
diff --git a/public/instagram-1763684730100.png b/public/instagram-1763684730100.png
new file mode 100644
index 0000000..ad9dd36
Binary files /dev/null and b/public/instagram-1763684730100.png differ
diff --git a/public/instagram-1763685242269.png b/public/instagram-1763685242269.png
new file mode 100644
index 0000000..1df33de
Binary files /dev/null and b/public/instagram-1763685242269.png differ
diff --git a/public/instagram-1763685682137.png b/public/instagram-1763685682137.png
new file mode 100644
index 0000000..df4d366
Binary files /dev/null and b/public/instagram-1763685682137.png differ
diff --git a/public/instagram-1763686094031.png b/public/instagram-1763686094031.png
new file mode 100644
index 0000000..7069272
Binary files /dev/null and b/public/instagram-1763686094031.png differ
diff --git a/public/instagram-caption-1763681171228.txt b/public/instagram-caption-1763681171228.txt
new file mode 100644
index 0000000..e246c66
--- /dev/null
+++ b/public/instagram-caption-1763681171228.txt
@@ -0,0 +1,7 @@
+Today's anonymous question:
+
+"Make matso baii chicken soup. It heals  a cold."
+
+🥯 What would Bubbe say? Find out at bubbe.ai (link in bio)
+
+#BubbeAI #JewishGrandma #AIAdvice #BubbeWisdom #AskBubbe #NoFilter #JewishHumor #GrandmaKnowsBest #LifeAdvice #TechMeetsTraition
\ No newline at end of file
diff --git a/public/instagram-caption.txt b/public/instagram-caption.txt
new file mode 100644
index 0000000..c490277
--- /dev/null
+++ b/public/instagram-caption.txt
@@ -0,0 +1,7 @@
+Someone needs Bubbe's wisdom:
+
+"My neighbor is too noisy"
+
+🥯 What would Bubbe say? Find out at bubbe.ai (link in bio)
+
+#BubbeAI #JewishGrandma #AIAdvice #BubbeWisdom #AskBubbe #JewishHumor #GrandmaKnowsBest #GrandmaAdvice #MotivationMonday #ModernBubbe
\ No newline at end of file
diff --git a/public/instagram-latest-caption.txt b/public/instagram-latest-caption.txt
new file mode 100644
index 0000000..d1351d5
--- /dev/null
+++ b/public/instagram-latest-caption.txt
@@ -0,0 +1,7 @@
+Someone asked Bubbe for advice today...
+
+"My kids never call me"
+
+🥯 What would Bubbe say? Find out at bubbe.ai (link in bio)
+
+#BubbeAI #JewishGrandma #AIAdvice #BubbeWisdom #AskBubbe #MotivationMonday #WisdomWednesday #RealTalk #ThrowbackThursday #JewishHumor
\ No newline at end of file
diff --git a/public/instagram-post.html b/public/instagram-post.html
new file mode 100644
index 0000000..331abe4
--- /dev/null
+++ b/public/instagram-post.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Instagram Post Ready</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <style>
+        body {
+            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+            max-width: 600px;
+            margin: 0 auto;
+            padding: 20px;
+            background: #fafafa;
+        }
+        .container {
+            background: white;
+            border-radius: 8px;
+            padding: 20px;
+            box-shadow: 0 1px 3px rgba(0,0,0,0.12);
+        }
+        h1 {
+            color: #262626;
+            font-size: 24px;
+            margin-bottom: 20px;
+        }
+        .image-container {
+            width: 100%;
+            margin-bottom: 20px;
+        }
+        img {
+            width: 100%;
+            border-radius: 8px;
+        }
+        .caption-box {
+            background: #f7f7f7;
+            padding: 15px;
+            border-radius: 8px;
+            white-space: pre-wrap;
+            font-size: 14px;
+            line-height: 1.5;
+            margin-bottom: 20px;
+        }
+        .download-btn {
+            display: inline-block;
+            background: #0095f6;
+            color: white;
+            padding: 12px 24px;
+            border-radius: 8px;
+            text-decoration: none;
+            font-weight: 600;
+            margin-right: 10px;
+        }
+        .instructions {
+            background: #fff3cd;
+            padding: 15px;
+            border-radius: 8px;
+            margin-top: 20px;
+        }
+        .instructions h2 {
+            margin-top: 0;
+            color: #856404;
+        }
+        .instructions ol {
+            margin: 10px 0;
+            padding-left: 20px;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <h1>📸 Instagram Post Ready!</h1>
+        
+        <div class="image-container">
+            <img src="/instagram-1763681171228.png" alt="Instagram Post">
+        </div>
+        
+        <h2>Caption:</h2>
+        <div class="caption-box">Today's anonymous question:
+
+"Make matso baii chicken soup. It heals  a cold."
+
+🥯 What would Bubbe say? Find out at bubbe.ai (link in bio)
+
+#BubbeAI #JewishGrandma #AIAdvice #BubbeWisdom #AskBubbe #NoFilter #JewishHumor #GrandmaKnowsBest #LifeAdvice #TechMeetsTraition</div>
+        
+        <a href="/instagram-1763681171228.png" download="instagram-post.png" class="download-btn">⬇️ Download Image</a>
+        <a href="/instagram-caption-1763681171228.txt" download="instagram-caption.txt" class="download-btn">📋 Download Caption</a>
+        
+        <div class="instructions">
+            <h2>📱 How to Post:</h2>
+            <ol>
+                <li>Download the image above</li>
+                <li>Open Instagram app or <a href="https://instagram.com" target="_blank">instagram.com</a></li>
+                <li>Click "New Post" (+)</li>
+                <li>Upload the downloaded image</li>
+                <li>Copy and paste the caption</li>
+                <li>Share!</li>
+            </ol>
+            
+            <h2>🔗 Quick Links:</h2>
+            <ul>
+                <li><a href="https://instagram.com" target="_blank">Instagram Web</a></li>
+                <li><a href="https://business.facebook.com/creatorstudio" target="_blank">Creator Studio (Schedule Posts)</a></li>
+                <li><a href="https://later.com" target="_blank">Later (Free Scheduling)</a></li>
+            </ul>
+        </div>
+        
+        <p style="text-align: center; margin-top: 30px; color: #8e8e8e;">
+            Generated at 11/20/2025, 11:26:11 PM
+        </p>
+    </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/public/instagram-schedule.json b/public/instagram-schedule.json
new file mode 100644
index 0000000..ddc07a9
--- /dev/null
+++ b/public/instagram-schedule.json
@@ -0,0 +1,15 @@
+{
+  "posts": [
+    {
+      "text": "Someone asked Bubbe for advice today...\n\n\"My kids never call me\"\n\n🥯 What would Bubbe say? Find out at bubbe.ai (link in bio)\n\n#BubbeAI #JewishGrandma #AIAdvice #BubbeWisdom #AskBubbe #MotivationMonday #WisdomWednesday #RealTalk #ThrowbackThursday #JewishHumor",
+      "media": {
+        "photo": "http://45.61.58.125:7902/instagram-1763684730100.png",
+        "thumbnail": "http://45.61.58.125:7902/instagram-1763684730100.png"
+      },
+      "platforms": [
+        "instagram"
+      ],
+      "scheduled_at": "2025-11-21T02:25:30.212Z"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/public/instagram-webhook.json b/public/instagram-webhook.json
new file mode 100644
index 0000000..58573e3
--- /dev/null
+++ b/public/instagram-webhook.json
@@ -0,0 +1,19 @@
+{
+  "platform": "instagram",
+  "image_url": "http://45.61.58.125:7902/instagram-1763684730100.png",
+  "caption": "Someone asked Bubbe for advice today...\n\n\"My kids never call me\"\n\n🥯 What would Bubbe say? Find out at bubbe.ai (link in bio)\n\n#BubbeAI #JewishGrandma #AIAdvice #BubbeWisdom #AskBubbe #MotivationMonday #WisdomWednesday #RealTalk #ThrowbackThursday #JewishHumor",
+  "hashtags": [
+    "#BubbeAI",
+    "#JewishGrandma",
+    "#AIAdvice",
+    "#BubbeWisdom",
+    "#AskBubbe",
+    "#MotivationMonday",
+    "#WisdomWednesday",
+    "#RealTalk",
+    "#ThrowbackThursday",
+    "#JewishHumor"
+  ],
+  "timestamp": "2025-11-21T00:25:30.211Z",
+  "webhook_url": "http://45.61.58.125:7902/instagram-webhook.json"
+}
\ No newline at end of file
diff --git a/setup-admin.sh b/setup-admin.sh
new file mode 100755
index 0000000..9e0bffa
--- /dev/null
+++ b/setup-admin.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+
+# Dear Bubbe Admin Dashboard Setup Script
+echo "🚀 Setting up Dear Bubbe Admin Dashboard..."
+
+# Create necessary directories
+mkdir -p logs reports exports uploads backend/logs
+
+# Install backend dependencies
+echo "📦 Installing backend dependencies..."
+npm install
+
+# Install frontend dependencies
+echo "📦 Installing frontend dependencies..."
+cd frontend
+npm install
+cd ..
+
+# Build frontend for production
+echo "🏗️  Building frontend..."
+cd frontend
+npm run build
+cd ..
+
+# Check if MongoDB is running
+echo "🔍 Checking MongoDB status..."
+if systemctl is-active --quiet mongod; then
+    echo "✅ MongoDB is running"
+else
+    echo "⚠️  MongoDB is not running. Starting MongoDB..."
+    sudo systemctl start mongod
+    sudo systemctl enable mongod
+    
+    # Wait for MongoDB to start
+    sleep 3
+    
+    if systemctl is-active --quiet mongod; then
+        echo "✅ MongoDB started successfully"
+    else
+        echo "❌ Failed to start MongoDB. Please start it manually."
+        exit 1
+    fi
+fi
+
+# Create MongoDB database and collections
+echo "🗃️  Setting up MongoDB database..."
+mongo << 'EOF'
+use dear-bubbe-admin;
+
+// Create collections with indexes
+db.alerts.createIndex({ timestamp: -1 });
+db.alerts.createIndex({ "userInfo.ip": 1, timestamp: -1 });
+db.alerts.createIndex({ type: 1, severity: 1 });
+db.alerts.createIndex({ status: 1, timestamp: -1 });
+
+db.users.createIndex({ userId: 1 }, { unique: true });
+db.users.createIndex({ "security.blocked": 1 });
+db.users.createIndex({ "security.riskLevel": 1 });
+db.users.createIndex({ "stats.lastVisit": -1 });
+
+db.sessions.createIndex({ sessionId: 1 }, { unique: true });
+db.sessions.createIndex({ userId: 1, startTime: -1 });
+db.sessions.createIndex({ ended: 1, lastActivity: -1 });
+
+db.conversations.createIndex({ timestamp: -1 });
+db.conversations.createIndex({ userId: 1, timestamp: -1 });
+db.conversations.createIndex({ topics: 1 });
+
+print("Database and indexes created successfully");
+exit
+EOF
+
+# Set up firewall rules
+echo "🔒 Setting up firewall rules..."
+sudo ufw allow 3015/tcp
+echo "✅ Port 3015 opened for admin dashboard"
+
+# Set proper permissions
+echo "🔑 Setting up permissions..."
+chmod +x backend/server.js 2>/dev/null || true
+chmod +x setup-admin.sh
+chmod -R 755 logs reports exports uploads 2>/dev/null || true
+
+# Copy built frontend to backend public directory
+if [ -d "frontend/build" ]; then
+    echo "📁 Copying built frontend..."
+    mkdir -p backend/public
+    cp -r frontend/build/* backend/public/
+    echo "✅ Frontend copied to backend"
+fi
+
+# Start the admin dashboard with PM2
+echo "🚀 Starting Dear Bubbe Admin Dashboard..."
+pm2 start ecosystem.config.js
+
+# Check if service is running
+sleep 3
+if pm2 list | grep -q "dear-bubbe-admin.*online"; then
+    echo ""
+    echo "✅ SUCCESS! Dear Bubbe Admin Dashboard is running!"
+    echo ""
+    echo "📊 Dashboard URL: http://45.61.58.125:3015"
+    echo "🔑 Default Login:"
+    echo "   Username: admin"
+    echo "   Password: password"
+    echo ""
+    echo "📋 Management Commands:"
+    echo "   pm2 status dear-bubbe-admin  - Check status"
+    echo "   pm2 logs dear-bubbe-admin    - View logs"
+    echo "   pm2 restart dear-bubbe-admin - Restart service"
+    echo "   pm2 stop dear-bubbe-admin    - Stop service"
+    echo ""
+    echo "🔧 Configuration file: /root/Projects/dear-bubbe-admin/.env"
+    echo ""
+    echo "⚠️  IMPORTANT SECURITY NOTES:"
+    echo "   1. Change default admin password immediately"
+    echo "   2. Update JWT_SECRET in production"
+    echo "   3. Configure HTTPS for production use"
+    echo "   4. Set up proper MongoDB authentication"
+    echo ""
+else
+    echo "❌ Failed to start admin dashboard"
+    echo "📝 Check logs with: pm2 logs dear-bubbe-admin"
+    exit 1
+fi
\ No newline at end of file

(oldest)  ·  back to Dear Bubbe Admin  ·  fix: add rel=noopener noreferrer to target=_blank links; add a88b6fd →