source: server.py@ 640ed89

Last change on this file since 640ed89 was 640ed89, checked in by istevanoska <ilinastevanoska@…>, 7 months ago

Initial commit

  • Property mode set to 100644
File size: 67.8 KB
RevLine 
[640ed89]1#!/usr/bin/env python3
2"""
3POPRAVEN SERVER со Sysmon обработка
4"""
5import traceback
6
7from flask import Flask, request, jsonify, render_template_string
8import json
9import sqlite3
10from datetime import datetime, timedelta
11import time
12import os
13import socket
14from collections import defaultdict
15import threading
16import requests
17from openai import OpenAI
18from openai import OpenAI
19
20OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
21client = OpenAI(
22 api_key=OPENAI_API_KEY,
23 timeout=15 # максимум 15 секунди да чека
24) if OPENAI_API_KEY else None
25
26
27
28
29
30app = Flask(__name__)
31
32DB_FILE = "lan_logs_sysmon.db"
33LOG_DIR = "received_data_sysmon"
34
35
36def init_database():
37 """Креирај подобрена база со Sysmon табели"""
38 os.makedirs(LOG_DIR, exist_ok=True)
39
40 conn = sqlite3.connect(DB_FILE)
41 c = conn.cursor()
42
43 # Табела за компјутери
44 c.execute('''CREATE TABLE IF NOT EXISTS computers
45 (id INTEGER PRIMARY KEY AUTOINCREMENT,
46 name TEXT UNIQUE,
47 user TEXT,
48 ip TEXT,
49 os TEXT,
50 first_seen TIMESTAMP,
51 last_seen TIMESTAMP,
52 sysmon_available BOOLEAN DEFAULT 0)''')
53
54 # Табела за историја
55 c.execute('''CREATE TABLE IF NOT EXISTS computer_history
56 (id INTEGER PRIMARY KEY AUTOINCREMENT,
57 computer_id INTEGER,
58 cpu_usage REAL,
59 ram_usage REAL,
60 disk_usage REAL,
61 network_sent_mb REAL,
62 network_recv_mb REAL,
63 timestamp TIMESTAMP,
64 FOREIGN KEY(computer_id) REFERENCES computers(id))''')
65
66 # Табела за процеси
67 c.execute('''CREATE TABLE IF NOT EXISTS computer_processes
68 (id INTEGER PRIMARY KEY AUTOINCREMENT,
69 computer_id INTEGER,
70 pid INTEGER,
71 name TEXT,
72 cpu_percent REAL,
73 memory_mb REAL,
74 username TEXT,
75 cmdline TEXT,
76 timestamp TIMESTAMP,
77 FOREIGN KEY(computer_id) REFERENCES computers(id))''')
78
79 # НОВА: Табела за Sysmon настани
80 c.execute('''CREATE TABLE IF NOT EXISTS sysmon_events
81 (id INTEGER PRIMARY KEY AUTOINCREMENT,
82 computer_id INTEGER,
83 event_id INTEGER,
84 event_type TEXT,
85 message TEXT,
86 timestamp TIMESTAMP,
87 details TEXT,
88 FOREIGN KEY(computer_id) REFERENCES computers(id))''')
89
90 # НОВА: Табела за детални мрежни конекции
91 c.execute('''CREATE TABLE IF NOT EXISTS network_connections
92 (id INTEGER PRIMARY KEY AUTOINCREMENT,
93 computer_id INTEGER,
94 pid INTEGER,
95 local_address TEXT,
96 remote_address TEXT,
97 status TEXT,
98 process_name TEXT,
99 timestamp TIMESTAMP,
100 FOREIGN KEY(computer_id) REFERENCES computers(id))''')
101
102 # НОВА: Табела за безбедносни настани
103 c.execute('''CREATE TABLE IF NOT EXISTS security_alerts
104 (id INTEGER PRIMARY KEY AUTOINCREMENT,
105 computer_id INTEGER,
106 alert_type TEXT,
107 severity TEXT,
108 description TEXT,
109 timestamp TIMESTAMP,
110 resolved BOOLEAN DEFAULT 0,
111 FOREIGN KEY(computer_id) REFERENCES computers(id))''')
112
113 conn.commit()
114 conn.close()
115
116 print(f"✅ Database created: {DB_FILE}")
117
118
119@app.route('/receive', methods=['POST'])
120def receive_data():
121 try:
122 data = request.json
123
124 if not data:
125 return jsonify({"error": "No data"}), 400
126
127 info = data['info']
128 computer_name = info['computer_name']
129 timestamp = datetime.now()
130 security_data = data.get('security_data', {})
131
132 conn = sqlite3.connect(DB_FILE)
133 c = conn.cursor()
134
135 # Провери дали компјутерот веќе постои
136 c.execute("SELECT id FROM computers WHERE name = ?", (computer_name,))
137 result = c.fetchone()
138
139 if result:
140 # Компјутерот постои - ажурирај го
141 computer_id = result[0]
142 c.execute('''UPDATE computers
143 SET user = ?, ip = ?, os = ?, last_seen = ?, sysmon_available = ?
144 WHERE id = ?''',
145 (info['user'], info['ip_address'], info['os'],
146 timestamp.isoformat(), info.get('is_sysmon_available', 0),
147 computer_id))
148 else:
149 # Нов компјутер - додади го
150 c.execute('''INSERT INTO computers
151 (name, user, ip, os, first_seen, last_seen, sysmon_available)
152 VALUES (?, ?, ?, ?, ?, ?, ?)''',
153 (computer_name, info['user'], info['ip_address'],
154 info['os'], timestamp.isoformat(), timestamp.isoformat(),
155 info.get('is_sysmon_available', 0)))
156 computer_id = c.lastrowid
157
158 # Додади во историја
159 c.execute('''INSERT INTO computer_history
160 (computer_id, cpu_usage, ram_usage, disk_usage,
161 network_sent_mb, network_recv_mb, timestamp)
162 VALUES (?, ?, ?, ?, ?, ?, ?)''',
163 (computer_id, info['cpu_usage'], info['ram_usage'],
164 info['disk_usage'], info.get('network_sent_mb', 0),
165 info.get('network_recv_mb', 0), info['timestamp']))
166
167 # Зачувај ги процесите
168 for proc in data.get('processes', []):
169 c.execute('''INSERT INTO computer_processes
170 (computer_id, pid, name, cpu_percent, memory_mb,
171 username, cmdline, timestamp)
172 VALUES (?, ?, ?, ?, ?, ?, ?, ?)''',
173 (computer_id, proc.get('pid'), proc.get('name'),
174 proc.get('cpu_percent', 0), proc.get('memory_mb', 0),
175 proc.get('user'), proc.get('cmdline'), info['timestamp']))
176
177 # ЗАЧУВАЈ SYSMON НАСТАНИ
178 sysmon_events_count = 0
179 for event in security_data.get('sysmon_events', []):
180 try:
181 c.execute('''INSERT INTO sysmon_events
182 (computer_id, event_id, event_type, message,
183 timestamp, details)
184 VALUES (?, ?, ?, ?, ?, ?)''',
185 (computer_id, event.get('event_id'),
186 event.get('event_type', 'Unknown'),
187 event.get('message', ''),
188 event.get('timestamp', info['timestamp']),
189 json.dumps(event)))
190 sysmon_events_count += 1
191 except Exception as e:
192 print(f"Error saving Sysmon event: {e}")
193
194 # ЗАЧУВАЈ МРЕЖНИ КОНЕКЦИИ
195 for conn_data in security_data.get('network_connections', []):
196 try:
197 c.execute('''INSERT INTO network_connections
198 (computer_id, pid, local_address, remote_address,
199 status, process_name, timestamp)
200 VALUES (?, ?, ?, ?, ?, ?, ?)''',
201 (computer_id, conn_data.get('pid'),
202 conn_data.get('local_address'),
203 conn_data.get('remote_address'),
204 conn_data.get('status'),
205 conn_data.get('process_name'),
206 info['timestamp']))
207 except Exception as e:
208 print(f"Error saving network connection: {e}")
209
210 conn.commit()
211 conn.close()
212
213 # Зачувај во JSON
214 save_json_file(data)
215
216 print(f"[{timestamp.strftime('%H:%M:%S')}] 📥 {computer_name}: "
217 f"CPU {info['cpu_usage']}% | Sysmon: {sysmon_events_count} events")
218
219 return jsonify({
220 "status": "success",
221 "message": f"Data saved for {computer_name}",
222 "computer_id": computer_id,
223 "event_count": sysmon_events_count,
224 "timestamp": timestamp.isoformat()
225 })
226
227 except Exception as e:
228 print(f"[-] Грешка: {e}")
229 return jsonify({"error": str(e)}), 500
230
231
232def save_json_file(data):
233 """Зачувај во JSON"""
234 info = data['info']
235 computer_name = info['computer_name']
236 date_str = datetime.now().strftime("%Y%m%d_%H")
237
238 # Креирај folder за компјутерот
239 computer_dir = os.path.join(LOG_DIR, computer_name)
240 os.makedirs(computer_dir, exist_ok=True)
241
242 # Фајл за секој час (за подобро организирање)
243 filepath = os.path.join(computer_dir, f"{date_str}.json")
244
245 # Додади во постоечки фајл или креирај нов
246 if os.path.exists(filepath):
247 with open(filepath, 'r', encoding='utf-8') as f:
248 existing_data = json.load(f)
249 else:
250 existing_data = []
251
252 # Додади нов запис
253 existing_data.append({
254 "timestamp": info['timestamp'],
255 "info": info,
256 "processes_count": len(data.get('processes', [])),
257 "sysmon_events_count": len(data.get('security_data', {}).get('sysmon_events', []))
258 })
259
260 # Зачувај
261 with open(filepath, 'w', encoding='utf-8') as f:
262 json.dump(existing_data, f, indent=2, ensure_ascii=False)
263
264
265@app.route('/')
266def dashboard():
267 """Главен dashboard"""
268 conn = sqlite3.connect(DB_FILE)
269 c = conn.cursor()
270
271 # Земaj ги сите компјутери
272 c.execute('''SELECT name, user, ip, os, first_seen, last_seen, sysmon_available
273 FROM computers
274 ORDER BY last_seen DESC''')
275
276 computers = []
277 for row in c.fetchall():
278 computer_name = row[0]
279
280 # Пресметaj колку записи има во последните 24 часа
281 c.execute('''SELECT COUNT(*) FROM computer_history
282 WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
283 AND timestamp > datetime('now', '-24 hours')''',
284 (computer_name,))
285 recent_logs = c.fetchone()[0]
286
287 # Земaj ги последните 5 записи за CPU/RAM
288 c.execute('''SELECT cpu_usage, ram_usage, timestamp
289 FROM computer_history
290 WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
291 ORDER BY timestamp DESC LIMIT 5''',
292 (computer_name,))
293 recent_metrics = c.fetchall()
294
295 # Sysmon настани за последните 24 часа
296 c.execute('''SELECT COUNT(*) FROM sysmon_events
297 WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
298 AND timestamp > datetime('now', '-24 hours')''',
299 (computer_name,))
300 recent_sysmon = c.fetchone()[0]
301
302 # Пресметaj просек
303 avg_cpu = sum(m[0] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
304 avg_ram = sum(m[1] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
305
306 # Статус
307 last_seen = datetime.fromisoformat(row[5])
308 time_diff = (datetime.now() - last_seen).seconds
309
310 if time_diff < 60:
311 status = 'online'
312 status_color = 'green'
313 elif time_diff < 300:
314 status = 'idle'
315 status_color = 'orange'
316 else:
317 status = 'offline'
318 status_color = 'gray'
319
320 computers.append({
321 'name': row[0],
322 'user': row[1],
323 'ip': row[2],
324 'os': row[3],
325 'first_seen': row[4],
326 'last_seen': row[5],
327 'sysmon_available': bool(row[6]),
328 'recent_logs': recent_logs,
329 'recent_sysmon': recent_sysmon,
330 'avg_cpu': round(avg_cpu, 1),
331 'avg_ram': round(avg_ram, 1),
332 'status': status,
333 'status_color': status_color
334 })
335
336 conn.close()
337
338 # Генерирај HTML
339 return render_template_string('''
340 <!DOCTYPE html>
341 <html>
342 <head>
343 <title>LAN Log Server - Sysmon Dashboard</title>
344 <meta charset="utf-8">
345 <meta name="viewport" content="width=device-width, initial-scale=1">
346 <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
347 <style>
348 :root {
349 --primary-color: #2c3e50;
350 --secondary-color: #3498db;
351 --danger-color: #e74c3c;
352 --warning-color: #f39c12;
353 --success-color: #27ae60;
354 }
355
356 body {
357 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
358 margin: 0;
359 padding: 20px;
360 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
361 min-height: 100vh;
362 }
363
364 .container {
365 max-width: 1400px;
366 margin: 0 auto;
367 background: white;
368 border-radius: 15px;
369 box-shadow: 0 20px 40px rgba(0,0,0,0.1);
370 overflow: hidden;
371 }
372
373 .header {
374 background: linear-gradient(to right, var(--primary-color), #1a2530);
375 color: white;
376 padding: 30px 40px;
377 border-radius: 15px 15px 0 0;
378 }
379
380 .header h1 {
381 margin: 0;
382 font-size: 2.5em;
383 display: flex;
384 align-items: center;
385 gap: 15px;
386 }
387
388 .stats-grid {
389 display: grid;
390 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
391 gap: 20px;
392 padding: 30px;
393 }
394
395 .stat-card {
396 background: white;
397 padding: 25px;
398 border-radius: 10px;
399 box-shadow: 0 5px 15px rgba(0,0,0,0.08);
400 border-left: 5px solid var(--secondary-color);
401 transition: transform 0.3s ease;
402 }
403
404 .stat-card:hover {
405 transform: translateY(-5px);
406 }
407
408 .stat-card.sysmon {
409 border-left-color: var(--danger-color);
410 }
411
412 .stat-card.network {
413 border-left-color: var(--success-color);
414 }
415
416 .stat-value {
417 font-size: 2.8em;
418 font-weight: bold;
419 margin: 10px 0;
420 }
421
422 .stat-label {
423 color: #666;
424 font-size: 0.9em;
425 text-transform: uppercase;
426 letter-spacing: 1px;
427 }
428
429 .computers-grid {
430 display: grid;
431 grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
432 gap: 25px;
433 padding: 30px;
434 }
435
436 .computer-card {
437 background: white;
438 border-radius: 12px;
439 padding: 25px;
440 box-shadow: 0 8px 20px rgba(0,0,0,0.1);
441 cursor: pointer;
442 transition: all 0.3s ease;
443 border: 2px solid transparent;
444 position: relative;
445 overflow: hidden;
446 }
447
448 .computer-card:hover {
449 transform: translateY(-8px);
450 box-shadow: 0 15px 30px rgba(0,0,0,0.15);
451 border-color: var(--secondary-color);
452 }
453
454 .computer-card::before {
455 content: '';
456 position: absolute;
457 top: 0;
458 left: 0;
459 right: 0;
460 height: 4px;
461 background: linear-gradient(to right, #667eea, #764ba2);
462 }
463
464 .computer-name {
465 font-size: 1.4em;
466 font-weight: bold;
467 color: var(--primary-color);
468 margin-bottom: 10px;
469 display: flex;
470 justify-content: space-between;
471 align-items: center;
472 }
473
474 .status-indicator {
475 display: inline-block;
476 width: 12px;
477 height: 12px;
478 border-radius: 50%;
479 margin-right: 8px;
480 }
481
482 .computer-info {
483 color: #555;
484 margin: 8px 0;
485 font-size: 0.95em;
486 display: flex;
487 align-items: center;
488 gap: 10px;
489 }
490
491 .computer-info i {
492 width: 20px;
493 color: #777;
494 }
495
496 .stats-row {
497 display: flex;
498 justify-content: space-between;
499 margin-top: 20px;
500 padding-top: 20px;
501 border-top: 1px solid #eee;
502 }
503
504 .stat-item {
505 text-align: center;
506 }
507
508 .stat-number {
509 font-size: 1.6em;
510 font-weight: bold;
511 color: var(--primary-color);
512 }
513
514 .stat-text {
515 font-size: 0.8em;
516 color: #777;
517 margin-top: 5px;
518 }
519
520 .sysmon-badge {
521 background: var(--danger-color);
522 color: white;
523 padding: 3px 10px;
524 border-radius: 20px;
525 font-size: 0.8em;
526 display: inline-block;
527 margin-left: 10px;
528 }
529
530 /* Модал */
531 .modal {
532 display: none;
533 position: fixed;
534 top: 0;
535 left: 0;
536 right: 0;
537 bottom: 0;
538 background: rgba(0,0,0,0.7);
539 z-index: 1000;
540 overflow-y: auto;
541 }
542
543 .modal-content {
544 background: white;
545 margin: 50px auto;
546 width: 90%;
547 max-width: 1200px;
548 border-radius: 15px;
549 animation: modalSlideIn 0.3s ease;
550 }
551
552 @keyframes modalSlideIn {
553 from {
554 transform: translateY(-50px);
555 opacity: 0;
556 }
557 to {
558 transform: translateY(0);
559 opacity: 1;
560 }
561 }
562
563 .modal-header {
564 padding: 25px 30px;
565 background: var(--primary-color);
566 color: white;
567 border-radius: 15px 15px 0 0;
568 display: flex;
569 justify-content: space-between;
570 align-items: center;
571 }
572
573 .close-btn {
574 background: none;
575 border: none;
576 color: white;
577 font-size: 1.8em;
578 cursor: pointer;
579 width: 40px;
580 height: 40px;
581 border-radius: 50%;
582 display: flex;
583 align-items: center;
584 justify-content: center;
585 transition: background 0.2s;
586 }
587
588 .close-btn:hover {
589 background: rgba(255,255,255,0.1);
590 }
591
592 .tabs {
593 display: flex;
594 border-bottom: 1px solid #ddd;
595 padding: 0 30px;
596 background: #f8f9fa;
597 }
598
599 .tab-btn {
600 padding: 15px 25px;
601 background: none;
602 border: none;
603 cursor: pointer;
604 font-size: 1em;
605 color: #666;
606 position: relative;
607 transition: all 0.3s;
608 }
609
610 .tab-btn:hover {
611 color: var(--primary-color);
612 }
613
614 .tab-btn.active {
615 color: var(--primary-color);
616 font-weight: bold;
617 }
618
619 .tab-btn.active::after {
620 content: '';
621 position: absolute;
622 bottom: -1px;
623 left: 0;
624 right: 0;
625 height: 3px;
626 background: var(--secondary-color);
627 }
628
629 .tab-content {
630 padding: 30px;
631 display: none;
632 }
633
634 .tab-content.active {
635 display: block;
636 }
637
638 .events-table {
639 width: 100%;
640 border-collapse: collapse;
641 margin-top: 20px;
642 }
643
644 .events-table th {
645 background: #f8f9fa;
646 padding: 15px;
647 text-align: left;
648 border-bottom: 2px solid #dee2e6;
649 color: var(--primary-color);
650 }
651
652 .events-table td {
653 padding: 12px 15px;
654 border-bottom: 1px solid #eee;
655 }
656
657 .events-table tr:hover {
658 background: #f8f9fa;
659 }
660
661 .severity-high {
662 background: #ffe6e6;
663 color: var(--danger-color);
664 padding: 3px 10px;
665 border-radius: 3px;
666 font-weight: bold;
667 }
668
669 .severity-medium {
670 background: #fff3cd;
671 color: var(--warning-color);
672 padding: 3px 10px;
673 border-radius: 3px;
674 }
675
676 .event-details {
677 max-width: 500px;
678 word-wrap: break-word;
679 font-family: monospace;
680 font-size: 0.9em;
681 }
682
683 .filter-bar {
684 padding: 20px 30px;
685 background: #f8f9fa;
686 border-bottom: 1px solid #ddd;
687 }
688
689 .search-box {
690 padding: 10px 15px;
691 border: 1px solid #ddd;
692 border-radius: 5px;
693 width: 300px;
694 }
695
696 .btn {
697 padding: 10px 20px;
698 background: var(--secondary-color);
699 color: white;
700 border: none;
701 border-radius: 5px;
702 cursor: pointer;
703 transition: background 0.3s;
704 }
705
706 .btn:hover {
707 background: #2980b9;
708 }
709
710 .chart-container {
711 height: 300px;
712 margin: 20px 0;
713 }
714
715 @media (max-width: 768px) {
716 .computers-grid {
717 grid-template-columns: 1fr;
718 }
719
720 .modal-content {
721 width: 95%;
722 margin: 20px auto;
723 }
724 }
725 </style>
726 </head>
727 <body>
728 <div class="container">
729 <div class="header">
730 <h1>
731 <span>🛡️ LAN Security Monitor</span>
732 <small style="font-size: 0.5em; opacity: 0.8;">Sysmon Edition</small>
733 </h1>
734 <p>Server: {{ socket.gethostbyname(socket.gethostname()) }}:5555 | Total Computers: {{ computers|length }}</p>
735 </div>
736
737 <div class="stats-grid">
738 <div class="stat-card">
739 <div class="stat-label">Total Computers</div>
740 <div class="stat-value">{{ computers|length }}</div>
741 <div class="stat-text">Monitored Systems</div>
742 </div>
743
744 <div class="stat-card sysmon">
745 <div class="stat-label">Sysmon Events (24h)</div>
746 <div class="stat-value" id="total-sysmon">0</div>
747 <div class="stat-text">Security Events</div>
748 </div>
749
750 <div class="stat-card network">
751 <div class="stat-label">Network Connections</div>
752 <div class="stat-value" id="total-network">0</div>
753 <div class="stat-text">Active Connections</div>
754 </div>
755 </div>
756
757 <div style="padding: 0 30px;">
758 <h2>Connected Computers</h2>
759 <div class="computers-grid" id="computers-grid">
760 {% for comp in computers %}
761 <div class="computer-card" onclick="showComputerDetails('{{ comp.name }}')">
762 <div class="computer-name">
763 {{ comp.name }}
764 <span class="status-indicator" style="background: {{ comp.status_color }}"></span>
765 </div>
766
767 <div class="computer-info">
768 <i>👤</i> {{ comp.user }}
769 </div>
770
771 <div class="computer-info">
772 <i>🌐</i> {{ comp.ip }}
773 </div>
774
775 <div class="computer-info">
776 <i>💻</i> {{ comp.os[:40] }}
777 </div>
778
779 <div class="computer-info">
780 <i>⏰</i> Last seen: {{ comp.last_seen[11:19] }}
781 </div>
782
783 {% if comp.sysmon_available %}
784 <div class="computer-info">
785 <i>🛡️</i> Sysmon Available
786 <span class="sysmon-badge">{{ comp.recent_sysmon }} events</span>
787 </div>
788 {% endif %}
789
790 <div class="stats-row">
791 <div class="stat-item">
792 <div class="stat-number">{{ comp.avg_cpu }}%</div>
793 <div class="stat-text">CPU</div>
794 </div>
795 <div class="stat-item">
796 <div class="stat-number">{{ comp.avg_ram }}%</div>
797 <div class="stat-text">RAM</div>
798 </div>
799 <div class="stat-item">
800 <div class="stat-number">{{ comp.recent_logs }}</div>
801 <div class="stat-text">Logs (24h)</div>
802 </div>
803 </div>
804 </div>
805 {% endfor %}
806 </div>
807 </div>
808 </div>
809
810 <!-- Детален модал -->
811 <div class="modal" id="computer-modal">
812 <div class="modal-content">
813 <div class="modal-header">
814 <h2 id="modal-title">Computer Details</h2>
815 <button class="close-btn" onclick="closeModal()">×</button>
816 </div>
817
818 <div class="filter-bar">
819 <input type="text" id="event-search" class="search-box" placeholder="Search events..."
820 onkeyup="filterEvents()">
821 <button class="btn" onclick="exportData()">Export Data</button>
822 </div>
823
824 <div class="tabs">
825 <button class="tab-btn active" onclick="switchTab('overview')">Overview</button>
826 <button class="tab-btn" onclick="switchTab('sysmon')">Sysmon Events</button>
827 <button class="tab-btn" onclick="switchTab('processes')">Processes</button>
828 <button class="tab-btn" onclick="switchTab('network')">Network</button>
829 <button class="tab-btn" onclick="switchTab('charts')">Charts</button>
830 </div>
831
832 <!-- Overview Tab -->
833 <div class="tab-content active" id="overview-tab">
834 <h3>System Overview</h3>
835 <div id="overview-content"></div>
836 </div>
837
838 <!-- Sysmon Events Tab -->
839 <div class="tab-content" id="sysmon-tab">
840 <h3>Security Events</h3>
841 <table class="events-table" id="sysmon-table">
842 <thead>
843 <tr>
844 <th>Time</th>
845 <th>Event ID</th>
846 <th>Type</th>
847 <th>Severity</th>
848 <th>Message</th>
849 <th>Actions</th>
850 </tr>
851 </thead>
852 <tbody id="sysmon-events">
853 <!-- Events will be populated here -->
854 </tbody>
855 </table>
856 </div>
857
858 <!-- Processes Tab -->
859 <div class="tab-content" id="processes-tab">
860 <h3>Running Processes</h3>
861 <table class="events-table">
862 <thead>
863 <tr>
864 <th>PID</th>
865 <th>Name</th>
866 <th>User</th>
867 <th>CPU %</th>
868 <th>Memory</th>
869 <th>Command Line</th>
870 </tr>
871 </thead>
872 <tbody id="processes-list">
873 <!-- Processes will be populated here -->
874 </tbody>
875 </table>
876 </div>
877
878 <!-- Network Tab -->
879 <div class="tab-content" id="network-tab">
880 <h3>Network Connections</h3>
881 <table class="events-table">
882 <thead>
883 <tr>
884 <th>PID</th>
885 <th>Process</th>
886 <th>Local Address</th>
887 <th>Remote Address</th>
888 <th>Status</th>
889 <th>Timestamp</th>
890 </tr>
891 </thead>
892 <tbody id="network-connections">
893 <!-- Connections will be populated here -->
894 </tbody>
895 </table>
896 </div>
897
898 <!-- Charts Tab -->
899 <div class="tab-content" id="charts-tab">
900 <h3>Performance Analytics</h3>
901 <div class="chart-container">
902 <canvas id="performance-chart"></canvas>
903 </div>
904 </div>
905 </div>
906 </div>
907
908 <!-- Event Details Modal -->
909 <div class="modal" id="event-modal" style="display: none;">
910 <div class="modal-content" style="max-width: 800px;">
911 <div class="modal-header">
912 <h2>Event Details</h2>
913 <button class="close-btn" onclick="closeEventModal()">×</button>
914 </div>
915 <div style="padding: 30px;">
916 <pre id="event-details-content" style="background: #f5f5f5; padding: 20px; border-radius: 5px; overflow: auto; max-height: 500px;"></pre>
917 </div>
918 </div>
919 </div>
920
921 <script>
922 let currentComputer = null;
923 let allSysmonEvents = [];
924
925 function updateStats() {
926 // Ажурирај ги вкупните статистики
927 fetch('/api/stats')
928 .then(r => r.json())
929 .then(data => {
930 document.getElementById('total-sysmon').textContent = data.total_sysmon_events;
931 document.getElementById('total-network').textContent = data.total_network_connections;
932 });
933 }
934
935 function showComputerDetails(computerName) {
936 currentComputer = computerName;
937
938 // Покажи модал
939 document.getElementById('computer-modal').style.display = 'block';
940 document.getElementById('modal-title').textContent = computerName;
941
942 // Вчитувај податоци
943 loadComputerDetails(computerName);
944 }
945
946 function loadComputerDetails(computerName) {
947 // Вчитувај целосни податоци за компјутерот
948 fetch(`/api/computer/${encodeURIComponent(computerName)}`)
949 .then(response => response.json())
950 .then(data => {
951 populateOverview(data);
952 populateSysmonEvents(data);
953 populateProcesses(data);
954 populateNetwork(data);
955 updateCharts(data);
956 })
957 .catch(error => {
958 console.error('Error loading details:', error);
959 });
960 }
961
962 function populateOverview(data) {
963 const overview = document.getElementById('overview-content');
964 overview.innerHTML = `
965 <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px;">
966 <div>
967 <h4>System Information</h4>
968 <p><strong>User:</strong> ${data.computer.user}</p>
969 <p><strong>IP:</strong> ${data.computer.ip}</p>
970 <p><strong>OS:</strong> ${data.computer.os}</p>
971 <p><strong>First Seen:</strong> ${new Date(data.computer.first_seen).toLocaleString()}</p>
972 <p><strong>Last Seen:</strong> ${new Date(data.computer.last_seen).toLocaleString()}</p>
973 <p><strong>Total Logs:</strong> ${data.computer.total_logs}</p>
974 </div>
975 <div>
976 <h4>Current Status</h4>
977 <p><strong>CPU Usage:</strong> ${data.current_metrics?.cpu_usage || 0}%</p>
978 <p><strong>RAM Usage:</strong> ${data.current_metrics?.ram_usage || 0}%</p>
979 <p><strong>Disk Usage:</strong> ${data.current_metrics?.disk_usage || 0}%</p>
980 <p><strong>Network Sent:</strong> ${data.current_metrics?.network_sent_mb || 0} MB</p>
981 <p><strong>Network Received:</strong> ${data.current_metrics?.network_recv_mb || 0} MB</p>
982 </div>
983 </div>
984 `;
985 }
986
987 function populateSysmonEvents(data) {
988 const tableBody = document.getElementById('sysmon-events');
989 tableBody.innerHTML = '';
990 allSysmonEvents = data.sysmon_events || [];
991
992 allSysmonEvents.forEach(event => {
993 const row = tableBody.insertRow();
994 const time = new Date(event.timestamp).toLocaleTimeString();
995 const severity = getSeverityForEvent(event.event_id);
996
997 row.innerHTML = `
998 <td>${time}</td>
999 <td>${event.event_id}</td>
1000 <td>${event.event_type}</td>
1001 <td><span class="severity-${severity}">${severity.toUpperCase()}</span></td>
1002 <td class="event-details">${event.message.substring(0, 100)}...</td>
1003 <td>
1004 <button class="btn" style="padding: 5px 10px; font-size: 0.9em;"
1005 onclick="showEventDetails(${JSON.stringify(event).replace(/"/g, '&quot;')})">
1006 View Details
1007 </button>
1008 </td>
1009 `;
1010 });
1011 }
1012
1013 function populateProcesses(data) {
1014 const tableBody = document.getElementById('processes-list');
1015 tableBody.innerHTML = '';
1016
1017 (data.recent_processes || []).forEach(proc => {
1018 const row = tableBody.insertRow();
1019 row.innerHTML = `
1020 <td>${proc.pid}</td>
1021 <td>${proc.name}</td>
1022 <td>${proc.username}</td>
1023 <td>${proc.cpu_percent || 0}</td>
1024 <td>${proc.memory_mb || 0} MB</td>
1025 <td class="event-details">${proc.cmdline || ''}</td>
1026 `;
1027 });
1028 }
1029
1030 function populateNetwork(data) {
1031 const tableBody = document.getElementById('network-connections');
1032 tableBody.innerHTML = '';
1033
1034 (data.network_connections || []).forEach(conn => {
1035 const row = tableBody.insertRow();
1036 const time = new Date(conn.timestamp).toLocaleTimeString();
1037 row.innerHTML = `
1038 <td>${conn.pid}</td>
1039 <td>${conn.process_name || 'N/A'}</td>
1040 <td>${conn.local_address || 'N/A'}</td>
1041 <td>${conn.remote_address || 'N/A'}</td>
1042 <td>${conn.status}</td>
1043 <td>${time}</td>
1044 `;
1045 });
1046 }
1047
1048 function updateCharts(data) {
1049 const ctx = document.getElementById('performance-chart').getContext('2d');
1050 const history = data.history || [];
1051
1052 const labels = history.slice(-20).map(h =>
1053 new Date(h.timestamp).toLocaleTimeString()
1054 );
1055 const cpuData = history.slice(-20).map(h => h.cpu_usage);
1056 const ramData = history.slice(-20).map(h => h.ram_usage);
1057
1058 if(window.performanceChart) {
1059 window.performanceChart.destroy();
1060 }
1061
1062 window.performanceChart = new Chart(ctx, {
1063 type: 'line',
1064 data: {
1065 labels: labels,
1066 datasets: [
1067 {
1068 label: 'CPU Usage %',
1069 data: cpuData,
1070 borderColor: 'rgb(255, 99, 132)',
1071 backgroundColor: 'rgba(255, 99, 132, 0.2)',
1072 tension: 0.4
1073 },
1074 {
1075 label: 'RAM Usage %',
1076 data: ramData,
1077 borderColor: 'rgb(54, 162, 235)',
1078 backgroundColor: 'rgba(54, 162, 235, 0.2)',
1079 tension: 0.4
1080 }
1081 ]
1082 },
1083 options: {
1084 responsive: true,
1085 maintainAspectRatio: false,
1086 scales: {
1087 y: {
1088 beginAtZero: true,
1089 max: 100
1090 }
1091 }
1092 }
1093 });
1094 }
1095
1096 function getSeverityForEvent(eventId) {
1097 const highSeverity = [1, 3, 8, 10]; // Process creation, network, remote thread, process access
1098 const mediumSeverity = [5, 6, 7, 11, 22]; // Process termination, driver/image load, file create, DNS
1099
1100 if (highSeverity.includes(parseInt(eventId))) return 'high';
1101 if (mediumSeverity.includes(parseInt(eventId))) return 'medium';
1102 return 'low';
1103 }
1104
1105 function showEventDetails(event) {
1106 document.getElementById('event-details-content').textContent =
1107 JSON.stringify(event, null, 2);
1108 document.getElementById('event-modal').style.display = 'block';
1109 }
1110
1111 function closeEventModal() {
1112 document.getElementById('event-modal').style.display = 'none';
1113 }
1114
1115 function filterEvents() {
1116 const searchTerm = document.getElementById('event-search').value.toLowerCase();
1117 const tableBody = document.getElementById('sysmon-events');
1118 tableBody.innerHTML = '';
1119
1120 allSysmonEvents
1121 .filter(event =>
1122 event.message.toLowerCase().includes(searchTerm) ||
1123 event.event_type.toLowerCase().includes(searchTerm) ||
1124 event.event_id.toString().includes(searchTerm)
1125 )
1126 .forEach(event => {
1127 const row = tableBody.insertRow();
1128 const time = new Date(event.timestamp).toLocaleTimeString();
1129 const severity = getSeverityForEvent(event.event_id);
1130
1131 row.innerHTML = `
1132 <td>${time}</td>
1133 <td>${event.event_id}</td>
1134 <td>${event.event_type}</td>
1135 <td><span class="severity-${severity}">${severity.toUpperCase()}</span></td>
1136 <td class="event-details">${event.message.substring(0, 100)}...</td>
1137 <td>
1138 <button class="btn" style="padding: 5px 10px; font-size: 0.9em;"
1139 onclick="showEventDetails(${JSON.stringify(event).replace(/"/g, '&quot;')})">
1140 View Details
1141 </button>
1142 </td>
1143 `;
1144 });
1145 }
1146
1147 function switchTab(tabName) {
1148 // Деактивирај сите tab-ови
1149 document.querySelectorAll('.tab-btn').forEach(btn => {
1150 btn.classList.remove('active');
1151 });
1152 document.querySelectorAll('.tab-content').forEach(tab => {
1153 tab.classList.remove('active');
1154 });
1155
1156 // Активирај избраниот tab
1157 document.querySelector(`[onclick="switchTab('${tabName}')"]`).classList.add('active');
1158 document.getElementById(`${tabName}-tab`).classList.add('active');
1159 }
1160
1161 function closeModal() {
1162 document.getElementById('computer-modal').style.display = 'none';
1163 currentComputer = null;
1164 allSysmonEvents = [];
1165 }
1166
1167 function exportData() {
1168 if (!currentComputer) return;
1169
1170 fetch(`/api/export/${encodeURIComponent(currentComputer)}`)
1171 .then(response => response.blob())
1172 .then(blob => {
1173 const url = window.URL.createObjectURL(blob);
1174 const a = document.createElement('a');
1175 a.href = url;
1176 a.download = `${currentComputer}_export_${new Date().toISOString().slice(0,10)}.json`;
1177 document.body.appendChild(a);
1178 a.click();
1179 document.body.removeChild(a);
1180 });
1181 }
1182
1183 // Auto-refresh
1184 setInterval(() => {
1185 updateStats();
1186
1187 // Ажурирај детали ако модал е отворен
1188 if (currentComputer && document.getElementById('computer-modal').style.display === 'block') {
1189 loadComputerDetails(currentComputer);
1190 }
1191 }, 30000);
1192
1193 // Затвори модал со Escape
1194 document.addEventListener('keydown', (e) => {
1195 if (e.key === 'Escape') {
1196 closeModal();
1197 closeEventModal();
1198 }
1199 });
1200
1201 // Иницијализирај статистики при старт
1202 updateStats();
1203 </script>
1204 </body>
1205 </html>
1206 ''', computers=computers, socket=socket, datetime=datetime)
1207
1208
1209@app.route('/api/computer/<computer_name>')
1210def get_computer_details(computer_name):
1211 """Добиј сите детални информации за еден компјутер"""
1212 conn = sqlite3.connect(DB_FILE)
1213 c = conn.cursor()
1214
1215 # Основни информации
1216 c.execute('''SELECT id, name, user, ip, os, first_seen, last_seen
1217 FROM computers WHERE name = ?''', (computer_name,))
1218 computer_data = c.fetchone()
1219
1220 if not computer_data:
1221 conn.close()
1222 return jsonify({"error": "Computer not found"}), 404
1223
1224 computer_id = computer_data[0]
1225
1226 # Број на сите записи
1227 c.execute('SELECT COUNT(*) FROM computer_history WHERE computer_id = ?', (computer_id,))
1228 total_logs = c.fetchone()[0]
1229
1230 # Сите history записи
1231 c.execute('''SELECT cpu_usage, ram_usage, disk_usage, network_sent_mb, network_recv_mb, timestamp
1232 FROM computer_history
1233 WHERE computer_id = ?
1234 ORDER BY timestamp DESC LIMIT 100''', (computer_id,))
1235 history = []
1236 for row in c.fetchall():
1237 history.append({
1238 'cpu_usage': row[0],
1239 'ram_usage': row[1],
1240 'disk_usage': row[2],
1241 'network_sent_mb': row[3],
1242 'network_recv_mb': row[4],
1243 'timestamp': row[5]
1244 })
1245
1246 # Последни метрики
1247 current_metrics = history[0] if history else {}
1248
1249 # Последни процеси
1250 c.execute('''SELECT pid, name, username, cpu_percent, memory_mb, cmdline, timestamp
1251 FROM computer_processes
1252 WHERE computer_id = ?
1253 ORDER BY timestamp DESC LIMIT 100''', (computer_id,))
1254 recent_processes = []
1255 for row in c.fetchall():
1256 recent_processes.append({
1257 'pid': row[0],
1258 'name': row[1],
1259 'username': row[2],
1260 'cpu_percent': row[3],
1261 'memory_mb': row[4],
1262 'cmdline': row[5],
1263 'timestamp': row[6]
1264 })
1265
1266 # SYSMON НАСТАНИ
1267 c.execute('''SELECT event_id, event_type, message, timestamp, details
1268 FROM sysmon_events
1269 WHERE computer_id = ?
1270 ORDER BY timestamp DESC LIMIT 200''', (computer_id,))
1271 sysmon_events = []
1272 for row in c.fetchall():
1273 try:
1274 details = json.loads(row[4]) if row[4] else {}
1275 except:
1276 details = {}
1277
1278 sysmon_events.append({
1279 'event_id': row[0],
1280 'event_type': row[1],
1281 'message': row[2],
1282 'timestamp': row[3],
1283 'details': details
1284 })
1285
1286 # Мрежни конекции
1287 c.execute('''SELECT pid, process_name, local_address, remote_address, status, timestamp
1288 FROM network_connections
1289 WHERE computer_id = ?
1290 ORDER BY timestamp DESC LIMIT 100''', (computer_id,))
1291 network_connections = []
1292 for row in c.fetchall():
1293 network_connections.append({
1294 'pid': row[0],
1295 'process_name': row[1],
1296 'local_address': row[2],
1297 'remote_address': row[3],
1298 'status': row[4],
1299 'timestamp': row[5]
1300 })
1301
1302 conn.close()
1303
1304 return jsonify({
1305 'computer': {
1306 'id': computer_data[0],
1307 'name': computer_data[1],
1308 'user': computer_data[2],
1309 'ip': computer_data[3],
1310 'os': computer_data[4],
1311 'first_seen': computer_data[5],
1312 'last_seen': computer_data[6],
1313 'total_logs': total_logs
1314 },
1315 'history': history,
1316 'current_metrics': current_metrics,
1317 'recent_processes': recent_processes,
1318 'sysmon_events': sysmon_events,
1319 'network_connections': network_connections
1320 })
1321
1322@app.route('/api/computers')
1323def get_computers():
1324 conn = sqlite3.connect(DB_FILE)
1325 c = conn.cursor()
1326
1327 c.execute('''SELECT name, user, ip, os, first_seen, last_seen, sysmon_available
1328 FROM computers
1329 ORDER BY last_seen DESC''')
1330
1331 computers = []
1332 for row in c.fetchall():
1333 computer_name = row[0]
1334
1335 # recent logs
1336 c.execute('''SELECT COUNT(*) FROM computer_history
1337 WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
1338 AND timestamp > datetime('now', '-24 hours')''',
1339 (computer_name,))
1340 recent_logs = c.fetchone()[0]
1341
1342 # last 5 metrics
1343 c.execute('''SELECT cpu_usage, ram_usage, timestamp
1344 FROM computer_history
1345 WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
1346 ORDER BY timestamp DESC LIMIT 5''',
1347 (computer_name,))
1348 recent_metrics = c.fetchall()
1349
1350 c.execute('''SELECT COUNT(*) FROM sysmon_events
1351 WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
1352 AND timestamp > datetime('now', '-24 hours')''',
1353 (computer_name,))
1354 recent_sysmon = c.fetchone()[0]
1355
1356 avg_cpu = sum(m[0] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
1357 avg_ram = sum(m[1] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
1358
1359 last_seen = datetime.fromisoformat(row[5])
1360 time_diff = (datetime.now() - last_seen).seconds
1361
1362 if time_diff < 60:
1363 status = 'online'
1364 status_color = 'green'
1365 elif time_diff < 300:
1366 status = 'idle'
1367 status_color = 'orange'
1368 else:
1369 status = 'offline'
1370 status_color = 'gray'
1371
1372 computers.append({
1373 'name': row[0],
1374 'user': row[1],
1375 'ip': row[2],
1376 'os': row[3],
1377 'first_seen': row[4],
1378 'last_seen': row[5],
1379 'sysmon_available': bool(row[6]),
1380 'recent_logs': recent_logs,
1381 'recent_sysmon': recent_sysmon,
1382 'avg_cpu': round(avg_cpu, 1),
1383 'avg_ram': round(avg_ram, 1),
1384 'status': status,
1385 'status_color': status_color
1386 })
1387
1388 conn.close()
1389 return jsonify(computers)
1390
1391@app.route('/api/stats')
1392def get_stats():
1393 """Добиј глобални статистики"""
1394 conn = sqlite3.connect(DB_FILE)
1395 c = conn.cursor()
1396
1397 # Вкупно Sysmon настани за последните 24 часа
1398 c.execute('''SELECT COUNT(*) FROM sysmon_events
1399 WHERE timestamp > datetime('now', '-24 hours')''')
1400 total_sysmon_events = c.fetchone()[0]
1401
1402 # Вкупно мрежни конекции за последните 24 часа
1403 c.execute('''SELECT COUNT(*) FROM network_connections
1404 WHERE timestamp > datetime('now', '-24 hours')''')
1405 total_network_connections = c.fetchone()[0]
1406
1407 conn.close()
1408
1409 return jsonify({
1410 'total_sysmon_events': total_sysmon_events,
1411 'total_network_connections': total_network_connections,
1412 'server_time': datetime.now().isoformat()
1413 })
1414
1415def build_rag_context(question, computer_name=None, limit_per_table=10):
1416 try:
1417 conn = sqlite3.connect(DB_FILE)
1418 c = conn.cursor()
1419
1420 params = []
1421 comp_filter = ""
1422 if computer_name:
1423 comp_filter = "AND computer_id = (SELECT id FROM computers WHERE name = ?)"
1424 params.append(computer_name)
1425
1426 # Sysmon настани
1427 c.execute(
1428 f"""
1429 SELECT timestamp, event_id, event_type, message
1430 FROM sysmon_events
1431 WHERE 1=1 {comp_filter}
1432 ORDER BY timestamp DESC
1433 LIMIT ?
1434 """,
1435 params + [limit_per_table],
1436 )
1437 sysmon_rows = c.fetchall()
1438
1439 # Перформанс историја
1440 c.execute(
1441 f"""
1442 SELECT timestamp, cpu_usage, ram_usage, disk_usage,
1443 network_sent_mb, network_recv_mb
1444 FROM computer_history
1445 WHERE 1=1 {comp_filter}
1446 ORDER BY timestamp DESC
1447 LIMIT ?
1448 """,
1449 params + [limit_per_table],
1450 )
1451 perf_rows = c.fetchall()
1452
1453 # Процеси
1454 c.execute(
1455 f"""
1456 SELECT timestamp, pid, name, cpu_percent, memory_mb
1457 FROM computer_processes
1458 WHERE 1=1 {comp_filter}
1459 ORDER BY timestamp DESC
1460 LIMIT ?
1461 """,
1462 params + [limit_per_table],
1463 )
1464 proc_rows = c.fetchall()
1465
1466 conn.close()
1467
1468 print("[RAG] sysmon:", len(sysmon_rows),
1469 "| perf:", len(perf_rows),
1470 "| proc:", len(proc_rows))
1471
1472 lines = []
1473
1474 for ts, eid, etype, msg in sysmon_rows:
1475 lines.append(f"[SYSMON] {ts} | Event {eid} ({etype}): {msg}")
1476
1477 for ts, cpu, ram, disk, nsent, nrecv in perf_rows:
1478 lines.append(
1479 f"[PERF] {ts} | CPU {cpu}% | RAM {ram}% | Disk {disk}% | "
1480 f"Net sent {nsent}MB / recv {nrecv}MB"
1481 )
1482
1483 for ts, pid, name, cpu, mem in proc_rows:
1484 lines.append(f"[PROC] {ts} | PID {pid} | {name} | CPU {cpu}% | RAM {mem}MB")
1485
1486 context = "\n".join(lines)
1487 print("[RAG] sample context:\n", "\n".join(lines[:5]))
1488 return context
1489
1490 except Exception as e:
1491 traceback.print_exc()
1492 return ""
1493
1494# def ask_llm_with_context(question, context, language="mk"):
1495# if not client:
1496# return "Немаш поставено OPENAI_API_KEY на серверот."
1497#
1498# system_msg = (
1499# "Ти си асистент за безбедност и систем мониторинг. "
1500# "Одговараш базирано ИСКЛУЧИВО на дадените логови што ти се дадени. "
1501# "Ако нема доволно информации, кажи дека нема доволно податоци. "
1502# "Објаснувај кратко и јасно."
1503# )
1504#
1505# user_msg = f"Прашање: {question}\n\nЛогови:\n{context or 'Нема логови.'}"
1506#
1507# try:
1508# completion = client.chat.completions.create(
1509# model="gpt-4.1-mini",
1510# messages=[
1511# {"role": "system", "content": system_msg},
1512# {"role": "user", "content": user_msg},
1513# ],
1514# temperature=0.2,
1515# )
1516# return completion.choices[0].message.content
1517# except Exception as e:
1518# # Може попрецизно: openai.APITimeoutError, openai.APIConnectionError итн.
1519# return f"Грешка при повик кон LLM: {e}"
1520
1521def ask_llm_with_context(question, context, language="mk"):
1522 """
1523 Вика OpenAI модел и враќа одговор базиран на контекстот од логови.
1524 """
1525 if not client:
1526 return "Немаш поставено OPENAI_API_KEY на серверот."
1527
1528 system_msg = (
1529 "Ти си асистент за безбедност и систем мониторинг. "
1530 "Одговараш базирано ИСКЛУЧИВО на дадените логови што ти се дадени. "
1531 "Типови редови во логовите:\n"
1532 "- [SYSMON] ... Sysmon безбедносни настани.\n"
1533 "- [PERF] ... Општи перформанси (CPU, RAM, диск, мрежа).\n"
1534 "- [PROC] ... Информации за поединечни процеси: PID, име, CPU%, RAM MB.\n"
1535 "Кога корисникот прашува за процеси и нивна потрошувачка, "
1536 "СЕКОГАШ користи ги [PROC] редовите. "
1537 "Никогаш не кажувај дека 'нема информации за процеси' ако постои барем еден [PROC] ред. "
1538 "Ако навистина НЕМА [PROC] редови, тогаш можеш да кажеш дека нема доволно податоци. "
1539 "Објаснувај кратко и јасно."
1540 )
1541
1542 user_msg = f"Прашање: {question}\n\nЛогови:\n{context or 'Нема логови.'}"
1543
1544 try:
1545 print("[LLM] Calling OpenAI…")
1546 completion = client.chat.completions.create(
1547 model="gpt-4.1-mini",
1548 messages=[
1549 {"role": "system", "content": system_msg},
1550 {"role": "user", "content": user_msg},
1551 ],
1552 temperature=0.2,
1553 )
1554 print("[LLM] Got response")
1555 return completion.choices[0].message.content
1556
1557 except Exception as e:
1558 print("[LLM] ERROR:", e)
1559 return f"Грешка при повик кон LLM: {e}"
1560
1561
1562
1563#
1564# def ask_llm_with_context(question, context, language="mk"):
1565# payload = {
1566# "model": "deepseek-r1:1.5b",
1567# "prompt": f"Прашање: {question}\n\nКонтекст:\n{context}",
1568# "stream": False
1569# }
1570#
1571# try:
1572# r = requests.post("http://localhost:11434/api/generate", json=payload)
1573# r.raise_for_status()
1574# data = r.json()
1575# return data.get("response", "")
1576# except Exception as e:
1577# return f"Грешка при повик кон Ollama: {e}"
1578
1579
1580@app.route('/api/export/<computer_name>')
1581def export_computer_data(computer_name):
1582 """Експортирај ги сите податоци за еден компјутер"""
1583 data = {}
1584
1585 conn = sqlite3.connect(DB_FILE)
1586 c = conn.cursor()
1587
1588 # Основни информации за компјутерот
1589 c.execute('''SELECT * FROM computers WHERE name = ?''', (computer_name,))
1590 columns = [description[0] for description in c.description]
1591 computer_data = c.fetchone()
1592
1593 if computer_data:
1594 data['computer'] = dict(zip(columns, computer_data))
1595
1596 # Историја
1597 computer_id = computer_data[0]
1598 c.execute('''SELECT * FROM computer_history WHERE computer_id = ? ORDER BY timestamp''', (computer_id,))
1599 data['history'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()]
1600
1601 # Sysmon настани
1602 c.execute('''SELECT * FROM sysmon_events WHERE computer_id = ? ORDER BY timestamp''', (computer_id,))
1603 data['sysmon_events'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()]
1604
1605 # Процеси
1606 c.execute('''SELECT * FROM computer_processes WHERE computer_id = ? ORDER BY timestamp''', (computer_id,))
1607 data['processes'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()]
1608
1609 # Мрежни конекции
1610 c.execute('''SELECT * FROM network_connections WHERE computer_id = ? ORDER BY timestamp''', (computer_id,))
1611 data['network_connections'] = [dict(zip([d[0] for d in c.description], row)) for row in c.fetchall()]
1612
1613 conn.close()
1614
1615 # Креирај JSON одговор
1616 from flask import Response
1617 response = Response(
1618 json.dumps(data, indent=2, default=str),
1619 mimetype='application/json',
1620 headers={'Content-Disposition': f'attachment; filename={computer_name}_export.json'}
1621 )
1622
1623 return response
1624
1625
1626@app.route('/ping')
1627def ping():
1628 return jsonify({
1629 'status': 'alive',
1630 'server_time': datetime.now().isoformat(),
1631 'version': '2.0_sysmon',
1632 'database': DB_FILE
1633 })
1634
1635
1636def get_local_ip():
1637 try:
1638 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
1639 s.connect(("8.8.8.8", 80))
1640 local_ip = s.getsockname()[0]
1641 s.close()
1642 return local_ip
1643 except:
1644 return "127.0.0.1"
1645
1646
1647def cleanup_old_data():
1648 """Чисти стари податоци од базата (стари од 30 дена)"""
1649 while True:
1650 time.sleep(3600) # Чисти на секој час
1651 try:
1652 conn = sqlite3.connect(DB_FILE)
1653 c = conn.cursor()
1654
1655 cutoff_date = (datetime.now() - timedelta(days=30)).isoformat()
1656
1657 # Избриши стари записи
1658 c.execute("DELETE FROM computer_history WHERE timestamp < ?", (cutoff_date,))
1659 c.execute("DELETE FROM computer_processes WHERE timestamp < ?", (cutoff_date,))
1660 c.execute("DELETE FROM sysmon_events WHERE timestamp < ?", (cutoff_date,))
1661 c.execute("DELETE FROM network_connections WHERE timestamp < ?", (cutoff_date,))
1662
1663 conn.commit()
1664 conn.close()
1665
1666 print(f"[Cleanup] Избришани стари записи пред {cutoff_date}")
1667
1668 except Exception as e:
1669 print(f"[Cleanup] Грешка: {e}")
1670
1671import time
1672@app.route("/api/chat", methods=["POST"])
1673def api_chat():
1674 try:
1675 t0 = time.time()
1676 data = request.get_json(force=True) or {}
1677 question = data.get("question", "").strip()
1678 computer_name = data.get("computer_name") or None
1679
1680 if not question:
1681 return jsonify({"error": "No question provided"}), 400
1682
1683 context = build_rag_context(question, computer_name=computer_name)
1684 t1 = time.time()
1685
1686 answer = ask_llm_with_context(question, context)
1687 t2 = time.time()
1688
1689 print(f"[CHAT] build_rag_context: {t1 - t0:.2f}s, LLM: {t2 - t1:.2f}s")
1690
1691 return jsonify({"answer": answer})
1692 except Exception as e:
1693 traceback.print_exc()
1694 return jsonify({"error": str(e)}), 500
1695
1696
1697@app.route('/api/computer/<computer_name>/sysmon')
1698def get_sysmon_processes(computer_name):
1699 """Добиј Sysmon процеси за компјутер"""
1700 conn = sqlite3.connect(DB_FILE)
1701 c = conn.cursor()
1702
1703 # Прво најди го computer_id
1704 c.execute('SELECT id FROM computers WHERE name = ?', (computer_name,))
1705 result = c.fetchone()
1706
1707 if not result:
1708 conn.close()
1709 return jsonify({"error": "Computer not found"}), 404
1710
1711 computer_id = result[0]
1712
1713 # Земaj ги Sysmon процесите (последни 100)
1714 c.execute('''SELECT
1715 event_id,
1716 event_type,
1717 message,
1718 timestamp,
1719 details
1720 FROM sysmon_events
1721 WHERE computer_id = ?
1722 ORDER BY timestamp DESC LIMIT 100''', (computer_id,))
1723
1724 processes = []
1725 for row in c.fetchall():
1726 try:
1727 details = json.loads(row[4]) if row[4] else {}
1728 except:
1729 details = {}
1730
1731 processes.append({
1732 'process_id': row[0], # Event ID како PID
1733 'process_name': row[1], # Event type како име
1734 'timestamp': row[3],
1735 'message': row[2],
1736 'details': details
1737 })
1738
1739 conn.close()
1740
1741 return jsonify({
1742 'computer_name': computer_name,
1743 'processes': processes,
1744 'count': len(processes)
1745 })
1746
1747
1748@app.route('/api/computer/<computer_name>/network')
1749def get_network_connections(computer_name):
1750 """Добиј мрежни конекции за компјутер"""
1751 conn = sqlite3.connect(DB_FILE)
1752 c = conn.cursor()
1753
1754 # Прво најди го computer_id
1755 c.execute('SELECT id FROM computers WHERE name = ?', (computer_name,))
1756 result = c.fetchone()
1757
1758 if not result:
1759 conn.close()
1760 return jsonify({"error": "Computer not found"}), 404
1761
1762 computer_id = result[0]
1763
1764 # Земaj ги мрежните конекции (последни 100)
1765 c.execute('''SELECT
1766 pid,
1767 process_name,
1768 local_address,
1769 remote_address,
1770 status,
1771 timestamp
1772 FROM network_connections
1773 WHERE computer_id = ?
1774 ORDER BY timestamp DESC LIMIT 100''', (computer_id,))
1775
1776 connections = []
1777 for row in c.fetchall():
1778 # Парсирај IP и порта
1779 local = row[2] or "N/A"
1780 remote = row[3] or "N/A"
1781
1782 connections.append({
1783 'pid': row[0],
1784 'process_name': row[1] or 'Unknown',
1785 'local_address': local,
1786 'remote_address': remote,
1787 'local_port': local.split(':')[-1] if ':' in local else '',
1788 'remote_port': remote.split(':')[-1] if ':' in remote else '',
1789 'protocol': 'TCP' if ':443' in local or ':80' in local else 'Unknown',
1790 'state': row[4] or 'ESTABLISHED',
1791 'timestamp': row[5]
1792 })
1793
1794 conn.close()
1795
1796 return jsonify({
1797 'computer_name': computer_name,
1798 'connections': connections,
1799 'count': len(connections)
1800 })
1801
1802
1803@app.route('/api/computer/<computer_name>/detailed-sysmon')
1804def get_detailed_sysmon(computer_name):
1805 """Добиј детални Sysmon процеси со повеќе информации"""
1806 conn = sqlite3.connect(DB_FILE)
1807 c = conn.cursor()
1808
1809 c.execute('SELECT id FROM computers WHERE name = ?', (computer_name,))
1810 result = c.fetchone()
1811
1812 if not result:
1813 conn.close()
1814 return jsonify({"error": "Computer not found"}), 404
1815
1816 computer_id = result[0]
1817
1818 c.execute('''SELECT event_id, event_type, message, timestamp, details
1819 FROM sysmon_events
1820 WHERE computer_id = ?
1821 ORDER BY timestamp DESC LIMIT 50''', (computer_id,))
1822
1823 detailed_processes = []
1824
1825 for row in c.fetchall():
1826 event_id = row[0]
1827 event_type = row[1]
1828 message = row[2]
1829 timestamp = row[3]
1830 details_str = row[4]
1831
1832 # Пробај да го парсираш details JSON
1833 try:
1834 details = json.loads(details_str) if details_str else {}
1835 except:
1836 details = {}
1837
1838 # Екстрактирај информации од details
1839 process_info = {
1840 'event_id': event_id,
1841 'event_type': event_type,
1842 'timestamp': timestamp,
1843 'message': message,
1844 'process_name': details.get('ProcessName', 'Unknown'),
1845 'process_id': details.get('ProcessId', event_id),
1846 'command_line': details.get('CommandLine', ''),
1847 'parent_process': details.get('ParentProcessName', ''),
1848 'parent_pid': details.get('ParentProcessId', ''),
1849 'user': details.get('User', ''),
1850 'details': details
1851 }
1852
1853 # Додади дополнителни полиња за различни типови на настани
1854 if event_id in [3, 22]: # Network connection or DNS
1855 process_info['source_ip'] = details.get('SourceIp', '')
1856 process_info['dest_ip'] = details.get('DestinationIp', '')
1857 process_info['dest_port'] = details.get('DestinationPort', '')
1858
1859 detailed_processes.append(process_info)
1860
1861 conn.close()
1862
1863 return jsonify({
1864 'computer_name': computer_name,
1865 'processes': detailed_processes,
1866 'count': len(detailed_processes)
1867 })
1868if __name__ == '__main__':
1869 init_database()
1870
1871 # Стартувај cleanup thread
1872 cleanup_thread = threading.Thread(target=cleanup_old_data, daemon=True)
1873 cleanup_thread.start()
1874
1875 local_ip = get_local_ip()
1876
1877 print("\n" + "=" * 70)
1878 print("🚀 ENHANCED LAN LOG SERVER со Sysmon")
1879 print("=" * 70)
1880 print(f"🌐 Server IP: {local_ip}:5555")
1881 print(f"📊 Dashboard: http://{local_ip}:5555/")
1882 print(f"📊 Alternative: http://localhost:5555/")
1883 print(f"🏥 Health check: http://{local_ip}:5555/ping")
1884 print("\n🛡️ Sysmon monitoring е активирано")
1885 print("✅ Кликни на секој компјутер за детални информации")
1886 print("✅ Сите Sysmon настани се прикажани во табла")
1887 print("✅ Кликни на секој настан за детали")
1888 print("=" * 70 + "\n")
1889
1890 app.run(host='0.0.0.0', port=5555, debug=False, threaded=True)
Note: See TracBrowser for help on using the repository browser.