ids.alfacom.it/shared/schema.ts
marco370 cbd03d9e64 Add network analytics and live dashboard features
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
2025-11-22 11:34:36 +00:00

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>;