Compare commits
5 Commits
9104c67f97
...
f6d656ce14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6d656ce14 | ||
|
|
17dc79372e | ||
|
|
4118d60d6d | ||
|
|
6ce60ed5d3 | ||
|
|
fe113d5518 |
@ -0,0 +1,85 @@
|
|||||||
|
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…
|
||||||
@ -0,0 +1,224 @@
|
|||||||
|
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"…
|
||||||
|
|
||||||
@ -2,25 +2,21 @@ 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, AlertCircle, Play, Square, RotateCw } from "lucide-react";
|
import { Activity, Brain, Database, FileText, Terminal, RefreshCw, Play, Square, RotateCw, Shield, Trash2, ListChecks, GraduationCap, Server, Clock, Timer } 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: "running" | "idle" | "offline" | "error" | "unknown";
|
status: string;
|
||||||
healthy: boolean;
|
healthy: boolean;
|
||||||
details: any;
|
details: any;
|
||||||
|
systemdUnit: string;
|
||||||
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServicesStatusResponse {
|
interface ServicesStatusResponse {
|
||||||
services: {
|
services: Record<string, ServiceStatus>;
|
||||||
mlBackend: ServiceStatus;
|
|
||||||
database: ServiceStatus;
|
|
||||||
syslogParser: ServiceStatus;
|
|
||||||
analyticsAggregator: ServiceStatus;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ServicesPage() {
|
export default function ServicesPage() {
|
||||||
@ -28,10 +24,9 @@ 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, // Refresh every 5s
|
refetchInterval: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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}`);
|
||||||
@ -39,9 +34,8 @@ 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 con successo`,
|
description: `Servizio ${variables.service}: ${variables.action} eseguito`,
|
||||||
});
|
});
|
||||||
// Refresh status after 2 seconds
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["/api/services/status"] });
|
queryClient.invalidateQueries({ queryKey: ["/api/services/status"] });
|
||||||
}, 2000);
|
}, 2000);
|
||||||
@ -59,39 +53,260 @@ export default function ServicesPage() {
|
|||||||
serviceControlMutation.mutate({ service, action });
|
serviceControlMutation.mutate({ service, action });
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatusBadge = (service: ServiceStatus) => {
|
const getStatusBadge = (service: ServiceStatus, key: string) => {
|
||||||
if (service.healthy) {
|
if (service.healthy) {
|
||||||
return <Badge variant="default" className="bg-green-600" data-testid={`badge-status-healthy`}>Online</Badge>;
|
return <Badge variant="default" className="bg-green-600" data-testid={`badge-status-${key}-healthy`}>Online</Badge>;
|
||||||
}
|
}
|
||||||
if (service.status === 'idle') {
|
if (service.status === 'idle') {
|
||||||
return <Badge variant="secondary" data-testid={`badge-status-idle`}>In Attesa</Badge>;
|
return <Badge variant="secondary" data-testid={`badge-status-${key}-idle`}>In Attesa</Badge>;
|
||||||
}
|
}
|
||||||
if (service.status === 'offline') {
|
if (service.status === 'offline') {
|
||||||
return <Badge variant="destructive" data-testid={`badge-status-offline`}>Offline</Badge>;
|
return <Badge variant="destructive" data-testid={`badge-status-${key}-offline`}>Offline</Badge>;
|
||||||
}
|
}
|
||||||
if (service.status === 'error') {
|
if (service.status === 'error') {
|
||||||
return <Badge variant="destructive" data-testid={`badge-status-error`}>Errore</Badge>;
|
return <Badge variant="destructive" data-testid={`badge-status-${key}-error`}>Errore</Badge>;
|
||||||
}
|
}
|
||||||
return <Badge variant="outline" data-testid={`badge-status-unknown`}>Sconosciuto</Badge>;
|
return <Badge variant="outline" data-testid={`badge-status-${key}-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" />;
|
return <div className="h-3 w-3 rounded-full bg-green-500 shrink-0" />;
|
||||||
}
|
}
|
||||||
if (service.status === 'idle') {
|
if (service.status === 'idle') {
|
||||||
return <div className="h-3 w-3 rounded-full bg-yellow-500" />;
|
return <div className="h-3 w-3 rounded-full bg-yellow-500 shrink-0" />;
|
||||||
}
|
}
|
||||||
return <div className="h-3 w-3 rounded-full bg-red-500" />;
|
return <div className="h-3 w-3 rounded-full bg-red-500 shrink-0" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
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">
|
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||||
<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 dei servizi IDS
|
Monitoraggio e controllo di tutti i servizi IDS
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={() => refetch()} variant="outline" data-testid="button-refresh">
|
<Button onClick={() => refetch()} variant="outline" data-testid="button-refresh">
|
||||||
@ -100,303 +315,40 @@ export default function ServicesPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Alert data-testid="alert-server-instructions">
|
{isLoading && (
|
||||||
<AlertCircle className="h-4 w-4" />
|
<div className="text-center py-8 text-muted-foreground">Caricamento stato servizi...</div>
|
||||||
<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>
|
|
||||||
|
|
||||||
{/* Services Grid */}
|
{servicesStatus && (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
<>
|
||||||
{/* ML Backend Service */}
|
<div>
|
||||||
<Card data-testid="card-ml-backend-service">
|
<h2 className="text-lg font-semibold mb-3 flex items-center gap-2">
|
||||||
<CardHeader>
|
<Server className="h-5 w-5" />
|
||||||
<CardTitle className="flex items-center gap-2 text-lg">
|
Servizi Core
|
||||||
<Brain className="h-5 w-5" />
|
</h2>
|
||||||
ML Backend Python
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
||||||
{servicesStatus && getStatusIndicator(servicesStatus.services.mlBackend)}
|
{coreServices.map((key) => {
|
||||||
</CardTitle>
|
const service = (servicesStatus.services as any)[key];
|
||||||
</CardHeader>
|
return service ? renderServiceCard(key, service) : null;
|
||||||
<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>
|
||||||
|
|
||||||
{servicesStatus?.services.mlBackend.details?.modelLoaded !== undefined && (
|
<div>
|
||||||
<div className="flex items-center justify-between">
|
<h2 className="text-lg font-semibold mb-3 flex items-center gap-2">
|
||||||
<span className="text-sm text-muted-foreground">Modello ML:</span>
|
<Clock className="h-5 w-5" />
|
||||||
<Badge variant={servicesStatus.services.mlBackend.details.modelLoaded ? "default" : "secondary"}>
|
Timer Systemd (Attivita Periodiche)
|
||||||
{servicesStatus.services.mlBackend.details.modelLoaded ? "Caricato" : "Non Caricato"}
|
</h2>
|
||||||
</Badge>
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||||
</div>
|
{timerServices.map((key) => {
|
||||||
)}
|
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">
|
||||||
@ -406,30 +358,27 @@ 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">Verifica tutti i processi IDS attivi:</p>
|
<p className="text-sm font-medium mb-2">Stato di tutti i servizi IDS:</p>
|
||||||
<code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-check-processes">
|
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-all-services">
|
||||||
ps aux | grep -E "python.*(main|syslog_parser)" | grep -v grep
|
systemctl list-units 'ids-*' --all
|
||||||
|
</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 block font-mono" data-testid="code-check-rsyslog">
|
<code className="text-xs bg-muted p-2 rounded-md 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">Esegui training manuale ML:</p>
|
<p className="text-sm font-medium mb-2">Verifica processi IDS attivi:</p>
|
||||||
<code className="text-xs bg-muted p-2 rounded block font-mono" data-testid="code-manual-training">
|
<code className="text-xs bg-muted p-2 rounded-md block font-mono" data-testid="code-check-processes">
|
||||||
curl -X POST http://localhost:8000/train -H "Content-Type: application/json" -d '{"max_records": 10000, "hours_back": 24}'
|
ps aux | grep -E "python.*(main|syslog_parser)" | grep -v grep
|
||||||
</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>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
-- PostgreSQL database dump
|
-- PostgreSQL database dump
|
||||||
--
|
--
|
||||||
|
|
||||||
\restrict eyisBUJGZpxqwUy3cADDz6PSlf2SjvYWDM3PJH885ta9lWx9pkI2q38QNzdHOVh
|
\restrict QQPZgpukcxzRMKOdS5xNsXDiphiHLW5uAuhQxN7luRJ2u8BkVkDOz1h9Un2BrJ0
|
||||||
|
|
||||||
-- 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 eyisBUJGZpxqwUy3cADDz6PSlf2SjvYWDM3PJH885ta9lWx9pkI2q38QNzdHOVh
|
\unrestrict QQPZgpukcxzRMKOdS5xNsXDiphiHLW5uAuhQxN7luRJ2u8BkVkDOz1h9Un2BrJ0
|
||||||
|
|
||||||
|
|||||||
199
server/routes.ts
199
server/routes.ts
@ -780,39 +780,43 @@ 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 = {
|
||||||
mlBackend: { name: "ML Backend Python", status: "unknown", healthy: false, details: null as any },
|
nodeBackend: { ...mkService("Node.js Backend"), systemdUnit: "ids-backend", type: "service" },
|
||||||
database: { name: "PostgreSQL Database", status: "unknown", healthy: false, details: null as any },
|
mlBackend: { ...mkService("ML Backend Python"), systemdUnit: "ids-ml-backend", type: "service" },
|
||||||
syslogParser: { name: "Syslog Parser", status: "unknown", healthy: false, details: null as any },
|
database: { ...mkService("PostgreSQL Database"), systemdUnit: "postgresql-16", type: "service" },
|
||||||
analyticsAggregator: { name: "Analytics Aggregator Timer", status: "unknown", healthy: false, details: null as any },
|
syslogParser: { ...mkService("Syslog Parser"), systemdUnit: "ids-syslog-parser", type: "service" },
|
||||||
|
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 = {
|
services.mlBackend.details = { modelLoaded: data.ml_model === "loaded", timestamp: data.timestamp };
|
||||||
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' ? "Connection refused" : error.message };
|
services.mlBackend.details = { error: error.code === 'ECONNREFUSED' ? "Connessione rifiutata" : error.message };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Database
|
// Check Database
|
||||||
@ -828,87 +832,156 @@ 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 (independent of ML Backend)
|
// Check Syslog Parser via database
|
||||||
try {
|
try {
|
||||||
const recentLogsResult = await db.execute(
|
const recentLogsResult = await db.execute(
|
||||||
sql`SELECT COUNT(*) as count, MAX(timestamp) as last_log
|
sql`SELECT COUNT(*) as count, MAX(timestamp) as last_log FROM network_logs WHERE timestamp > NOW() - INTERVAL '30 minutes'`
|
||||||
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 = {
|
services.syslogParser.details = { recentLogs30min: recentLogCount, lastLog: lastLogTime };
|
||||||
recentLogs30min: recentLogCount,
|
|
||||||
lastLog: lastLogTime,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
const lastLogEverResult = await db.execute(
|
const lastLogEverResult = await db.execute(sql`SELECT MAX(timestamp) as last_log FROM network_logs`);
|
||||||
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 = {
|
services.syslogParser.details = { recentLogs30min: 0, lastLog: lastLogEverRows[0]?.last_log || "Mai", warning: "Nessun log negli ultimi 30 minuti" };
|
||||||
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
|
const latestAnalytics = await db.select().from(networkAnalytics).orderBy(desc(networkAnalytics.date), desc(networkAnalytics.hour)).limit(1);
|
||||||
.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 lastTimestamp = lastRun.toISOString();
|
const hoursSince = (Date.now() - lastRun.getTime()) / (1000 * 60 * 60);
|
||||||
const hoursSinceLastRun = (Date.now() - lastRun.getTime()) / (1000 * 60 * 60);
|
if (hoursSince < 2) {
|
||||||
|
|
||||||
if (hoursSinceLastRun < 2) {
|
|
||||||
services.analyticsAggregator.status = "running";
|
services.analyticsAggregator.status = "running";
|
||||||
services.analyticsAggregator.healthy = true;
|
services.analyticsAggregator.healthy = true;
|
||||||
services.analyticsAggregator.details = {
|
services.analyticsAggregator.details = { lastRun: latestAnalytics[0].date, hoursSinceLastRun: hoursSince.toFixed(1) };
|
||||||
lastRun: latestAnalytics[0].date,
|
|
||||||
lastTimestamp,
|
|
||||||
hoursSinceLastRun: hoursSinceLastRun.toFixed(1),
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
services.analyticsAggregator.status = "idle";
|
services.analyticsAggregator.status = "idle";
|
||||||
services.analyticsAggregator.healthy = false;
|
services.analyticsAggregator.details = { lastRun: latestAnalytics[0].date, hoursSinceLastRun: hoursSince.toFixed(1), warning: "Nessuna aggregazione nelle ultime 2 ore" };
|
||||||
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.healthy = false;
|
services.analyticsAggregator.details = { error: "Nessun dato analytics trovato" };
|
||||||
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" });
|
||||||
@ -916,7 +989,11 @@ 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 = ["ids-ml-backend", "ids-syslog-parser"];
|
const ALLOWED_SERVICES = [
|
||||||
|
"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) => {
|
||||||
|
|||||||
16
version.json
16
version.json
@ -1,7 +1,13 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.121",
|
"version": "1.0.122",
|
||||||
"lastUpdate": "2026-02-17T08:11:26.352Z",
|
"lastUpdate": "2026-02-17T09:13:40.571Z",
|
||||||
"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",
|
||||||
@ -295,12 +301,6 @@
|
|||||||
"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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user