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
198 lines
7.5 KiB
TypeScript
198 lines
7.5 KiB
TypeScript
import { sql, relations } from "drizzle-orm";
|
|
import { pgTable, text, varchar, integer, timestamp, decimal, boolean, index, bigint } from "drizzle-orm/pg-core";
|
|
import { createInsertSchema } from "drizzle-zod";
|
|
import { z } from "zod";
|
|
|
|
// Router MikroTik configuration
|
|
export const routers = pgTable("routers", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
name: text("name").notNull(),
|
|
ipAddress: text("ip_address").notNull().unique(),
|
|
apiPort: integer("api_port").notNull().default(8728),
|
|
username: text("username").notNull(),
|
|
password: text("password").notNull(),
|
|
enabled: boolean("enabled").notNull().default(true),
|
|
lastSync: timestamp("last_sync"),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
});
|
|
|
|
// Network logs from MikroTik (syslog)
|
|
export const networkLogs = pgTable("network_logs", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
routerName: text("router_name").notNull(), // Hostname dal syslog
|
|
timestamp: timestamp("timestamp").notNull(),
|
|
sourceIp: text("source_ip").notNull(),
|
|
sourcePort: integer("source_port"),
|
|
destinationIp: text("destination_ip"),
|
|
destinationPort: integer("destination_port"),
|
|
protocol: text("protocol"),
|
|
action: text("action"),
|
|
packetLength: integer("packet_length"),
|
|
rawMessage: text("raw_message"),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
}, (table) => ({
|
|
sourceIpIdx: index("source_ip_idx").on(table.sourceIp),
|
|
timestampIdx: index("timestamp_idx").on(table.timestamp),
|
|
routerNameIdx: index("router_name_idx").on(table.routerName),
|
|
}));
|
|
|
|
// Detected threats/anomalies
|
|
export const detections = pgTable("detections", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
sourceIp: text("source_ip").notNull(),
|
|
riskScore: decimal("risk_score", { precision: 5, scale: 2 }).notNull(),
|
|
confidence: decimal("confidence", { precision: 5, scale: 2 }).notNull(),
|
|
anomalyType: text("anomaly_type").notNull(),
|
|
reason: text("reason"),
|
|
logCount: integer("log_count").notNull(),
|
|
firstSeen: timestamp("first_seen").notNull(),
|
|
lastSeen: timestamp("last_seen").notNull(),
|
|
blocked: boolean("blocked").notNull().default(false),
|
|
blockedAt: timestamp("blocked_at"),
|
|
detectedAt: timestamp("detected_at").defaultNow().notNull(),
|
|
// Geolocation & AS info
|
|
country: text("country"),
|
|
countryCode: text("country_code"),
|
|
city: text("city"),
|
|
organization: text("organization"),
|
|
asNumber: text("as_number"),
|
|
asName: text("as_name"),
|
|
isp: text("isp"),
|
|
}, (table) => ({
|
|
sourceIpIdx: index("detection_source_ip_idx").on(table.sourceIp),
|
|
riskScoreIdx: index("risk_score_idx").on(table.riskScore),
|
|
detectedAtIdx: index("detected_at_idx").on(table.detectedAt),
|
|
countryIdx: index("country_idx").on(table.country),
|
|
}));
|
|
|
|
// Whitelist per IP fidati
|
|
export const whitelist = pgTable("whitelist", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
ipAddress: text("ip_address").notNull().unique(),
|
|
comment: text("comment"),
|
|
reason: text("reason"),
|
|
createdBy: text("created_by"),
|
|
active: boolean("active").notNull().default(true),
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
});
|
|
|
|
// ML Training history
|
|
export const trainingHistory = pgTable("training_history", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
modelVersion: text("model_version").notNull(),
|
|
recordsProcessed: integer("records_processed").notNull(),
|
|
featuresCount: integer("features_count").notNull(),
|
|
accuracy: decimal("accuracy", { precision: 5, scale: 2 }),
|
|
trainingDuration: integer("training_duration"),
|
|
status: text("status").notNull(),
|
|
notes: text("notes"),
|
|
trainedAt: timestamp("trained_at").defaultNow().notNull(),
|
|
});
|
|
|
|
// Network analytics - aggregazioni permanenti per statistiche long-term
|
|
export const networkAnalytics = pgTable("network_analytics", {
|
|
id: varchar("id").primaryKey().default(sql`gen_random_uuid()`),
|
|
date: timestamp("date", { mode: 'date' }).notNull(),
|
|
hour: integer("hour"), // NULL = giornaliera, 0-23 = oraria
|
|
|
|
// Traffico totale
|
|
totalPackets: integer("total_packets").notNull().default(0),
|
|
totalBytes: bigint("total_bytes", { mode: 'number' }).notNull().default(0),
|
|
uniqueIps: integer("unique_ips").notNull().default(0),
|
|
|
|
// Traffico normale (non anomalo)
|
|
normalPackets: integer("normal_packets").notNull().default(0),
|
|
normalBytes: bigint("normal_bytes", { mode: 'number' }).notNull().default(0),
|
|
normalUniqueIps: integer("normal_unique_ips").notNull().default(0),
|
|
topNormalIps: text("top_normal_ips"), // JSON: [{ip, packets, bytes, country}]
|
|
|
|
// Attacchi/Anomalie
|
|
attackPackets: integer("attack_packets").notNull().default(0),
|
|
attackBytes: bigint("attack_bytes", { mode: 'number' }).notNull().default(0),
|
|
attackUniqueIps: integer("attack_unique_ips").notNull().default(0),
|
|
attacksByCountry: text("attacks_by_country"), // JSON: {IT: 5, RU: 30}
|
|
attacksByType: text("attacks_by_type"), // JSON: {ddos: 10, port_scan: 5}
|
|
topAttackers: text("top_attackers"), // JSON: [{ip, country, risk_score, packets}]
|
|
|
|
// Dettagli geografici (tutto il traffico)
|
|
trafficByCountry: text("traffic_by_country"), // JSON: {IT: {normal: 100, attacks: 5}}
|
|
|
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
}, (table) => ({
|
|
dateHourIdx: index("network_analytics_date_hour_idx").on(table.date, table.hour),
|
|
dateIdx: index("network_analytics_date_idx").on(table.date),
|
|
}));
|
|
|
|
// Schema version tracking for database migrations
|
|
export const schemaVersion = pgTable("schema_version", {
|
|
id: integer("id").primaryKey().default(1),
|
|
version: integer("version").notNull().default(0),
|
|
appliedAt: timestamp("applied_at").defaultNow().notNull(),
|
|
description: text("description"),
|
|
});
|
|
|
|
// Relations
|
|
export const routersRelations = relations(routers, ({ many }) => ({
|
|
logs: many(networkLogs),
|
|
}));
|
|
|
|
// Rimossa relazione router (non più FK)
|
|
|
|
// Insert schemas
|
|
export const insertRouterSchema = createInsertSchema(routers).omit({
|
|
id: true,
|
|
createdAt: true,
|
|
lastSync: true,
|
|
});
|
|
|
|
export const insertNetworkLogSchema = createInsertSchema(networkLogs).omit({
|
|
id: true,
|
|
createdAt: true,
|
|
});
|
|
|
|
export const insertDetectionSchema = createInsertSchema(detections).omit({
|
|
id: true,
|
|
detectedAt: true,
|
|
});
|
|
|
|
export const insertWhitelistSchema = createInsertSchema(whitelist).omit({
|
|
id: true,
|
|
createdAt: true,
|
|
});
|
|
|
|
export const insertTrainingHistorySchema = createInsertSchema(trainingHistory).omit({
|
|
id: true,
|
|
trainedAt: true,
|
|
});
|
|
|
|
export const insertSchemaVersionSchema = createInsertSchema(schemaVersion).omit({
|
|
appliedAt: true,
|
|
});
|
|
|
|
export const insertNetworkAnalyticsSchema = createInsertSchema(networkAnalytics).omit({
|
|
id: true,
|
|
createdAt: true,
|
|
});
|
|
|
|
// Types
|
|
export type Router = typeof routers.$inferSelect;
|
|
export type InsertRouter = z.infer<typeof insertRouterSchema>;
|
|
|
|
export type NetworkLog = typeof networkLogs.$inferSelect;
|
|
export type InsertNetworkLog = z.infer<typeof insertNetworkLogSchema>;
|
|
|
|
export type Detection = typeof detections.$inferSelect;
|
|
export type InsertDetection = z.infer<typeof insertDetectionSchema>;
|
|
|
|
export type Whitelist = typeof whitelist.$inferSelect;
|
|
export type InsertWhitelist = z.infer<typeof insertWhitelistSchema>;
|
|
|
|
export type TrainingHistory = typeof trainingHistory.$inferSelect;
|
|
export type InsertTrainingHistory = z.infer<typeof insertTrainingHistorySchema>;
|
|
|
|
export type SchemaVersion = typeof schemaVersion.$inferSelect;
|
|
export type InsertSchemaVersion = z.infer<typeof insertSchemaVersionSchema>;
|
|
|
|
export type NetworkAnalytics = typeof networkAnalytics.$inferSelect;
|
|
export type InsertNetworkAnalytics = z.infer<typeof insertNetworkAnalyticsSchema>;
|