import { routers, networkLogs, detections, whitelist, trainingHistory, networkAnalytics, type Router, type InsertRouter, type NetworkLog, type InsertNetworkLog, type Detection, type InsertDetection, type Whitelist, type InsertWhitelist, type TrainingHistory, type InsertTrainingHistory, type NetworkAnalytics, } from "@shared/schema"; import { db } from "./db"; import { eq, desc, and, gte, sql, inArray } from "drizzle-orm"; export interface IStorage { // Routers getAllRouters(): Promise; getRouterById(id: string): Promise; createRouter(router: InsertRouter): Promise; updateRouter(id: string, router: Partial): Promise; deleteRouter(id: string): Promise; // Network Logs getRecentLogs(limit: number): Promise; getLogsByIp(sourceIp: string, limit: number): Promise; createLog(log: InsertNetworkLog): Promise; getLogsForTraining(limit: number, minTimestamp?: Date): Promise; // Detections getAllDetections(options: { limit?: number; anomalyType?: string; minScore?: number; maxScore?: number; }): Promise; getDetectionByIp(sourceIp: string): Promise; createDetection(detection: InsertDetection): Promise; updateDetection(id: string, detection: Partial): Promise; getUnblockedDetections(): Promise; // Whitelist getAllWhitelist(): Promise; getWhitelistByIp(ipAddress: string): Promise; createWhitelist(whitelist: InsertWhitelist): Promise; deleteWhitelist(id: string): Promise; isWhitelisted(ipAddress: string): Promise; // Training History getTrainingHistory(limit: number): Promise; createTrainingHistory(history: InsertTrainingHistory): Promise; getLatestTraining(): Promise; // Network Analytics getAnalyticsByDateRange(startDate: Date, endDate: Date, hourly?: boolean): Promise; getRecentAnalytics(days: number, hourly?: boolean): Promise; // Dashboard Live Stats getLiveDashboardStats(hours: number): Promise<{ totalPackets: number; attackPackets: number; normalPackets: number; uniqueIps: number; attackUniqueIps: number; attacksByCountry: Record; attacksByType: Record; recentDetections: Detection[]; }>; // System testConnection(): Promise; } export class DatabaseStorage implements IStorage { // Routers async getAllRouters(): Promise { return await db.select().from(routers).orderBy(desc(routers.createdAt)); } async getRouterById(id: string): Promise { const [router] = await db.select().from(routers).where(eq(routers.id, id)); return router || undefined; } async createRouter(insertRouter: InsertRouter): Promise { const [router] = await db.insert(routers).values(insertRouter).returning(); return router; } async updateRouter(id: string, updateData: Partial): Promise { const [router] = await db .update(routers) .set(updateData) .where(eq(routers.id, id)) .returning(); return router || undefined; } async deleteRouter(id: string): Promise { const result = await db.delete(routers).where(eq(routers.id, id)); return result.rowCount !== null && result.rowCount > 0; } // Network Logs async getRecentLogs(limit: number): Promise { return await db .select() .from(networkLogs) .orderBy(desc(networkLogs.timestamp)) .limit(limit); } async getLogsByIp(sourceIp: string, limit: number): Promise { return await db .select() .from(networkLogs) .where(eq(networkLogs.sourceIp, sourceIp)) .orderBy(desc(networkLogs.timestamp)) .limit(limit); } async createLog(insertLog: InsertNetworkLog): Promise { const [log] = await db.insert(networkLogs).values(insertLog).returning(); return log; } async getLogsForTraining(limit: number, minTimestamp?: Date): Promise { const conditions = minTimestamp ? and(gte(networkLogs.timestamp, minTimestamp)) : undefined; return await db .select() .from(networkLogs) .where(conditions) .orderBy(desc(networkLogs.timestamp)) .limit(limit); } // Detections async getAllDetections(options: { limit?: number; anomalyType?: string; minScore?: number; maxScore?: number; }): Promise { const { limit = 500, anomalyType, minScore, maxScore } = options; // Build WHERE conditions const conditions = []; if (anomalyType) { conditions.push(eq(detections.anomalyType, anomalyType)); } if (minScore !== undefined) { conditions.push(gte(detections.riskScore, minScore.toString())); } if (maxScore !== undefined) { conditions.push(sql`${detections.riskScore}::numeric <= ${maxScore}`); } const query = db .select() .from(detections) .orderBy(desc(detections.detectedAt)) .limit(limit); if (conditions.length > 0) { return await query.where(and(...conditions)); } return await query; } async getDetectionByIp(sourceIp: string): Promise { const [detection] = await db .select() .from(detections) .where(eq(detections.sourceIp, sourceIp)) .orderBy(desc(detections.detectedAt)) .limit(1); return detection || undefined; } async createDetection(insertDetection: InsertDetection): Promise { const [detection] = await db .insert(detections) .values(insertDetection) .returning(); return detection; } async updateDetection( id: string, updateData: Partial ): Promise { const [detection] = await db .update(detections) .set(updateData) .where(eq(detections.id, id)) .returning(); return detection || undefined; } async getUnblockedDetections(): Promise { return await db .select() .from(detections) .where(eq(detections.blocked, false)) .orderBy(desc(detections.riskScore)); } // Whitelist async getAllWhitelist(): Promise { return await db .select() .from(whitelist) .where(eq(whitelist.active, true)) .orderBy(desc(whitelist.createdAt)); } async getWhitelistByIp(ipAddress: string): Promise { const [item] = await db .select() .from(whitelist) .where(and(eq(whitelist.ipAddress, ipAddress), eq(whitelist.active, true))); return item || undefined; } async createWhitelist(insertWhitelist: InsertWhitelist): Promise { const [item] = await db.insert(whitelist).values(insertWhitelist).returning(); return item; } async deleteWhitelist(id: string): Promise { const result = await db.delete(whitelist).where(eq(whitelist.id, id)); return result.rowCount !== null && result.rowCount > 0; } async isWhitelisted(ipAddress: string): Promise { const item = await this.getWhitelistByIp(ipAddress); return item !== undefined; } // Training History async getTrainingHistory(limit: number): Promise { return await db .select() .from(trainingHistory) .orderBy(desc(trainingHistory.trainedAt)) .limit(limit); } async createTrainingHistory(insertHistory: InsertTrainingHistory): Promise { const [history] = await db .insert(trainingHistory) .values(insertHistory) .returning(); return history; } async getLatestTraining(): Promise { const [history] = await db .select() .from(trainingHistory) .orderBy(desc(trainingHistory.trainedAt)) .limit(1); return history || undefined; } // Network Analytics async getAnalyticsByDateRange(startDate: Date, endDate: Date, hourly: boolean = false): Promise { const hourCondition = hourly ? sql`hour IS NOT NULL` : sql`hour IS NULL`; // DEBUG: Log query parameters console.log('[ANALYTICS QUERY]', { startDate: startDate.toISOString(), endDate: endDate.toISOString(), hourly, hourCondition: hourly ? 'NOT NULL' : 'NULL' }); const results = await db .select() .from(networkAnalytics) .where( and( gte(networkAnalytics.date, startDate), sql`${networkAnalytics.date} <= ${endDate}`, hourCondition ) ) .orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour)); console.log('[ANALYTICS RESULTS]', results.length, 'records found'); if (results.length > 0) { console.log('[ANALYTICS SAMPLE]', results[0]); } return results; } async getRecentAnalytics(days: number, hourly: boolean = false): Promise { const startDate = new Date(); startDate.setDate(startDate.getDate() - days); return this.getAnalyticsByDateRange(startDate, new Date(), hourly); } async getLiveDashboardStats(hours: number = 72) { const cutoffDate = new Date(); cutoffDate.setHours(cutoffDate.getHours() - hours); const analytics = await db .select() .from(networkAnalytics) .where( and( gte(networkAnalytics.date, cutoffDate), sql`hour IS NOT NULL` ) ) .orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour)); let totalPackets = 0; let attackPackets = 0; let normalPackets = 0; let uniqueIps = 0; let attackUniqueIps = 0; const attacksByCountry: Record = {}; const attacksByType: Record = {}; analytics.forEach(record => { totalPackets += record.totalPackets || 0; attackPackets += record.attackPackets || 0; normalPackets += record.normalPackets || 0; uniqueIps += record.uniqueIps || 0; attackUniqueIps += record.attackUniqueIps || 0; if (record.attacksByCountry) { try { const countries = JSON.parse(record.attacksByCountry); Object.entries(countries).forEach(([country, count]) => { attacksByCountry[country] = (attacksByCountry[country] || 0) + (count as number); }); } catch {} } if (record.attacksByType) { try { const types = JSON.parse(record.attacksByType); Object.entries(types).forEach(([type, count]) => { attacksByType[type] = (attacksByType[type] || 0) + (count as number); }); } catch {} } }); const recentDetections = await db .select() .from(detections) .where(gte(detections.detectedAt, cutoffDate)) .orderBy(desc(detections.detectedAt)) .limit(100); return { totalPackets, attackPackets, normalPackets, uniqueIps, attackUniqueIps, attacksByCountry, attacksByType, recentDetections, }; } async testConnection(): Promise { try { await db.execute(sql`SELECT 1`); return true; } catch (error) { console.error('[DB ERROR] Connection test failed:', error); return false; } } } export const storage = new DatabaseStorage();