Introduce new network analytics capabilities with persistent storage, hourly and daily aggregations, and enhanced frontend visualizations. This includes API endpoints for retrieving analytics data, systemd services for automated aggregation, and UI updates for live and historical dashboards. Additionally, country flag emojis are now displayed on the detections page. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 3c14f651-7633-4128-8526-314b4942b3a0 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/oGXAoP7
273 lines
8.1 KiB
TypeScript
273 lines
8.1 KiB
TypeScript
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<Router[]>;
|
|
getRouterById(id: string): Promise<Router | undefined>;
|
|
createRouter(router: InsertRouter): Promise<Router>;
|
|
updateRouter(id: string, router: Partial<InsertRouter>): Promise<Router | undefined>;
|
|
deleteRouter(id: string): Promise<boolean>;
|
|
|
|
// Network Logs
|
|
getRecentLogs(limit: number): Promise<NetworkLog[]>;
|
|
getLogsByIp(sourceIp: string, limit: number): Promise<NetworkLog[]>;
|
|
createLog(log: InsertNetworkLog): Promise<NetworkLog>;
|
|
getLogsForTraining(limit: number, minTimestamp?: Date): Promise<NetworkLog[]>;
|
|
|
|
// Detections
|
|
getAllDetections(limit: number): Promise<Detection[]>;
|
|
getDetectionByIp(sourceIp: string): Promise<Detection | undefined>;
|
|
createDetection(detection: InsertDetection): Promise<Detection>;
|
|
updateDetection(id: string, detection: Partial<InsertDetection>): Promise<Detection | undefined>;
|
|
getUnblockedDetections(): Promise<Detection[]>;
|
|
|
|
// Whitelist
|
|
getAllWhitelist(): Promise<Whitelist[]>;
|
|
getWhitelistByIp(ipAddress: string): Promise<Whitelist | undefined>;
|
|
createWhitelist(whitelist: InsertWhitelist): Promise<Whitelist>;
|
|
deleteWhitelist(id: string): Promise<boolean>;
|
|
isWhitelisted(ipAddress: string): Promise<boolean>;
|
|
|
|
// Training History
|
|
getTrainingHistory(limit: number): Promise<TrainingHistory[]>;
|
|
createTrainingHistory(history: InsertTrainingHistory): Promise<TrainingHistory>;
|
|
getLatestTraining(): Promise<TrainingHistory | undefined>;
|
|
|
|
// Network Analytics
|
|
getAnalyticsByDateRange(startDate: Date, endDate: Date, hourly?: boolean): Promise<NetworkAnalytics[]>;
|
|
getRecentAnalytics(days: number, hourly?: boolean): Promise<NetworkAnalytics[]>;
|
|
|
|
// System
|
|
testConnection(): Promise<boolean>;
|
|
}
|
|
|
|
export class DatabaseStorage implements IStorage {
|
|
// Routers
|
|
async getAllRouters(): Promise<Router[]> {
|
|
return await db.select().from(routers).orderBy(desc(routers.createdAt));
|
|
}
|
|
|
|
async getRouterById(id: string): Promise<Router | undefined> {
|
|
const [router] = await db.select().from(routers).where(eq(routers.id, id));
|
|
return router || undefined;
|
|
}
|
|
|
|
async createRouter(insertRouter: InsertRouter): Promise<Router> {
|
|
const [router] = await db.insert(routers).values(insertRouter).returning();
|
|
return router;
|
|
}
|
|
|
|
async updateRouter(id: string, updateData: Partial<InsertRouter>): Promise<Router | undefined> {
|
|
const [router] = await db
|
|
.update(routers)
|
|
.set(updateData)
|
|
.where(eq(routers.id, id))
|
|
.returning();
|
|
return router || undefined;
|
|
}
|
|
|
|
async deleteRouter(id: string): Promise<boolean> {
|
|
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<NetworkLog[]> {
|
|
return await db
|
|
.select()
|
|
.from(networkLogs)
|
|
.orderBy(desc(networkLogs.timestamp))
|
|
.limit(limit);
|
|
}
|
|
|
|
async getLogsByIp(sourceIp: string, limit: number): Promise<NetworkLog[]> {
|
|
return await db
|
|
.select()
|
|
.from(networkLogs)
|
|
.where(eq(networkLogs.sourceIp, sourceIp))
|
|
.orderBy(desc(networkLogs.timestamp))
|
|
.limit(limit);
|
|
}
|
|
|
|
async createLog(insertLog: InsertNetworkLog): Promise<NetworkLog> {
|
|
const [log] = await db.insert(networkLogs).values(insertLog).returning();
|
|
return log;
|
|
}
|
|
|
|
async getLogsForTraining(limit: number, minTimestamp?: Date): Promise<NetworkLog[]> {
|
|
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(limit: number): Promise<Detection[]> {
|
|
return await db
|
|
.select()
|
|
.from(detections)
|
|
.orderBy(desc(detections.detectedAt))
|
|
.limit(limit);
|
|
}
|
|
|
|
async getDetectionByIp(sourceIp: string): Promise<Detection | undefined> {
|
|
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<Detection> {
|
|
const [detection] = await db
|
|
.insert(detections)
|
|
.values(insertDetection)
|
|
.returning();
|
|
return detection;
|
|
}
|
|
|
|
async updateDetection(
|
|
id: string,
|
|
updateData: Partial<InsertDetection>
|
|
): Promise<Detection | undefined> {
|
|
const [detection] = await db
|
|
.update(detections)
|
|
.set(updateData)
|
|
.where(eq(detections.id, id))
|
|
.returning();
|
|
return detection || undefined;
|
|
}
|
|
|
|
async getUnblockedDetections(): Promise<Detection[]> {
|
|
return await db
|
|
.select()
|
|
.from(detections)
|
|
.where(eq(detections.blocked, false))
|
|
.orderBy(desc(detections.riskScore));
|
|
}
|
|
|
|
// Whitelist
|
|
async getAllWhitelist(): Promise<Whitelist[]> {
|
|
return await db
|
|
.select()
|
|
.from(whitelist)
|
|
.where(eq(whitelist.active, true))
|
|
.orderBy(desc(whitelist.createdAt));
|
|
}
|
|
|
|
async getWhitelistByIp(ipAddress: string): Promise<Whitelist | undefined> {
|
|
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<Whitelist> {
|
|
const [item] = await db.insert(whitelist).values(insertWhitelist).returning();
|
|
return item;
|
|
}
|
|
|
|
async deleteWhitelist(id: string): Promise<boolean> {
|
|
const result = await db.delete(whitelist).where(eq(whitelist.id, id));
|
|
return result.rowCount !== null && result.rowCount > 0;
|
|
}
|
|
|
|
async isWhitelisted(ipAddress: string): Promise<boolean> {
|
|
const item = await this.getWhitelistByIp(ipAddress);
|
|
return item !== undefined;
|
|
}
|
|
|
|
// Training History
|
|
async getTrainingHistory(limit: number): Promise<TrainingHistory[]> {
|
|
return await db
|
|
.select()
|
|
.from(trainingHistory)
|
|
.orderBy(desc(trainingHistory.trainedAt))
|
|
.limit(limit);
|
|
}
|
|
|
|
async createTrainingHistory(insertHistory: InsertTrainingHistory): Promise<TrainingHistory> {
|
|
const [history] = await db
|
|
.insert(trainingHistory)
|
|
.values(insertHistory)
|
|
.returning();
|
|
return history;
|
|
}
|
|
|
|
async getLatestTraining(): Promise<TrainingHistory | undefined> {
|
|
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<NetworkAnalytics[]> {
|
|
const hourCondition = hourly
|
|
? sql`hour IS NOT NULL`
|
|
: sql`hour IS NULL`;
|
|
|
|
return await db
|
|
.select()
|
|
.from(networkAnalytics)
|
|
.where(
|
|
and(
|
|
gte(networkAnalytics.date, startDate),
|
|
sql`${networkAnalytics.date} <= ${endDate}`,
|
|
hourCondition
|
|
)
|
|
)
|
|
.orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour));
|
|
}
|
|
|
|
async getRecentAnalytics(days: number, hourly: boolean = false): Promise<NetworkAnalytics[]> {
|
|
const startDate = new Date();
|
|
startDate.setDate(startDate.getDate() - days);
|
|
|
|
return this.getAnalyticsByDateRange(startDate, new Date(), hourly);
|
|
}
|
|
|
|
async testConnection(): Promise<boolean> {
|
|
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();
|