Compare commits

..

No commits in common. "main" and "v1.0.121" have entirely different histories.

6 changed files with 403 additions and 738 deletions

View File

@ -1,85 +0,0 @@
tail -f /var/log/ids/backend.log
9:21:00 AM [express] GET /api/detections 304 in 19ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:21:01 AM [express] GET /api/services/status 200 in 29ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:21:02 AM [express] GET /api/routers 200 in 3ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","n…
9:21:12 AM [express] PUT /api/routers/c6904896-59dc-4060-952f-0e55df7dee4a 200 in 8ms :: {"id":"c690…
9:21:12 AM [express] GET /api/routers 304 in 2ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","n…
9:22:16 AM [express] POST /api/ml/block-all-critical 200 in 99ms :: {"message":"Nessun IP critico da…
9:24:17 AM [express] POST /api/ml/block-all-critical 200 in 75ms :: {"message":"Nessun IP critico da…
 Using standard PostgreSQL database
9:26:18 AM [express] serving on port 5000
✅ Database connection successful
9:28:19 AM [express] POST /api/ml/block-all-critical 200 in 93ms :: {"message":"Nessun IP critico da…
9:30:19 AM [express] POST /api/ml/block-all-critical 200 in 82ms :: {"message":"Nessun IP critico da…
9:30:25 AM [express] GET /api/training-history 200 in 6ms :: [{"id":"7570df54-8169-4fb2-abdc-e9c1bfa…
9:30:29 AM [express] GET /api/detections 200 in 24ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:30:29 AM [express] GET /api/whitelist 200 in 259ms :: {"items":[{"id":"49b5b9a9-4683-452c-a784-fc5…
9:30:37 AM [express] GET /api/services/status 200 in 22ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:30:39 AM [express] POST /api/services/ids-syslog-parser/start 500 in 22ms :: {"error":"Service con…
9:30:42 AM [express] GET /api/services/status 200 in 21ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:30:47 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:30:52 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:30:57 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:02 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:07 AM [express] GET /api/services/status 200 in 23ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:12 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:16 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:21 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:26 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:27 AM [express] GET /api/detections 304 in 11ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:31:27 AM [express] GET /api/whitelist 200 in 229ms :: {"items":[{"id":"91108765-7ef6-4e49-87d8-319…
9:31:30 AM [express] GET /api/public-lists 200 in 5ms :: [{"id":"2a835798-02a7-4129-a528-d39a026ad15…
9:31:32 AM [express] GET /api/services/status 200 in 19ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:37 AM [express] GET /api/services/status 200 in 20ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:42 AM [express] GET /api/services/status 200 in 19ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:47 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:52 AM [express] GET /api/services/status 200 in 17ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:31:57 AM [express] GET /api/services/status 200 in 21ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:02 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:07 AM [express] GET /api/services/status 200 in 24ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:12 AM [express] GET /api/services/status 200 in 24ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:17 AM [express] GET /api/services/status 200 in 23ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:25 AM [express] GET /api/services/status 200 in 2875ms :: {"services":{"mlBackend":{"name":"ML …
[BLOCK-ALL] Avvio blocco massivo: 11/11 IP con score >= 80 su 2 router
[BULK-BLOCK] Starting: 11 IPs on 2 routers (10.20.30.100, 185.203.24.2)
[BULK-BLOCK] Router 185.203.24.2: 124 IPs already in list (50ms)
9:32:30 AM [express] GET /api/services/status 200 in 31ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:35 AM [express] GET /api/services/status 200 in 31ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:40 AM [express] GET /api/services/status 200 in 46ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:32:46 AM [express] GET /api/services/status 200 in 75ms :: {"services":{"mlBackend":{"name":"ML Ba…
[MIKROTIK] Failed to get address-list from 10.20.30.100: This operation was aborted
[BULK-BLOCK] Router 10.20.30.100: 0 IPs already in list (20004ms)
[BULK-BLOCK] 0 already blocked, 11 new to block
9:32:51 AM [express] GET /api/services/status 200 in 40ms :: {"services":{"mlBackend":{"name":"ML Ba…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 87.17.182.32: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.98.164.31: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 109.115.163.146: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 2.118.225.238: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 74.125.99.38: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 192.178.112.96: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 74.125.111.102: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 212.14.142.214: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 89.96.215.146: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.39.230: This operation was aborted
9:32:56 AM [express] GET /api/services/status 200 in 44ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:01 AM [express] GET /api/services/status 200 in 46ms :: {"services":{"mlBackend":{"name":"ML Ba…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 104.19.148.8: This operation was aborted
[BULK-BLOCK] Progress: 11/11
[BULK-BLOCK] Router 10.20.30.100: 0 blocked, 11 failed, 0 skipped
[BULK-BLOCK] Router 185.203.24.2: 11 blocked, 0 failed, 0 skipped
[BULK-BLOCK] Done: 11 blocked, 0 failed, 0 skipped
[BLOCK-ALL] Database aggiornato: 11 IP marcati come bloccati
9:33:03 AM [express] POST /api/ml/block-all-critical 200 in 36137ms :: {"message":"Blocco massivo co…
9:33:07 AM [express] GET /api/services/status 200 in 1125ms :: {"services":{"mlBackend":{"name":"ML …
9:33:12 AM [express] GET /api/services/status 200 in 40ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:17 AM [express] GET /api/services/status 200 in 56ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:22 AM [express] GET /api/services/status 200 in 58ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:27 AM [express] GET /api/services/status 200 in 57ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:32 AM [express] GET /api/services/status 200 in 57ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:37 AM [express] GET /api/services/status 200 in 52ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:42 AM [express] GET /api/services/status 200 in 77ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:48 AM [express] GET /api/services/status 200 in 50ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:53 AM [express] GET /api/services/status 200 in 72ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:33:58 AM [express] GET /api/services/status 200 in 69ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:34:03 AM [express] GET /api/services/status 200 in 64ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:34:08 AM [express] GET /api/services/status 200 in 79ms :: {"services":{"mlBackend":{"name":"ML Ba…

View File

@ -1,224 +0,0 @@
tail -f /var/log/ids/backend.log
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 188.8.66.34: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.101.193.229: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 109.205.211.40: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.59.33.110: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 151.73.209.26: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.8.248.29: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 217.202.28.18: This operation was aborted
 Using standard PostgreSQL database
9:13:09 AM [express] serving on port 5000
✅ Database connection successful
[BLOCK-ALL] Avvio blocco massivo: 100/100 IP con score >= 80 su 2 router
[BULK-BLOCK] Starting: 100 IPs on 2 routers (10.20.30.100, 185.203.24.2)
[BULK-BLOCK] Router 185.203.24.2: 74 IPs already in list (38ms)
9:14:15 AM [express] GET /api/detections 200 in 17ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:14:15 AM [express] GET /api/services/status 200 in 42ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:14:20 AM [express] GET /api/detections 200 in 19ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:14:20 AM [express] GET /api/services/status 200 in 20ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:14:25 AM [express] GET /api/detections 200 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:14:25 AM [express] GET /api/whitelist 200 in 222ms :: {"items":[{"id":"4b7a60b4-b47c-431e-8efa-864…
[MIKROTIK] Failed to get address-list from 10.20.30.100: This operation was aborted
[BULK-BLOCK] Router 10.20.30.100: 0 IPs already in list (20004ms)
[BULK-BLOCK] 0 already blocked, 100 new to block
9:14:35 AM [express] GET /api/detections 200 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:14:39 AM [express] GET /api/training-history 200 in 5ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.73.24.49: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 104.16.248.249: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 77.39.130.185: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 104.16.249.249: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 104.18.36.146: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 193.205.185.20: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 50.93.53.165: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 91.208.175.82: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 74.0.42.209: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8005ms for IP 79.6.115.203: This operation was aborted
9:14:50 AM [express] GET /api/detections 200 in 31ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:14:50 AM [express] GET /api/whitelist 200 in 246ms :: {"items":[{"id":"49b5b9a9-4683-452c-a784-fc5…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 142.250.181.168: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 195.32.127.72: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.150.41.42: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 212.183.171.12: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 78.134.17.204: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 62.94.77.251: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.73.24.107: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 74.125.45.108: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 2.42.47.211: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 89.97.31.234: This operation was aborted
9:14:52 AM [express] GET /api/dashboard/live 200 in 12ms :: {"totalPackets":75520962,"attackPackets"…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 31.197.212.130: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 44.197.141.31: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 146.75.61.140: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.150.200.81: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 101.56.92.15: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 2.33.192.82: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.41.104.237: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 77.39.220.111: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 185.96.131.245: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8004ms for IP 142.250.181.164: This operation was aborted
9:15:00 AM [express] GET /api/routers 200 in 16ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","…
9:15:00 AM [express] GET /api/detections 200 in 24ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:15:00 AM [express] GET /api/services/status 200 in 28ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:15:05 AM [express] GET /api/detections 200 in 11ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:15:05 AM [express] GET /api/services/status 200 in 18ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:15:06 AM [express] GET /api/stats 200 in 5385ms :: {"routers":{"total":2,"enabled":2},"detections"…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 45.146.216.240: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 62.112.11.234: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 51.159.85.76: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 188.8.66.34: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 151.101.193.229: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 109.205.211.40: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.59.33.110: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 151.73.209.26: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 79.8.248.29: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8003ms for IP 217.202.28.18: This operation was aborted
[ANALYTICS QUERY] {
startDate: '2026-01-18T08:15:10.899Z',
endDate: '2026-02-17T08:15:10.899Z',
hourly: true,
hourCondition: 'NOT NULL'
}
[ANALYTICS RESULTS] 41 records found
[ANALYTICS SAMPLE] {
id: 'b3216c05-7f95-4ba9-a5fb-b37461441401',
date: 2026-02-16T00:00:00.000Z,
hour: 23,
totalPackets: 10463767,
totalBytes: 13102201398,
uniqueIps: 25472,
normalPackets: 10463767,
normalBytes: 13102201398,
normalUniqueIps: 25472,
topNormalIps: '[{"ip": "88.39.149.52", "packets": 3744014, "bytes": 5747025096, "country": null}, {"ip": "79.11.175.156", "packets": 1279527, "bytes": 1892212998, "country": null}, {"ip": "95.229.133.69", "packets": 1188856, "bytes": 1758976078, "country": null}, {"ip": "188.12.75.242", "packets": 1168922, "bytes": 1729133469, "country": null}, {"ip": "95.229.85.134", "packets": 1151191, "bytes": 1703382127, "country": null}, {"ip": "74.125.45.108", "packets": 108382, "bytes": 13501983, "country": "United States"}, {"ip": "185.243.5.22", "packets": 87376, "bytes": 38384352, "country": null}, {"ip": "104.16.248.249", "packets": 72117, "bytes": 40864635, "country": "Canada"}, {"ip": "8.8.8.8", "packets": 66005, "bytes": 18862021, "country": "United States"}, {"ip": "95.110.183.67", "packets": 51456, "bytes": 2281666, "country": "Italy"}]',
attackPackets: 0,
attackBytes: 0,
attackUniqueIps: 0,
attacksByCountry: '{}',
attacksByType: '{}',
topAttackers: '[]',
trafficByCountry: '{"Canada": {"normal": 81941, "attacks": 0}, "The Netherlands": {"normal": 49409, "attacks": 0}, "Singapore": {"normal": 61, "attacks": 0}, "Germany": {"normal": 339, "attacks": 0}, "United States":
{"normal": 385230, "attacks": 0}, "Italy": {"normal": 73801, "attacks": 0}, "Netherlands": {"normal": 19928, "attacks": 0}}',
createdAt: 2026-02-17T00:05:00.185Z
}
9:15:10 AM [express] GET /api/analytics/recent 200 in 16ms :: [{"id":"b3216c05-7f95-4ba9-a5fb-b37461…
9:15:12 AM [express] GET /api/training-history 200 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7968ms for IP 151.73.139.162: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7969ms for IP 217.28.70.122: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7971ms for IP 79.9.120.141: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7971ms for IP 52.123.129.14: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7970ms for IP 157.240.231.35: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7975ms for IP 46.229.84.162: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7973ms for IP 178.248.182.171: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7974ms for IP 93.43.107.86: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7974ms for IP 178.248.51.104: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 7977ms for IP 93.51.52.251: HTTP 401: {"error":401,"message":"Unauthorized"}
[BULK-BLOCK] Progress: 50/100
9:15:19 AM [express] GET /api/ml/stats 200 in 6466ms :: {"logs":{"total":126216346,"last_hour":0},"d…
9:15:22 AM [express] POST /api/ml/train 200 in 10ms :: {"message":"Training avviato in background","…
9:15:22 AM [express] GET /api/training-history 304 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 85.44.118.45: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 2.228.8.122: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 188.217.110.96: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.84.198.239: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.54.65.87: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 213.82.166.186: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 23.216.150.137: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 185.110.20.185: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 95.100.171.8: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 79.11.222.236: This operation was aborted
9:15:28 AM [express] GET /api/ml/stats 304 in 6490ms :: {"logs":{"total":126216346,"last_hour":0},"d…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 85.35.59.252: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.10.32.9: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.13.197.84: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 94.85.21.211: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 213.215.214.82: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.82.114.4: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 5.158.71.206: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 94.34.87.184: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.67.251.46: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 31.7.147.21: This operation was aborted
9:15:32 AM [express] GET /api/training-history 304 in 15ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 91.187.197.104: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.150: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 217.141.0.110: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.157: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 140.82.121.3: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 95.255.202.79: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.58.132.84: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 82.55.154.58: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 62.98.165.6: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 95.110.183.67: This operation was aborted
9:15:42 AM [express] GET /api/training-history 304 in 16ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
9:15:45 AM [express] GET /api/ml/stats 304 in 7198ms :: {"logs":{"total":126216346,"last_hour":0},"d…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.40.225.146: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 216.128.11.53: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 95.230.242.4: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 2.118.179.69: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 188.8.204.7: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.168.176.197: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 93.64.198.214: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 217.202.56.187: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8002ms for IP 5.63.174.25: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 151.64.151.157: This operation was aborted
9:15:52 AM [express] GET /api/training-history 304 in 17ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 79.2.176.5: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 82.192.139.100: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.40.226.33: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 93.146.168.160: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 94.101.59.182: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 185.104.127.31: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 31.197.212.236: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8000ms for IP 79.3.132.252: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.148: This operation was aborted
[BULK-BLOCK] SLOW: Router 10.20.30.100 took 8001ms for IP 172.217.38.151: This operation was aborted
[BULK-BLOCK] Progress: 100/100
[BULK-BLOCK] Router 10.20.30.100: 0 blocked, 100 failed, 0 skipped
[BULK-BLOCK] Router 185.203.24.2: 50 blocked, 0 failed, 50 skipped
[BULK-BLOCK] Done: 100 blocked, 0 failed, 0 skipped
[BLOCK-ALL] Database aggiornato: 100 IP marcati come bloccati
9:15:55 AM [express] POST /api/ml/block-all-critical 200 in 100172ms :: {"message":"Blocco massivo c…
9:16:02 AM [express] GET /api/training-history 304 in 2ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
9:16:02 AM [express] GET /api/ml/stats 200 in 6527ms :: {"logs":{"total":126216346,"last_hour":0},"d…
9:16:12 AM [express] GET /api/training-history 304 in 13ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
9:16:19 AM [express] GET /api/ml/stats 304 in 6558ms :: {"logs":{"total":126216346,"last_hour":0},"d…
9:16:19 AM [express] POST /api/ml/block-all-critical 200 in 76ms :: {"message":"Nessun IP critico da…
9:16:22 AM [express] GET /api/training-history 304 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
9:16:29 AM [express] GET /api/training-history 304 in 3ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e2…
9:16:35 AM [express] GET /api/ml/stats 304 in 6623ms :: {"logs":{"total":126216346,"last_hour":0},"d…
9:16:39 AM [express] GET /api/training-history 304 in 13ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
9:16:49 AM [express] GET /api/training-history 304 in 14ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
9:16:59 AM [express] GET /api/training-history 304 in 15ms :: [{"id":"4306f5a1-b30b-4241-97d9-6b6a1e…
[ML Stats] Fallback to database - ML Backend error: This operation was aborted
9:17:06 AM [express] GET /api/ml/stats 200 in 20345ms :: {"source":"database_fallback","ml_backend_s…
9:17:08 AM [express] GET /api/detections 200 in 21ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:17:08 AM [express] GET /api/routers 200 in 21ms :: [{"id":"c6904896-59dc-4060-952f-0e55df7dee4a","…
9:17:13 AM [express] GET /api/services/status 200 in 5007ms :: {"services":{"mlBackend":{"name":"ML …
9:17:13 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:14 AM [express] GET /api/stats 200 in 5393ms :: {"routers":{"total":2,"enabled":2},"detections"…
9:17:18 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:23 AM [express] GET /api/services/status 304 in 5006ms :: {"services":{"mlBackend":{"name":"ML …
9:17:23 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:28 AM [express] GET /api/services/status 200 in 85ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:17:28 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:29 AM [express] GET /api/stats 304 in 5314ms :: {"routers":{"total":2,"enabled":2},"detections"…
9:17:33 AM [express] GET /api/services/status 200 in 29ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:17:33 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:38 AM [express] GET /api/services/status 200 in 53ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:17:39 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:43 AM [express] GET /api/services/status 200 in 70ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:17:44 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:44 AM [express] GET /api/stats 304 in 5395ms :: {"routers":{"total":2,"enabled":2},"detections"…
9:17:49 AM [express] GET /api/services/status 200 in 47ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:17:49 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:17:54 AM [express] GET /api/detections 304 in 12ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:17:54 AM [express] GET /api/services/status 200 in 61ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:17:59 AM [express] GET /api/detections 304 in 11ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:17:59 AM [express] GET /api/services/status 200 in 30ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:18:00 AM [express] GET /api/stats 304 in 5405ms :: {"routers":{"total":2,"enabled":2},"detections"…
9:18:04 AM [express] GET /api/detections 304 in 7ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:18:04 AM [express] GET /api/services/status 200 in 64ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:18:09 AM [express] GET /api/detections 304 in 8ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7cf…
9:18:09 AM [express] GET /api/services/status 200 in 21ms :: {"services":{"mlBackend":{"name":"ML Ba…
9:18:14 AM [express] GET /api/detections 304 in 21ms :: {"detections":[{"id":"fcde52d3-0ae4-4904-a7c…
9:18:15 AM [express] GET /api/stats 304 in 5344ms :: {"routers":{"total":2,"enabled":2},"detections"…

View File

@ -2,21 +2,25 @@ import { useQuery, useMutation } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Activity, Brain, Database, FileText, Terminal, RefreshCw, Play, Square, RotateCw, Shield, Trash2, ListChecks, GraduationCap, Server, Clock, Timer } from "lucide-react"; import { Activity, Brain, Database, FileText, Terminal, RefreshCw, AlertCircle, Play, Square, RotateCw } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { queryClient, apiRequest } from "@/lib/queryClient"; import { queryClient, apiRequest } from "@/lib/queryClient";
interface ServiceStatus { interface ServiceStatus {
name: string; name: string;
status: string; status: "running" | "idle" | "offline" | "error" | "unknown";
healthy: boolean; healthy: boolean;
details: any; details: any;
systemdUnit: string;
type: string;
} }
interface ServicesStatusResponse { interface ServicesStatusResponse {
services: Record<string, ServiceStatus>; services: {
mlBackend: ServiceStatus;
database: ServiceStatus;
syslogParser: ServiceStatus;
analyticsAggregator: ServiceStatus;
};
} }
export default function ServicesPage() { export default function ServicesPage() {
@ -24,9 +28,10 @@ export default function ServicesPage() {
const { data: servicesStatus, isLoading, refetch } = useQuery<ServicesStatusResponse>({ const { data: servicesStatus, isLoading, refetch } = useQuery<ServicesStatusResponse>({
queryKey: ["/api/services/status"], queryKey: ["/api/services/status"],
refetchInterval: 5000, refetchInterval: 5000, // Refresh every 5s
}); });
// Mutation for service control
const serviceControlMutation = useMutation({ const serviceControlMutation = useMutation({
mutationFn: async ({ service, action }: { service: string; action: string }) => { mutationFn: async ({ service, action }: { service: string; action: string }) => {
return apiRequest("POST", `/api/services/${service}/${action}`); return apiRequest("POST", `/api/services/${service}/${action}`);
@ -34,8 +39,9 @@ export default function ServicesPage() {
onSuccess: (data, variables) => { onSuccess: (data, variables) => {
toast({ toast({
title: "Operazione completata", title: "Operazione completata",
description: `Servizio ${variables.service}: ${variables.action} eseguito`, description: `Servizio ${variables.service}: ${variables.action} eseguito con successo`,
}); });
// Refresh status after 2 seconds
setTimeout(() => { setTimeout(() => {
queryClient.invalidateQueries({ queryKey: ["/api/services/status"] }); queryClient.invalidateQueries({ queryKey: ["/api/services/status"] });
}, 2000); }, 2000);
@ -53,260 +59,39 @@ export default function ServicesPage() {
serviceControlMutation.mutate({ service, action }); serviceControlMutation.mutate({ service, action });
}; };
const getStatusBadge = (service: ServiceStatus, key: string) => { const getStatusBadge = (service: ServiceStatus) => {
if (service.healthy) { if (service.healthy) {
return <Badge variant="default" className="bg-green-600" data-testid={`badge-status-${key}-healthy`}>Online</Badge>; return <Badge variant="default" className="bg-green-600" data-testid={`badge-status-healthy`}>Online</Badge>;
} }
if (service.status === 'idle') { if (service.status === 'idle') {
return <Badge variant="secondary" data-testid={`badge-status-${key}-idle`}>In Attesa</Badge>; return <Badge variant="secondary" data-testid={`badge-status-idle`}>In Attesa</Badge>;
} }
if (service.status === 'offline') { if (service.status === 'offline') {
return <Badge variant="destructive" data-testid={`badge-status-${key}-offline`}>Offline</Badge>; return <Badge variant="destructive" data-testid={`badge-status-offline`}>Offline</Badge>;
} }
if (service.status === 'error') { if (service.status === 'error') {
return <Badge variant="destructive" data-testid={`badge-status-${key}-error`}>Errore</Badge>; return <Badge variant="destructive" data-testid={`badge-status-error`}>Errore</Badge>;
} }
return <Badge variant="outline" data-testid={`badge-status-${key}-unknown`}>Sconosciuto</Badge>; return <Badge variant="outline" data-testid={`badge-status-unknown`}>Sconosciuto</Badge>;
}; };
const getStatusIndicator = (service: ServiceStatus) => { const getStatusIndicator = (service: ServiceStatus) => {
if (service.healthy) { if (service.healthy) {
return <div className="h-3 w-3 rounded-full bg-green-500 shrink-0" />; return <div className="h-3 w-3 rounded-full bg-green-500" />;
} }
if (service.status === 'idle') { if (service.status === 'idle') {
return <div className="h-3 w-3 rounded-full bg-yellow-500 shrink-0" />; return <div className="h-3 w-3 rounded-full bg-yellow-500" />;
} }
return <div className="h-3 w-3 rounded-full bg-red-500 shrink-0" />; return <div className="h-3 w-3 rounded-full bg-red-500" />;
};
const getServiceIcon = (key: string) => {
const icons: Record<string, any> = {
nodeBackend: Server,
mlBackend: Brain,
database: Database,
syslogParser: FileText,
analyticsAggregator: Activity,
autoBlock: Shield,
cleanup: Trash2,
listFetcher: ListChecks,
mlTraining: GraduationCap,
};
const Icon = icons[key] || Activity;
return <Icon className="h-5 w-5" />;
};
const controllableServices = [
"ids-ml-backend", "ids-syslog-parser", "ids-backend",
"ids-analytics-aggregator", "ids-auto-block", "ids-cleanup",
"ids-list-fetcher", "ids-ml-training"
];
const getLogCommand = (key: string): string | null => {
const logs: Record<string, string> = {
nodeBackend: "tail -f /var/log/ids/backend.log",
mlBackend: "journalctl -u ids-ml-backend -f",
database: "sudo journalctl -u postgresql-16 -f",
syslogParser: "tail -f /var/log/ids/syslog_parser.log",
analyticsAggregator: "journalctl -u ids-analytics-aggregator -f",
autoBlock: "journalctl -u ids-auto-block -f",
cleanup: "journalctl -u ids-cleanup -f",
listFetcher: "journalctl -u ids-list-fetcher -f",
mlTraining: "journalctl -u ids-ml-training -f",
};
return logs[key] || null;
};
const renderDetailRow = (label: string, value: any, variant?: "default" | "destructive" | "secondary" | "outline") => {
if (value === undefined || value === null) return null;
return (
<div className="flex items-center justify-between gap-2 flex-wrap">
<span className="text-sm text-muted-foreground">{label}:</span>
{variant ? (
<Badge variant={variant} className="text-xs">{String(value)}</Badge>
) : (
<span className="text-sm font-mono">{String(value)}</span>
)}
</div>
);
};
const renderServiceDetails = (key: string, service: ServiceStatus) => {
const d = service.details;
if (!d) return null;
switch (key) {
case "nodeBackend":
return (
<>
{renderDetailRow("Porta", d.port)}
{renderDetailRow("Uptime", d.uptime)}
</>
);
case "mlBackend":
return (
<>
{d.modelLoaded !== undefined && renderDetailRow("Modello ML", d.modelLoaded ? "Caricato" : "Non Caricato", d.modelLoaded ? "default" : "secondary")}
{d.error && renderDetailRow("Errore", d.error, "destructive")}
</>
);
case "database":
return (
<>
{d.connected && renderDetailRow("Connessione", "Attiva", "default")}
{d.error && renderDetailRow("Errore", d.error, "destructive")}
</>
);
case "syslogParser":
return (
<>
{d.recentLogs30min !== undefined && renderDetailRow("Log ultimi 30min", d.recentLogs30min.toLocaleString())}
{d.lastLog && renderDetailRow("Ultimo log", typeof d.lastLog === 'string' ? d.lastLog : new Date(d.lastLog).toLocaleString('it-IT'))}
{d.warning && renderDetailRow("Avviso", d.warning, "destructive")}
</>
);
case "analyticsAggregator":
return (
<>
{d.lastRun && renderDetailRow("Ultima esecuzione", new Date(d.lastRun).toLocaleString('it-IT'))}
{d.hoursSinceLastRun && renderDetailRow("Ore dall'ultimo run", d.hoursSinceLastRun + "h", parseFloat(d.hoursSinceLastRun) < 2 ? "default" : "destructive")}
{d.warning && renderDetailRow("Avviso", d.warning, "destructive")}
</>
);
case "autoBlock":
return (
<>
{renderDetailRow("Blocchi ultimi 10min", d.recentBlocks10min)}
{renderDetailRow("Totale bloccati", d.totalBlocked)}
{d.lastBlock && renderDetailRow("Ultimo blocco", typeof d.lastBlock === 'string' && d.lastBlock !== 'Mai' ? new Date(d.lastBlock).toLocaleString('it-IT') : d.lastBlock)}
{renderDetailRow("Intervallo", d.interval)}
</>
);
case "cleanup":
return (
<>
{renderDetailRow("Detection vecchie (>48h)", d.oldDetections48h, d.oldDetections48h > 0 ? "destructive" : "default")}
{renderDetailRow("Detection totali", d.totalDetections)}
{renderDetailRow("Intervallo", d.interval)}
{d.warning && renderDetailRow("Avviso", d.warning, "destructive")}
</>
);
case "listFetcher":
return (
<>
{renderDetailRow("Liste totali", d.totalLists)}
{renderDetailRow("Liste attive", d.enabledLists)}
{d.lastFetched && renderDetailRow("Ultimo fetch", typeof d.lastFetched === 'string' && d.lastFetched !== 'Mai' ? new Date(d.lastFetched).toLocaleString('it-IT') : d.lastFetched)}
{d.hoursSinceLastFetch && renderDetailRow("Ore dall'ultimo fetch", d.hoursSinceLastFetch + "h", parseFloat(d.hoursSinceLastFetch) < 1 ? "default" : "destructive")}
{renderDetailRow("Intervallo", d.interval)}
</>
);
case "mlTraining":
return (
<>
{d.lastTraining && renderDetailRow("Ultimo training", typeof d.lastTraining === 'string' && d.lastTraining !== 'Mai' ? new Date(d.lastTraining).toLocaleString('it-IT') : d.lastTraining)}
{d.daysSinceLastTraining && renderDetailRow("Giorni dall'ultimo", d.daysSinceLastTraining, parseFloat(d.daysSinceLastTraining) < 8 ? "default" : "destructive")}
{d.lastStatus && renderDetailRow("Stato ultimo training", d.lastStatus, d.lastStatus === 'completed' ? "default" : "destructive")}
{d.lastModel && renderDetailRow("Modello", d.lastModel)}
{d.recordsProcessed && renderDetailRow("Record processati", d.recordsProcessed.toLocaleString())}
{renderDetailRow("Intervallo", d.interval)}
</>
);
default:
return d.error ? renderDetailRow("Errore", d.error, "destructive") : null;
}
};
const coreServices = ["nodeBackend", "mlBackend", "database", "syslogParser"];
const timerServices = ["autoBlock", "analyticsAggregator", "cleanup", "listFetcher", "mlTraining"];
const renderServiceCard = (key: string, service: ServiceStatus) => {
const isControllable = controllableServices.includes(service.systemdUnit);
const isTimer = service.type === "timer";
const logCmd = getLogCommand(key);
return (
<Card key={key} data-testid={`card-service-${key}`}>
<CardHeader className="flex flex-row items-center justify-between gap-2 space-y-0 pb-2">
<CardTitle className="flex items-center gap-2 text-base">
{getServiceIcon(key)}
<span className="truncate">{service.name}</span>
</CardTitle>
<div className="flex items-center gap-2 shrink-0">
{isTimer && <Timer className="h-4 w-4 text-muted-foreground" />}
{getStatusIndicator(service)}
</div>
</CardHeader>
<CardContent className="space-y-3">
<div className="flex items-center justify-between gap-2">
<span className="text-sm text-muted-foreground">Stato:</span>
{getStatusBadge(service, key)}
</div>
<div className="flex items-center justify-between gap-2">
<span className="text-sm text-muted-foreground">Systemd:</span>
<Badge variant="outline" className="text-xs font-mono">
{service.systemdUnit}{isTimer ? '.timer' : '.service'}
</Badge>
</div>
{renderServiceDetails(key, service)}
{isControllable && (
<div className="space-y-2 pt-2 border-t">
<p className="text-xs font-medium text-muted-foreground">Controlli:</p>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction(service.systemdUnit, "restart")}
disabled={serviceControlMutation.isPending}
data-testid={`button-restart-${key}`}
>
<RotateCw className="h-3 w-3 mr-1" />
Restart
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction(service.systemdUnit, "start")}
disabled={serviceControlMutation.isPending || service.status === 'running'}
data-testid={`button-start-${key}`}
>
<Play className="h-3 w-3 mr-1" />
Start
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction(service.systemdUnit, "stop")}
disabled={serviceControlMutation.isPending || service.status === 'offline'}
data-testid={`button-stop-${key}`}
>
<Square className="h-3 w-3 mr-1" />
Stop
</Button>
</div>
</div>
)}
{logCmd && (
<div className="p-2 bg-muted rounded-md">
<p className="text-xs font-medium text-muted-foreground mb-1">Log:</p>
<code className="text-xs font-mono break-all" data-testid={`code-log-${key}`}>{logCmd}</code>
</div>
)}
</CardContent>
</Card>
);
}; };
return ( return (
<div className="flex flex-col gap-6 p-6" data-testid="page-services"> <div className="flex flex-col gap-6 p-6" data-testid="page-services">
<div className="flex items-center justify-between gap-4 flex-wrap"> <div className="flex items-center justify-between">
<div> <div>
<h1 className="text-3xl font-semibold" data-testid="text-services-title">Gestione Servizi</h1> <h1 className="text-3xl font-semibold" data-testid="text-services-title">Gestione Servizi</h1>
<p className="text-muted-foreground" data-testid="text-services-subtitle"> <p className="text-muted-foreground" data-testid="text-services-subtitle">
Monitoraggio e controllo di tutti i servizi IDS Monitoraggio e controllo dei servizi IDS
</p> </p>
</div> </div>
<Button onClick={() => refetch()} variant="outline" data-testid="button-refresh"> <Button onClick={() => refetch()} variant="outline" data-testid="button-refresh">
@ -315,40 +100,303 @@ export default function ServicesPage() {
</Button> </Button>
</div> </div>
{isLoading && ( <Alert data-testid="alert-server-instructions">
<div className="text-center py-8 text-muted-foreground">Caricamento stato servizi...</div> <AlertCircle className="h-4 w-4" />
)} <AlertTitle>Gestione Servizi Systemd</AlertTitle>
<AlertDescription>
I servizi IDS sono gestiti da systemd sul server AlmaLinux.
Usa i pulsanti qui sotto per controllarli oppure esegui i comandi systemctl direttamente sul server.
</AlertDescription>
</Alert>
{servicesStatus && ( {/* Services Grid */}
<> <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div> {/* ML Backend Service */}
<h2 className="text-lg font-semibold mb-3 flex items-center gap-2"> <Card data-testid="card-ml-backend-service">
<Server className="h-5 w-5" /> <CardHeader>
Servizi Core <CardTitle className="flex items-center gap-2 text-lg">
</h2> <Brain className="h-5 w-5" />
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4"> ML Backend Python
{coreServices.map((key) => { {servicesStatus && getStatusIndicator(servicesStatus.services.mlBackend)}
const service = (servicesStatus.services as any)[key]; </CardTitle>
return service ? renderServiceCard(key, service) : null; </CardHeader>
})} <CardContent className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Stato:</span>
{servicesStatus && getStatusBadge(servicesStatus.services.mlBackend)}
</div> </div>
</div>
<div> {servicesStatus?.services.mlBackend.details?.modelLoaded !== undefined && (
<h2 className="text-lg font-semibold mb-3 flex items-center gap-2"> <div className="flex items-center justify-between">
<Clock className="h-5 w-5" /> <span className="text-sm text-muted-foreground">Modello ML:</span>
Timer Systemd (Attivita Periodiche) <Badge variant={servicesStatus.services.mlBackend.details.modelLoaded ? "default" : "secondary"}>
</h2> {servicesStatus.services.mlBackend.details.modelLoaded ? "Caricato" : "Non Caricato"}
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4"> </Badge>
{timerServices.map((key) => { </div>
const service = (servicesStatus.services as any)[key]; )}
return service ? renderServiceCard(key, service) : null;
})} {/* Service Controls */}
<div className="mt-4 space-y-2">
<p className="text-xs font-medium mb-2">Controlli Servizio:</p>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction("ids-ml-backend", "start")}
disabled={serviceControlMutation.isPending || servicesStatus?.services.mlBackend.status === 'running'}
data-testid="button-start-ml"
>
<Play className="h-3 w-3 mr-1" />
Start
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction("ids-ml-backend", "stop")}
disabled={serviceControlMutation.isPending || servicesStatus?.services.mlBackend.status === 'offline'}
data-testid="button-stop-ml"
>
<Square className="h-3 w-3 mr-1" />
Stop
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction("ids-ml-backend", "restart")}
disabled={serviceControlMutation.isPending}
data-testid="button-restart-ml"
>
<RotateCw className="h-3 w-3 mr-1" />
Restart
</Button>
</div>
</div> </div>
</div>
</>
)}
{/* Manual Commands (fallback) */}
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Comando systemctl (sul server):</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-systemctl-ml">
sudo systemctl {servicesStatus?.services.mlBackend.status === 'offline' ? 'start' : 'restart'} ids-ml-backend
</code>
</div>
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Log:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-ml">
tail -f /var/log/ids/backend.log
</code>
</div>
</CardContent>
</Card>
{/* Database Service */}
<Card data-testid="card-database-service">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Database className="h-5 w-5" />
PostgreSQL Database
{servicesStatus && getStatusIndicator(servicesStatus.services.database)}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Stato:</span>
{servicesStatus && getStatusBadge(servicesStatus.services.database)}
</div>
{servicesStatus?.services.database.status === 'running' && (
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Connessione:</span>
<Badge variant="default" className="bg-green-600">Connesso</Badge>
</div>
)}
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Verifica status:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-status-db">
systemctl status postgresql-16
</code>
</div>
{servicesStatus?.services.database.status === 'error' && (
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Riavvia database:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-restart-db">
sudo systemctl restart postgresql-16
</code>
</div>
)}
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Log:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-db">
sudo journalctl -u postgresql-16 -f
</code>
</div>
</CardContent>
</Card>
{/* Syslog Parser Service */}
<Card data-testid="card-syslog-parser-service">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<FileText className="h-5 w-5" />
Syslog Parser
{servicesStatus && getStatusIndicator(servicesStatus.services.syslogParser)}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Stato:</span>
{servicesStatus && getStatusBadge(servicesStatus.services.syslogParser)}
</div>
{servicesStatus?.services.syslogParser.details?.pid && (
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">PID Processo:</span>
<Badge variant="outline" className="font-mono">
{servicesStatus.services.syslogParser.details.pid}
</Badge>
</div>
)}
{servicesStatus?.services.syslogParser.details?.systemd_unit && (
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Systemd Unit:</span>
<Badge variant="outline" className="font-mono text-xs">
{servicesStatus.services.syslogParser.details.systemd_unit}
</Badge>
</div>
)}
{/* Service Controls */}
<div className="mt-4 space-y-2">
<p className="text-xs font-medium mb-2">Controlli Servizio:</p>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction("ids-syslog-parser", "start")}
disabled={serviceControlMutation.isPending || servicesStatus?.services.syslogParser.status === 'running'}
data-testid="button-start-parser"
>
<Play className="h-3 w-3 mr-1" />
Start
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction("ids-syslog-parser", "stop")}
disabled={serviceControlMutation.isPending || servicesStatus?.services.syslogParser.status === 'offline'}
data-testid="button-stop-parser"
>
<Square className="h-3 w-3 mr-1" />
Stop
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleServiceAction("ids-syslog-parser", "restart")}
disabled={serviceControlMutation.isPending}
data-testid="button-restart-parser"
>
<RotateCw className="h-3 w-3 mr-1" />
Restart
</Button>
</div>
</div>
{/* Manual Commands (fallback) */}
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Comando systemctl (sul server):</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-systemctl-parser">
sudo systemctl {servicesStatus?.services.syslogParser.status === 'offline' ? 'start' : 'restart'} ids-syslog-parser
</code>
</div>
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Log:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-parser">
tail -f /var/log/ids/syslog_parser.log
</code>
</div>
</CardContent>
</Card>
{/* Analytics Aggregator Service */}
<Card data-testid="card-analytics-aggregator-service">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Activity className="h-5 w-5" />
Analytics Aggregator
{servicesStatus && getStatusIndicator(servicesStatus.services.analyticsAggregator)}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Stato:</span>
{servicesStatus && getStatusBadge(servicesStatus.services.analyticsAggregator)}
</div>
{servicesStatus?.services.analyticsAggregator.details?.lastRun && (
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Ultima Aggregazione:</span>
<Badge variant="outline" className="text-xs">
{new Date(servicesStatus.services.analyticsAggregator.details.lastRun).toLocaleString('it-IT')}
</Badge>
</div>
)}
{servicesStatus?.services.analyticsAggregator.details?.hoursSinceLastRun && (
<div className="flex items-center justify-between">
<span className="text-sm text-muted-foreground">Ore dall'ultimo run:</span>
<Badge variant={parseFloat(servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun) < 2 ? "default" : "destructive"}>
{servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun}h
</Badge>
</div>
)}
{/* CRITICAL ALERT: Aggregator idle for too long */}
{servicesStatus?.services.analyticsAggregator.details?.hoursSinceLastRun &&
parseFloat(servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun) > 2 && (
<Alert variant="destructive" className="mt-2" data-testid="alert-aggregator-idle">
<AlertCircle className="h-4 w-4" />
<AlertTitle className="text-sm font-semibold"> Timer Systemd Non Attivo</AlertTitle>
<AlertDescription className="text-xs mt-1">
<p className="mb-2">L'aggregatore non esegue da {servicesStatus.services.analyticsAggregator.details.hoursSinceLastRun}h! Dashboard e Analytics bloccati.</p>
<p className="font-semibold">Soluzione Immediata (sul server):</p>
<code className="block bg-destructive-foreground/10 p-2 rounded mt-1 font-mono text-xs">
sudo /opt/ids/deployment/setup_analytics_timer.sh
</code>
</AlertDescription>
</Alert>
)}
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Verifica timer:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-status-aggregator">
systemctl status ids-analytics-aggregator.timer
</code>
</div>
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Avvia aggregazione manualmente:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-run-aggregator">
cd /opt/ids && ./deployment/run_analytics.sh
</code>
</div>
<div className="mt-4 p-3 bg-muted rounded-lg">
<p className="text-xs font-medium mb-2">Log:</p>
<code className="text-xs bg-background p-2 rounded block font-mono" data-testid="code-log-aggregator">
journalctl -u ids-analytics-aggregator.timer -f
</code>
</div>
</CardContent>
</Card>
</div>
{/* Additional Commands */}
<Card data-testid="card-additional-commands"> <Card data-testid="card-additional-commands">
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="flex items-center gap-2">
@ -358,27 +406,30 @@ export default function ServicesPage() {
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div> <div>
<p className="text-sm font-medium mb-2">Stato di tutti i servizi IDS:</p> <p className="text-sm font-medium mb-2">Verifica tutti i processi IDS attivi:</p>
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-all-services"> <code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-check-processes">
systemctl list-units 'ids-*' --all ps aux | grep -E "python.*(main|syslog_parser)" | grep -v grep
</code>
</div>
<div>
<p className="text-sm font-medium mb-2">Stato di tutti i timer IDS:</p>
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-all-timers">
systemctl list-timers 'ids-*' --all
</code> </code>
</div> </div>
<div> <div>
<p className="text-sm font-medium mb-2">Verifica log RSyslog (ricezione log MikroTik):</p> <p className="text-sm font-medium mb-2">Verifica log RSyslog (ricezione log MikroTik):</p>
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-check-rsyslog"> <code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-check-rsyslog">
tail -f /var/log/mikrotik/raw.log tail -f /var/log/mikrotik/raw.log
</code> </code>
</div> </div>
<div> <div>
<p className="text-sm font-medium mb-2">Verifica processi IDS attivi:</p> <p className="text-sm font-medium mb-2">Esegui training manuale ML:</p>
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-check-processes"> <code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-manual-training">
ps aux | grep -E "python.*(main|syslog_parser)" | grep -v grep curl -X POST http://localhost:8000/train -H "Content-Type: application/json" -d '&#123;"max_records": 10000, "hours_back": 24&#125;'
</code>
</div>
<div>
<p className="text-sm font-medium mb-2">Verifica storico training nel database:</p>
<code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-check-training">
psql $DATABASE_URL -c "SELECT * FROM training_history ORDER BY trained_at DESC LIMIT 5;"
</code> </code>
</div> </div>
</CardContent> </CardContent>

View File

@ -2,7 +2,7 @@
-- PostgreSQL database dump -- PostgreSQL database dump
-- --
\restrict QQPZgpukcxzRMKOdS5xNsXDiphiHLW5uAuhQxN7luRJ2u8BkVkDOz1h9Un2BrJ0 \restrict eyisBUJGZpxqwUy3cADDz6PSlf2SjvYWDM3PJH885ta9lWx9pkI2q38QNzdHOVh
-- Dumped from database version 16.11 (df20cf9) -- Dumped from database version 16.11 (df20cf9)
-- Dumped by pg_dump version 16.10 -- Dumped by pg_dump version 16.10
@ -387,5 +387,5 @@ ALTER TABLE ONLY public.public_blacklist_ips
-- PostgreSQL database dump complete -- PostgreSQL database dump complete
-- --
\unrestrict QQPZgpukcxzRMKOdS5xNsXDiphiHLW5uAuhQxN7luRJ2u8BkVkDOz1h9Un2BrJ0 \unrestrict eyisBUJGZpxqwUy3cADDz6PSlf2SjvYWDM3PJH885ta9lWx9pkI2q38QNzdHOVh

View File

@ -780,43 +780,39 @@ export async function registerRoutes(app: Express): Promise<Server> {
// Services monitoring // Services monitoring
app.get("/api/services/status", async (req, res) => { app.get("/api/services/status", async (req, res) => {
try { try {
const mkService = (name: string) => ({ name, status: "unknown" as string, healthy: false, details: null as any, systemdUnit: "" as string, type: "service" as string });
const services = { const services = {
nodeBackend: { ...mkService("Node.js Backend"), systemdUnit: "ids-backend", type: "service" }, mlBackend: { name: "ML Backend Python", status: "unknown", healthy: false, details: null as any },
mlBackend: { ...mkService("ML Backend Python"), systemdUnit: "ids-ml-backend", type: "service" }, database: { name: "PostgreSQL Database", status: "unknown", healthy: false, details: null as any },
database: { ...mkService("PostgreSQL Database"), systemdUnit: "postgresql-16", type: "service" }, syslogParser: { name: "Syslog Parser", status: "unknown", healthy: false, details: null as any },
syslogParser: { ...mkService("Syslog Parser"), systemdUnit: "ids-syslog-parser", type: "service" }, analyticsAggregator: { name: "Analytics Aggregator Timer", status: "unknown", healthy: false, details: null as any },
analyticsAggregator: { ...mkService("Analytics Aggregator"), systemdUnit: "ids-analytics-aggregator", type: "timer" },
autoBlock: { ...mkService("Auto Block"), systemdUnit: "ids-auto-block", type: "timer" },
cleanup: { ...mkService("Cleanup Detections"), systemdUnit: "ids-cleanup", type: "timer" },
listFetcher: { ...mkService("Public Lists Fetcher"), systemdUnit: "ids-list-fetcher", type: "timer" },
mlTraining: { ...mkService("ML Training Settimanale"), systemdUnit: "ids-ml-training", type: "timer" },
}; };
// Node.js Backend - always running if this endpoint responds
services.nodeBackend.status = "running";
services.nodeBackend.healthy = true;
services.nodeBackend.details = { port: 5000, uptime: process.uptime().toFixed(0) + "s" };
// Check ML Backend Python // Check ML Backend Python
try { try {
const controller = new AbortController(); const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000); const timeout = setTimeout(() => controller.abort(), 5000);
const response = await fetch(`${ML_BACKEND_URL}/health`, { signal: controller.signal });
const response = await fetch(`${ML_BACKEND_URL}/health`, {
signal: controller.signal,
});
clearTimeout(timeout); clearTimeout(timeout);
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
services.mlBackend.status = "running"; services.mlBackend.status = "running";
services.mlBackend.healthy = true; services.mlBackend.healthy = true;
services.mlBackend.details = { modelLoaded: data.ml_model === "loaded", timestamp: data.timestamp }; services.mlBackend.details = {
modelLoaded: data.ml_model === "loaded",
timestamp: data.timestamp,
};
} else { } else {
services.mlBackend.status = "error"; services.mlBackend.status = "error";
services.mlBackend.details = { error: `HTTP ${response.status}` }; services.mlBackend.details = { error: `HTTP ${response.status}` };
} }
} catch (error: any) { } catch (error: any) {
services.mlBackend.status = "offline"; services.mlBackend.status = "offline";
services.mlBackend.details = { error: error.code === 'ECONNREFUSED' ? "Connessione rifiutata" : error.message }; services.mlBackend.details = { error: error.code === 'ECONNREFUSED' ? "Connection refused" : error.message };
} }
// Check Database // Check Database
@ -832,156 +828,87 @@ export async function registerRoutes(app: Express): Promise<Server> {
services.database.details = { error: error.message }; services.database.details = { error: error.message };
} }
// Check Syslog Parser via database // Check Syslog Parser via database (independent of ML Backend)
try { try {
const recentLogsResult = await db.execute( const recentLogsResult = await db.execute(
sql`SELECT COUNT(*) as count, MAX(timestamp) as last_log FROM network_logs WHERE timestamp > NOW() - INTERVAL '30 minutes'` sql`SELECT COUNT(*) as count, MAX(timestamp) as last_log
FROM network_logs
WHERE timestamp > NOW() - INTERVAL '30 minutes'`
); );
const logRows = (recentLogsResult as any).rows || recentLogsResult; const logRows = (recentLogsResult as any).rows || recentLogsResult;
const recentLogCount = parseInt(logRows[0]?.count || "0"); const recentLogCount = parseInt(logRows[0]?.count || "0");
const lastLogTime = logRows[0]?.last_log; const lastLogTime = logRows[0]?.last_log;
if (recentLogCount > 0) { if (recentLogCount > 0) {
services.syslogParser.status = "running"; services.syslogParser.status = "running";
services.syslogParser.healthy = true; services.syslogParser.healthy = true;
services.syslogParser.details = { recentLogs30min: recentLogCount, lastLog: lastLogTime }; services.syslogParser.details = {
recentLogs30min: recentLogCount,
lastLog: lastLogTime,
};
} else { } else {
const lastLogEverResult = await db.execute(sql`SELECT MAX(timestamp) as last_log FROM network_logs`); const lastLogEverResult = await db.execute(
sql`SELECT MAX(timestamp) as last_log FROM network_logs`
);
const lastLogEverRows = (lastLogEverResult as any).rows || lastLogEverResult; const lastLogEverRows = (lastLogEverResult as any).rows || lastLogEverResult;
const lastLogEver = lastLogEverRows[0]?.last_log;
services.syslogParser.status = "offline"; services.syslogParser.status = "offline";
services.syslogParser.healthy = false; services.syslogParser.healthy = false;
services.syslogParser.details = { recentLogs30min: 0, lastLog: lastLogEverRows[0]?.last_log || "Mai", warning: "Nessun log negli ultimi 30 minuti" }; services.syslogParser.details = {
recentLogs30min: 0,
lastLog: lastLogEver || "Never",
warning: "No logs received in last 30 minutes",
};
} }
} catch (error: any) { } catch (error: any) {
services.syslogParser.status = "error"; services.syslogParser.status = "error";
services.syslogParser.healthy = false;
services.syslogParser.details = { error: error.message }; services.syslogParser.details = { error: error.message };
} }
// Check Analytics Aggregator (via last record timestamp) // Check Analytics Aggregator (via last record timestamp)
try { try {
const latestAnalytics = await db.select().from(networkAnalytics).orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour)).limit(1); const latestAnalytics = await db
.select()
.from(networkAnalytics)
.orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour))
.limit(1);
if (latestAnalytics.length > 0) { if (latestAnalytics.length > 0) {
const lastRun = new Date(latestAnalytics[0].date); const lastRun = new Date(latestAnalytics[0].date);
const hoursSince = (Date.now() - lastRun.getTime()) / (1000 * 60 * 60); const lastTimestamp = lastRun.toISOString();
if (hoursSince < 2) { const hoursSinceLastRun = (Date.now() - lastRun.getTime()) / (1000 * 60 * 60);
if (hoursSinceLastRun < 2) {
services.analyticsAggregator.status = "running"; services.analyticsAggregator.status = "running";
services.analyticsAggregator.healthy = true; services.analyticsAggregator.healthy = true;
services.analyticsAggregator.details = { lastRun: latestAnalytics[0].date, hoursSinceLastRun: hoursSince.toFixed(1) }; services.analyticsAggregator.details = {
lastRun: latestAnalytics[0].date,
lastTimestamp,
hoursSinceLastRun: hoursSinceLastRun.toFixed(1),
};
} else { } else {
services.analyticsAggregator.status = "idle"; services.analyticsAggregator.status = "idle";
services.analyticsAggregator.details = { lastRun: latestAnalytics[0].date, hoursSinceLastRun: hoursSince.toFixed(1), warning: "Nessuna aggregazione nelle ultime 2 ore" }; services.analyticsAggregator.healthy = false;
services.analyticsAggregator.details = {
lastRun: latestAnalytics[0].date,
lastTimestamp,
hoursSinceLastRun: hoursSinceLastRun.toFixed(1),
warning: "No aggregation in last 2 hours",
};
} }
} else { } else {
services.analyticsAggregator.status = "idle"; services.analyticsAggregator.status = "idle";
services.analyticsAggregator.details = { error: "Nessun dato analytics trovato" }; services.analyticsAggregator.healthy = false;
services.analyticsAggregator.details = { error: "No analytics data found" };
} }
} catch (error: any) { } catch (error: any) {
services.analyticsAggregator.status = "error"; services.analyticsAggregator.status = "error";
services.analyticsAggregator.healthy = false;
services.analyticsAggregator.details = { error: error.message }; services.analyticsAggregator.details = { error: error.message };
} }
// Check Auto Block (via recent blocked detections)
try {
const recentBlockResult = await db.execute(
sql`SELECT COUNT(*) as count, MAX(detected_at) as last_block FROM detections WHERE blocked = true AND detected_at > NOW() - INTERVAL '10 minutes'`
);
const blockRows = (recentBlockResult as any).rows || recentBlockResult;
const recentBlocks = parseInt(blockRows[0]?.count || "0");
const lastBlock = blockRows[0]?.last_block;
const totalBlockedResult = await db.execute(sql`SELECT COUNT(*) as count FROM detections WHERE blocked = true`);
const totalBlockedRows = (totalBlockedResult as any).rows || totalBlockedResult;
const totalBlocked = parseInt(totalBlockedRows[0]?.count || "0");
services.autoBlock.status = recentBlocks > 0 ? "running" : "idle";
services.autoBlock.healthy = true;
services.autoBlock.details = {
recentBlocks10min: recentBlocks,
totalBlocked,
lastBlock: lastBlock || "Mai",
interval: "ogni 5 minuti"
};
} catch (error: any) {
services.autoBlock.status = "error";
services.autoBlock.details = { error: error.message };
}
// Check Cleanup (via absence of old detections)
try {
const oldDetResult = await db.execute(
sql`SELECT COUNT(*) as count FROM detections WHERE detected_at < NOW() - INTERVAL '48 hours'`
);
const oldRows = (oldDetResult as any).rows || oldDetResult;
const oldDetections = parseInt(oldRows[0]?.count || "0");
const totalDetResult = await db.execute(sql`SELECT COUNT(*) as count FROM detections`);
const totalRows = (totalDetResult as any).rows || totalDetResult;
const totalDetections = parseInt(totalRows[0]?.count || "0");
services.cleanup.status = oldDetections === 0 ? "running" : "idle";
services.cleanup.healthy = oldDetections === 0;
services.cleanup.details = {
oldDetections48h: oldDetections,
totalDetections,
interval: "ogni ora",
warning: oldDetections > 0 ? `${oldDetections} detection vecchie non ancora pulite` : undefined
};
} catch (error: any) {
services.cleanup.status = "error";
services.cleanup.details = { error: error.message };
}
// Check List Fetcher (via public lists last_updated)
try {
const listsResult = await db.execute(
sql`SELECT COUNT(*) as total,
COUNT(*) FILTER (WHERE enabled = true) as enabled,
MAX(last_fetch) as last_fetch
FROM public_lists`
);
const listRows = (listsResult as any).rows || listsResult;
const totalLists = parseInt(listRows[0]?.total || "0");
const enabledLists = parseInt(listRows[0]?.enabled || "0");
const lastFetched = listRows[0]?.last_fetch;
if (lastFetched) {
const hoursSince = (Date.now() - new Date(lastFetched).getTime()) / (1000 * 60 * 60);
services.listFetcher.status = hoursSince < 1 ? "running" : "idle";
services.listFetcher.healthy = hoursSince < 1;
services.listFetcher.details = { totalLists, enabledLists, lastFetched, hoursSinceLastFetch: hoursSince.toFixed(1), interval: "ogni 10 minuti" };
} else {
services.listFetcher.status = "idle";
services.listFetcher.details = { totalLists, enabledLists, lastFetched: "Mai", interval: "ogni 10 minuti" };
}
} catch (error: any) {
services.listFetcher.status = "error";
services.listFetcher.details = { error: error.message };
}
// Check ML Training (via training history)
try {
const latestTraining = await db.select().from(trainingHistory).orderBy(desc(trainingHistory.trainedAt)).limit(1);
if (latestTraining.length > 0) {
const lastTrainDate = new Date(latestTraining[0].trainedAt);
const daysSince = (Date.now() - lastTrainDate.getTime()) / (1000 * 60 * 60 * 24);
services.mlTraining.status = daysSince < 8 ? "running" : "idle";
services.mlTraining.healthy = daysSince < 8;
services.mlTraining.details = {
lastTraining: latestTraining[0].trainedAt,
daysSinceLastTraining: daysSince.toFixed(1),
lastStatus: latestTraining[0].status,
lastModel: latestTraining[0].modelVersion,
recordsProcessed: latestTraining[0].recordsProcessed,
interval: "settimanale"
};
} else {
services.mlTraining.status = "idle";
services.mlTraining.details = { lastTraining: "Mai", interval: "settimanale" };
}
} catch (error: any) {
services.mlTraining.status = "error";
services.mlTraining.details = { error: error.message };
}
res.json({ services }); res.json({ services });
} catch (error: any) { } catch (error: any) {
res.status(500).json({ error: "Failed to check services status" }); res.status(500).json({ error: "Failed to check services status" });
@ -989,11 +916,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
}); });
// Service Control Endpoints (Secured - only allow specific systemd operations) // Service Control Endpoints (Secured - only allow specific systemd operations)
const ALLOWED_SERVICES = [ const ALLOWED_SERVICES = ["ids-ml-backend", "ids-syslog-parser"];
"ids-ml-backend", "ids-syslog-parser", "ids-backend",
"ids-analytics-aggregator", "ids-auto-block", "ids-cleanup",
"ids-list-fetcher", "ids-ml-training"
];
const ALLOWED_ACTIONS = ["start", "stop", "restart", "status"]; const ALLOWED_ACTIONS = ["start", "stop", "restart", "status"];
app.post("/api/services/:service/:action", async (req, res) => { app.post("/api/services/:service/:action", async (req, res) => {

View File

@ -1,13 +1,7 @@
{ {
"version": "1.0.122", "version": "1.0.121",
"lastUpdate": "2026-02-17T09:13:40.571Z", "lastUpdate": "2026-02-17T08:11:26.352Z",
"changelog": [ "changelog": [
{
"version": "1.0.122",
"date": "2026-02-17",
"type": "patch",
"description": "Deployment automatico v1.0.122"
},
{ {
"version": "1.0.121", "version": "1.0.121",
"date": "2026-02-17", "date": "2026-02-17",
@ -301,6 +295,12 @@
"date": "2025-11-25", "date": "2025-11-25",
"type": "patch", "type": "patch",
"description": "Deployment automatico v1.0.73" "description": "Deployment automatico v1.0.73"
},
{
"version": "1.0.72",
"date": "2025-11-25",
"type": "patch",
"description": "Deployment automatico v1.0.72"
} }
] ]
} }