| 1 | #!/usr/bin/env python3
|
|---|
| 2 | """
|
|---|
| 3 | POPRAVEN SERVER - So azhuriranje na postoechki zapisi i detalni informacii
|
|---|
| 4 | """
|
|---|
| 5 |
|
|---|
| 6 | from flask import Flask, request, jsonify
|
|---|
| 7 | import json
|
|---|
| 8 | import sqlite3
|
|---|
| 9 | from datetime import datetime, timedelta
|
|---|
| 10 | import os
|
|---|
| 11 | import socket
|
|---|
| 12 |
|
|---|
| 13 | app = Flask(__name__)
|
|---|
| 14 |
|
|---|
| 15 | DB_FILE = "lan_logs_fixed.db"
|
|---|
| 16 | LOG_DIR = "received_data_fixed"
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 | def init_database():
|
|---|
| 20 | """Kreiraј pobolshana baza"""
|
|---|
| 21 | os.makedirs(LOG_DIR, exist_ok=True)
|
|---|
| 22 |
|
|---|
| 23 | conn = sqlite3.connect(DB_FILE)
|
|---|
| 24 | c = conn.cursor()
|
|---|
| 25 |
|
|---|
| 26 | # Tabela za kompjuteri (SEGA so edinstveno ime)
|
|---|
| 27 | c.execute('''CREATE TABLE IF NOT EXISTS computers
|
|---|
| 28 | (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|---|
| 29 | name TEXT UNIQUE,
|
|---|
| 30 | user TEXT,
|
|---|
| 31 | ip TEXT,
|
|---|
| 32 | os TEXT,
|
|---|
| 33 | first_seen TIMESTAMP,
|
|---|
| 34 | last_seen TIMESTAMP)''')
|
|---|
| 35 |
|
|---|
| 36 | # Tabela za history (site podatoci)
|
|---|
| 37 | c.execute('''CREATE TABLE IF NOT EXISTS computer_history
|
|---|
| 38 | (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|---|
| 39 | computer_id INTEGER,
|
|---|
| 40 | cpu_usage REAL,
|
|---|
| 41 | ram_usage REAL,
|
|---|
| 42 | disk_usage REAL,
|
|---|
| 43 | process_count INTEGER,
|
|---|
| 44 | timestamp TIMESTAMP,
|
|---|
| 45 | FOREIGN KEY(computer_id) REFERENCES computers(id))''')
|
|---|
| 46 |
|
|---|
| 47 | # Tabela za procesi (so vremenska oznaka)
|
|---|
| 48 | c.execute('''CREATE TABLE IF NOT EXISTS computer_processes
|
|---|
| 49 | (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|---|
| 50 | computer_id INTEGER,
|
|---|
| 51 | pid INTEGER,
|
|---|
| 52 | name TEXT,
|
|---|
| 53 | cpu_percent REAL,
|
|---|
| 54 | memory_mb REAL,
|
|---|
| 55 | username TEXT,
|
|---|
| 56 | timestamp TIMESTAMP,
|
|---|
| 57 | FOREIGN KEY(computer_id) REFERENCES computers(id))''')
|
|---|
| 58 |
|
|---|
| 59 | conn.commit()
|
|---|
| 60 | conn.close()
|
|---|
| 61 |
|
|---|
| 62 | print(f"✅ Database created: {DB_FILE}")
|
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 | @app.route('/receive', methods=['POST'])
|
|---|
| 66 | def receive_data():
|
|---|
| 67 | try:
|
|---|
| 68 | data = request.json
|
|---|
| 69 |
|
|---|
| 70 | if not data:
|
|---|
| 71 | return jsonify({"error": "No data"}), 400
|
|---|
| 72 |
|
|---|
| 73 | info = data['info']
|
|---|
| 74 | computer_name = info['computer_name']
|
|---|
| 75 | timestamp = datetime.now()
|
|---|
| 76 |
|
|---|
| 77 | conn = sqlite3.connect(DB_FILE)
|
|---|
| 78 | c = conn.cursor()
|
|---|
| 79 |
|
|---|
| 80 | # Proveri dali kompjuterot veќe postoi
|
|---|
| 81 | c.execute("SELECT id FROM computers WHERE name = ?", (computer_name,))
|
|---|
| 82 | result = c.fetchone()
|
|---|
| 83 |
|
|---|
| 84 | if result:
|
|---|
| 85 | # Kompjuterot postoi - azhuriraј go
|
|---|
| 86 | computer_id = result[0]
|
|---|
| 87 | c.execute('''UPDATE computers
|
|---|
| 88 | SET user = ?, ip = ?, os = ?, last_seen = ?
|
|---|
| 89 | WHERE id = ?''',
|
|---|
| 90 | (info['user'], info['ip_address'], info['os'],
|
|---|
| 91 | timestamp.isoformat(), computer_id))
|
|---|
| 92 | else:
|
|---|
| 93 | # Nov kompjuter - dodadi go
|
|---|
| 94 | c.execute('''INSERT INTO computers
|
|---|
| 95 | (name, user, ip, os, first_seen, last_seen)
|
|---|
| 96 | VALUES (?, ?, ?, ?, ?, ?)''',
|
|---|
| 97 | (computer_name, info['user'], info['ip_address'],
|
|---|
| 98 | info['os'], timestamp.isoformat(), timestamp.isoformat()))
|
|---|
| 99 | computer_id = c.lastrowid
|
|---|
| 100 |
|
|---|
| 101 | # Dodadi vo history
|
|---|
| 102 | c.execute('''INSERT INTO computer_history
|
|---|
| 103 | (computer_id, cpu_usage, ram_usage, disk_usage,
|
|---|
| 104 | process_count, timestamp)
|
|---|
| 105 | VALUES (?, ?, ?, ?, ?, ?)''',
|
|---|
| 106 | (computer_id, info['cpu_usage'], info['ram_usage'],
|
|---|
| 107 | info['disk_usage'], len(data.get('processes', [])),
|
|---|
| 108 | info['timestamp']))
|
|---|
| 109 |
|
|---|
| 110 | # Zachuvaj gi procesite
|
|---|
| 111 | for proc in data.get('processes', []):
|
|---|
| 112 | c.execute('''INSERT INTO computer_processes
|
|---|
| 113 | (computer_id, pid, name, username, timestamp)
|
|---|
| 114 | VALUES (?, ?, ?, ?, ?)''',
|
|---|
| 115 | (computer_id, proc.get('pid'), proc.get('name'),
|
|---|
| 116 | proc.get('user'), info['timestamp']))
|
|---|
| 117 |
|
|---|
| 118 | conn.commit()
|
|---|
| 119 | conn.close()
|
|---|
| 120 |
|
|---|
| 121 | # Zachuvaj vo JSON
|
|---|
| 122 | save_json_file(data)
|
|---|
| 123 |
|
|---|
| 124 | print(f"[{timestamp.strftime('%H:%M:%S')}] 📥 {computer_name}: "
|
|---|
| 125 | f"CPU {info['cpu_usage']}% | RAM {info['ram_usage']}%")
|
|---|
| 126 |
|
|---|
| 127 | return jsonify({
|
|---|
| 128 | "status": "success",
|
|---|
| 129 | "message": f"Data saved for {computer_name}",
|
|---|
| 130 | "computer_id": computer_id,
|
|---|
| 131 | "timestamp": timestamp.isoformat()
|
|---|
| 132 | })
|
|---|
| 133 |
|
|---|
| 134 | except Exception as e:
|
|---|
| 135 | print(f"[-] Greshka: {e}")
|
|---|
| 136 | return jsonify({"error": str(e)}), 500
|
|---|
| 137 |
|
|---|
| 138 |
|
|---|
| 139 | def save_json_file(data):
|
|---|
| 140 | """Zachuvaj vo JSON"""
|
|---|
| 141 | info = data['info']
|
|---|
| 142 | computer_name = info['computer_name']
|
|---|
| 143 | date_str = datetime.now().strftime("%Y%m%d")
|
|---|
| 144 |
|
|---|
| 145 | # Kreiraј folder za kompjuterot
|
|---|
| 146 | computer_dir = os.path.join(LOG_DIR, computer_name)
|
|---|
| 147 | os.makedirs(computer_dir, exist_ok=True)
|
|---|
| 148 |
|
|---|
| 149 | # Fajl za denes
|
|---|
| 150 | filepath = os.path.join(computer_dir, f"{date_str}.json")
|
|---|
| 151 |
|
|---|
| 152 | # Dodadi vo postoechki fajl ili kreiraј nov
|
|---|
| 153 | if os.path.exists(filepath):
|
|---|
| 154 | with open(filepath, 'r', encoding='utf-8') as f:
|
|---|
| 155 | existing_data = json.load(f)
|
|---|
| 156 | else:
|
|---|
| 157 | existing_data = []
|
|---|
| 158 |
|
|---|
| 159 | # Dodadi nov zapis
|
|---|
| 160 | existing_data.append({
|
|---|
| 161 | "timestamp": info['timestamp'],
|
|---|
| 162 | "data": info,
|
|---|
| 163 | "processes_count": len(data.get('processes', []))
|
|---|
| 164 | })
|
|---|
| 165 |
|
|---|
| 166 | # Zachuvaj
|
|---|
| 167 | with open(filepath, 'w', encoding='utf-8') as f:
|
|---|
| 168 | json.dump(existing_data, f, indent=2, ensure_ascii=False)
|
|---|
| 169 |
|
|---|
| 170 |
|
|---|
| 171 | @app.route('/')
|
|---|
| 172 | def dashboard():
|
|---|
| 173 | """Glaven dashboard so site kompjuteri"""
|
|---|
| 174 | conn = sqlite3.connect(DB_FILE)
|
|---|
| 175 | c = conn.cursor()
|
|---|
| 176 |
|
|---|
| 177 | # Zemaj gi site kompjuteri
|
|---|
| 178 | c.execute('''SELECT name, user, ip, os, first_seen, last_seen
|
|---|
| 179 | FROM computers
|
|---|
| 180 | ORDER BY last_seen DESC''')
|
|---|
| 181 |
|
|---|
| 182 | computers = []
|
|---|
| 183 | for row in c.fetchall():
|
|---|
| 184 | # Presmetaj kolku zapisi ima vo poslednite 24 chasa
|
|---|
| 185 | c.execute('''SELECT COUNT(*) FROM computer_history
|
|---|
| 186 | WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
|
|---|
| 187 | AND timestamp > datetime('now', '-24 hours')''',
|
|---|
| 188 | (row[0],))
|
|---|
| 189 | recent_logs = c.fetchone()[0]
|
|---|
| 190 |
|
|---|
| 191 | # Zemaj gi poslednite 5 zapisi za CPU/RAM
|
|---|
| 192 | c.execute('''SELECT cpu_usage, ram_usage, timestamp
|
|---|
| 193 | FROM computer_history
|
|---|
| 194 | WHERE computer_id = (SELECT id FROM computers WHERE name = ?)
|
|---|
| 195 | ORDER BY timestamp DESC LIMIT 5''',
|
|---|
| 196 | (row[0],))
|
|---|
| 197 | recent_metrics = c.fetchall()
|
|---|
| 198 |
|
|---|
| 199 | # Presmetaj prosek
|
|---|
| 200 | avg_cpu = sum(m[0] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
|
|---|
| 201 | avg_ram = sum(m[1] for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
|
|---|
| 202 |
|
|---|
| 203 | computers.append({
|
|---|
| 204 | 'name': row[0],
|
|---|
| 205 | 'user': row[1],
|
|---|
| 206 | 'ip': row[2],
|
|---|
| 207 | 'os': row[3],
|
|---|
| 208 | 'first_seen': row[4],
|
|---|
| 209 | 'last_seen': row[5],
|
|---|
| 210 | 'recent_logs': recent_logs,
|
|---|
| 211 | 'avg_cpu': round(avg_cpu, 1),
|
|---|
| 212 | 'avg_ram': round(avg_ram, 1),
|
|---|
| 213 | 'status': 'online' if (datetime.now() - datetime.fromisoformat(row[5])).seconds < 60 else 'offline'
|
|---|
| 214 | })
|
|---|
| 215 |
|
|---|
| 216 | conn.close()
|
|---|
| 217 |
|
|---|
| 218 | # Generiraј HTML
|
|---|
| 219 | html = '''
|
|---|
| 220 | <!DOCTYPE html>
|
|---|
| 221 | <html>
|
|---|
| 222 | <head>
|
|---|
| 223 | <title>LAN Log Server - Fixed Dashboard</title>
|
|---|
| 224 | <meta charset="utf-8">
|
|---|
| 225 | <style>
|
|---|
| 226 | body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
|
|---|
| 227 | .header { background: #2c3e50; color: white; padding: 20px; border-radius: 10px; margin-bottom: 20px; }
|
|---|
| 228 | .computers-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
|
|---|
| 229 | .computer-card { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
|
|---|
| 230 | .computer-card:hover { box-shadow: 0 4px 10px rgba(0,0,0,0.2); cursor: pointer; }
|
|---|
| 231 | .computer-name { font-size: 18px; font-weight: bold; margin-bottom: 10px; color: #2c3e50; }
|
|---|
| 232 | .computer-info { color: #666; margin: 5px 0; }
|
|---|
| 233 | .online { color: green; }
|
|---|
| 234 | .offline { color: gray; }
|
|---|
| 235 | .stats { display: flex; justify-content: space-between; margin-top: 15px; padding-top: 15px; border-top: 1px solid #eee; }
|
|---|
| 236 | .stat { text-align: center; }
|
|---|
| 237 | .stat-value { font-size: 20px; font-weight: bold; }
|
|---|
| 238 | .stat-label { font-size: 12px; color: #888; }
|
|---|
| 239 | .details-panel { display: none; position: fixed; top: 0; right: 0; width: 600px; height: 100%; background: white; box-shadow: -2px 0 10px rgba(0,0,0,0.1); padding: 20px; overflow-y: auto; z-index: 1000; }
|
|---|
| 240 | .close-btn { float: right; cursor: pointer; font-size: 24px; }
|
|---|
| 241 | .tab { display: none; }
|
|---|
| 242 | .tab.active { display: block; }
|
|---|
| 243 | .tab-buttons { display: flex; border-bottom: 1px solid #ddd; margin-bottom: 20px; }
|
|---|
| 244 | .tab-button { padding: 10px 20px; cursor: pointer; border: none; background: none; }
|
|---|
| 245 | .tab-button.active { border-bottom: 3px solid #2c3e50; font-weight: bold; }
|
|---|
| 246 | table { width: 100%; border-collapse: collapse; margin-top: 10px; }
|
|---|
| 247 | th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }
|
|---|
| 248 | th { background: #f5f5f5; }
|
|---|
| 249 | </style>
|
|---|
| 250 | <script>
|
|---|
| 251 | function showComputerDetails(computerName) {
|
|---|
| 252 | fetch(`/api/computer/${encodeURIComponent(computerName)}`)
|
|---|
| 253 | .then(response => response.json())
|
|---|
| 254 | .then(data => {
|
|---|
| 255 | document.getElementById('details-panel').style.display = 'block';
|
|---|
| 256 | populateDetails(data);
|
|---|
| 257 | });
|
|---|
| 258 | }
|
|---|
| 259 |
|
|---|
| 260 | function populateDetails(data) {
|
|---|
| 261 | // Popolni osnovni informacii
|
|---|
| 262 | document.getElementById('details-title').textContent = data.computer.name;
|
|---|
| 263 | document.getElementById('details-user').textContent = data.computer.user;
|
|---|
| 264 | document.getElementById('details-ip').textContent = data.computer.ip;
|
|---|
| 265 | document.getElementById('details-os').textContent = data.computer.os;
|
|---|
| 266 | document.getElementById('details-first-seen').textContent = new Date(data.computer.first_seen).toLocaleString();
|
|---|
| 267 | document.getElementById('details-last-seen').textContent = new Date(data.computer.last_seen).toLocaleString();
|
|---|
| 268 | document.getElementById('details-total-logs').textContent = data.computer.total_logs;
|
|---|
| 269 |
|
|---|
| 270 | // Popolni tabela za history
|
|---|
| 271 | let historyTable = document.getElementById('history-table');
|
|---|
| 272 | historyTable.innerHTML = '';
|
|---|
| 273 | data.history.forEach(log => {
|
|---|
| 274 | let row = historyTable.insertRow();
|
|---|
| 275 | row.innerHTML = `
|
|---|
| 276 | <td>${new Date(log.timestamp).toLocaleString()}</td>
|
|---|
| 277 | <td>${log.cpu_usage}%</td>
|
|---|
| 278 | <td>${log.ram_usage}%</td>
|
|---|
| 279 | <td>${log.disk_usage}%</td>
|
|---|
| 280 | <td>${log.process_count}</td>
|
|---|
| 281 | `;
|
|---|
| 282 | });
|
|---|
| 283 |
|
|---|
| 284 | // Popolni tabela za procesi (posledni)
|
|---|
| 285 | let processTable = document.getElementById('process-table');
|
|---|
| 286 | processTable.innerHTML = '';
|
|---|
| 287 | data.recent_processes.forEach(proc => {
|
|---|
| 288 | let row = processTable.insertRow();
|
|---|
| 289 | row.innerHTML = `
|
|---|
| 290 | <td>${proc.pid}</td>
|
|---|
| 291 | <td>${proc.name}</td>
|
|---|
| 292 | <td>${proc.username}</td>
|
|---|
| 293 | <td>${new Date(proc.timestamp).toLocaleTimeString()}</td>
|
|---|
| 294 | `;
|
|---|
| 295 | });
|
|---|
| 296 |
|
|---|
| 297 | // Popolni chart (simple)
|
|---|
| 298 | updateChart(data.history);
|
|---|
| 299 | }
|
|---|
| 300 |
|
|---|
| 301 | function updateChart(history) {
|
|---|
| 302 | const ctx = document.getElementById('performance-chart').getContext('2d');
|
|---|
| 303 |
|
|---|
| 304 | // Grupiraј po chas (za podobar prikaz)
|
|---|
| 305 | const labels = history.slice(-20).map(h =>
|
|---|
| 306 | new Date(h.timestamp).toLocaleTimeString()
|
|---|
| 307 | );
|
|---|
| 308 | const cpuData = history.slice(-20).map(h => h.cpu_usage);
|
|---|
| 309 | const ramData = history.slice(-20).map(h => h.ram_usage);
|
|---|
| 310 |
|
|---|
| 311 | if(window.myChart) {
|
|---|
| 312 | window.myChart.destroy();
|
|---|
| 313 | }
|
|---|
| 314 |
|
|---|
| 315 | window.myChart = new Chart(ctx, {
|
|---|
| 316 | type: 'line',
|
|---|
| 317 | data: {
|
|---|
| 318 | labels: labels,
|
|---|
| 319 | datasets: [
|
|---|
| 320 | {
|
|---|
| 321 | label: 'CPU Usage %',
|
|---|
| 322 | data: cpuData,
|
|---|
| 323 | borderColor: 'rgb(255, 99, 132)',
|
|---|
| 324 | backgroundColor: 'rgba(255, 99, 132, 0.2)',
|
|---|
| 325 | tension: 0.4
|
|---|
| 326 | },
|
|---|
| 327 | {
|
|---|
| 328 | label: 'RAM Usage %',
|
|---|
| 329 | data: ramData,
|
|---|
| 330 | borderColor: 'rgb(54, 162, 235)',
|
|---|
| 331 | backgroundColor: 'rgba(54, 162, 235, 0.2)',
|
|---|
| 332 | tension: 0.4
|
|---|
| 333 | }
|
|---|
| 334 | ]
|
|---|
| 335 | },
|
|---|
| 336 | options: {
|
|---|
| 337 | responsive: true,
|
|---|
| 338 | scales: {
|
|---|
| 339 | y: {
|
|---|
| 340 | beginAtZero: true,
|
|---|
| 341 | max: 100
|
|---|
| 342 | }
|
|---|
| 343 | }
|
|---|
| 344 | }
|
|---|
| 345 | });
|
|---|
| 346 | }
|
|---|
| 347 |
|
|---|
| 348 | function closeDetails() {
|
|---|
| 349 | document.getElementById('details-panel').style.display = 'none';
|
|---|
| 350 | }
|
|---|
| 351 |
|
|---|
| 352 | function switchTab(tabName) {
|
|---|
| 353 | // Deaktiviraј site tabovi
|
|---|
| 354 | document.querySelectorAll('.tab').forEach(tab => {
|
|---|
| 355 | tab.classList.remove('active');
|
|---|
| 356 | });
|
|---|
| 357 | document.querySelectorAll('.tab-button').forEach(btn => {
|
|---|
| 358 | btn.classList.remove('active');
|
|---|
| 359 | });
|
|---|
| 360 |
|
|---|
| 361 | // Aktiviraј izbraniot tab
|
|---|
| 362 | document.getElementById(tabName + '-tab').classList.add('active');
|
|---|
| 363 | document.querySelector(`[onclick="switchTab('${tabName}')"]`).classList.add('active');
|
|---|
| 364 | }
|
|---|
| 365 |
|
|---|
| 366 | // Aktiviraј prv tab na start
|
|---|
| 367 | document.addEventListener('DOMContentLoaded', function() {
|
|---|
| 368 | switchTab('history');
|
|---|
| 369 | });
|
|---|
| 370 | </script>
|
|---|
| 371 | </head>
|
|---|
| 372 | <body>
|
|---|
| 373 | <div class="header">
|
|---|
| 374 | <h1>🖥️ LAN Log Server - Fixed Dashboard</h1>
|
|---|
| 375 | <p>Connected Computers: <span id="computer-count">''' + str(len(computers)) + '''</span></p>
|
|---|
| 376 | <p>Server IP: ''' + socket.gethostbyname(socket.gethostname()) + ''':5555</p>
|
|---|
| 377 | </div>
|
|---|
| 378 |
|
|---|
| 379 | <h2>Connected Computers</h2>
|
|---|
| 380 | <div class="computers-grid">
|
|---|
| 381 | '''
|
|---|
| 382 |
|
|---|
| 383 | for comp in computers:
|
|---|
| 384 | html += f'''
|
|---|
| 385 | <div class="computer-card" onclick="showComputerDetails('{comp['name']}')">
|
|---|
| 386 | <div class="computer-name">{comp['name']}</div>
|
|---|
| 387 | <div class="computer-info">👤 User: {comp['user']}</div>
|
|---|
| 388 | <div class="computer-info">🌐 IP: {comp['ip']}</div>
|
|---|
| 389 | <div class="computer-info">💻 OS: {comp['os']}</div>
|
|---|
| 390 | <div class="computer-info">⏰ Last seen: {datetime.fromisoformat(comp['last_seen']).strftime('%H:%M:%S')}</div>
|
|---|
| 391 | <div class="computer-info {'online' if comp['status'] == 'online' else 'offline'}">
|
|---|
| 392 | ● {comp['status'].upper()}
|
|---|
| 393 | </div>
|
|---|
| 394 | <div class="stats">
|
|---|
| 395 | <div class="stat">
|
|---|
| 396 | <div class="stat-value">{comp['avg_cpu']}%</div>
|
|---|
| 397 | <div class="stat-label">CPU</div>
|
|---|
| 398 | </div>
|
|---|
| 399 | <div class="stat">
|
|---|
| 400 | <div class="stat-value">{comp['avg_ram']}%</div>
|
|---|
| 401 | <div class="stat-label">RAM</div>
|
|---|
| 402 | </div>
|
|---|
| 403 | <div class="stat">
|
|---|
| 404 | <div class="stat-value">{comp['recent_logs']}</div>
|
|---|
| 405 | <div class="stat-label">Logs (24h)</div>
|
|---|
| 406 | </div>
|
|---|
| 407 | </div>
|
|---|
| 408 | </div>
|
|---|
| 409 | '''
|
|---|
| 410 |
|
|---|
| 411 | html += '''
|
|---|
| 412 | </div>
|
|---|
| 413 |
|
|---|
| 414 | <!-- Details Panel -->
|
|---|
| 415 | <div id="details-panel" class="details-panel">
|
|---|
| 416 | <div class="close-btn" onclick="closeDetails()">×</div>
|
|---|
| 417 | <h2 id="details-title">Computer Details</h2>
|
|---|
| 418 |
|
|---|
| 419 | <div class="computer-info">
|
|---|
| 420 | <p><strong>👤 User:</strong> <span id="details-user"></span></p>
|
|---|
| 421 | <p><strong>🌐 IP:</strong> <span id="details-ip"></span></p>
|
|---|
| 422 | <p><strong>💻 OS:</strong> <span id="details-os"></span></p>
|
|---|
| 423 | <p><strong>📅 First Seen:</strong> <span id="details-first-seen"></span></p>
|
|---|
| 424 | <p><strong>⏰ Last Seen:</strong> <span id="details-last-seen"></span></p>
|
|---|
| 425 | <p><strong>📊 Total Logs:</strong> <span id="details-total-logs"></span></p>
|
|---|
| 426 | </div>
|
|---|
| 427 |
|
|---|
| 428 | <div class="tab-buttons">
|
|---|
| 429 | <button class="tab-button active" onclick="switchTab('history')">History</button>
|
|---|
| 430 | <button class="tab-button" onclick="switchTab('processes')">Processes</button>
|
|---|
| 431 | <button class="tab-button" onclick="switchTab('charts')">Charts</button>
|
|---|
| 432 | </div>
|
|---|
| 433 |
|
|---|
| 434 | <div id="history-tab" class="tab active">
|
|---|
| 435 | <h3>Performance History</h3>
|
|---|
| 436 | <table>
|
|---|
| 437 | <thead>
|
|---|
| 438 | <tr>
|
|---|
| 439 | <th>Time</th>
|
|---|
| 440 | <th>CPU %</th>
|
|---|
| 441 | <th>RAM %</th>
|
|---|
| 442 | <th>Disk %</th>
|
|---|
| 443 | <th>Processes</th>
|
|---|
| 444 | </tr>
|
|---|
| 445 | </thead>
|
|---|
| 446 | <tbody id="history-table"></tbody>
|
|---|
| 447 | </table>
|
|---|
| 448 | </div>
|
|---|
| 449 |
|
|---|
| 450 | <div id="processes-tab" class="tab">
|
|---|
| 451 | <h3>Recent Processes</h3>
|
|---|
| 452 | <table>
|
|---|
| 453 | <thead>
|
|---|
| 454 | <tr>
|
|---|
| 455 | <th>PID</th>
|
|---|
| 456 | <th>Name</th>
|
|---|
| 457 | <th>User</th>
|
|---|
| 458 | <th>Time</th>
|
|---|
| 459 | </tr>
|
|---|
| 460 | </thead>
|
|---|
| 461 | <tbody id="process-table"></tbody>
|
|---|
| 462 | </table>
|
|---|
| 463 | </div>
|
|---|
| 464 |
|
|---|
| 465 | <div id="charts-tab" class="tab">
|
|---|
| 466 | <h3>Performance Chart</h3>
|
|---|
| 467 | <canvas id="performance-chart" width="400" height="200"></canvas>
|
|---|
| 468 | </div>
|
|---|
| 469 | </div>
|
|---|
| 470 |
|
|---|
| 471 | <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|---|
| 472 | <script>
|
|---|
| 473 | // Auto-refresh na dashboard sekoj 30 sekundi
|
|---|
| 474 | setInterval(() => {
|
|---|
| 475 | location.reload();
|
|---|
| 476 | }, 30000);
|
|---|
| 477 | </script>
|
|---|
| 478 | </body>
|
|---|
| 479 | </html>
|
|---|
| 480 | '''
|
|---|
| 481 |
|
|---|
| 482 | return html
|
|---|
| 483 |
|
|---|
| 484 |
|
|---|
| 485 | @app.route('/api/computer/<computer_name>')
|
|---|
| 486 | def get_computer_details(computer_name):
|
|---|
| 487 | """Dobi site detalni informacii za eden kompjuter"""
|
|---|
| 488 | conn = sqlite3.connect(DB_FILE)
|
|---|
| 489 | c = conn.cursor()
|
|---|
| 490 |
|
|---|
| 491 | # Osnovni informacii
|
|---|
| 492 | c.execute('''SELECT id, name, user, ip, os, first_seen, last_seen
|
|---|
| 493 | FROM computers WHERE name = ?''', (computer_name,))
|
|---|
| 494 | computer_data = c.fetchone()
|
|---|
| 495 |
|
|---|
| 496 | if not computer_data:
|
|---|
| 497 | conn.close()
|
|---|
| 498 | return jsonify({"error": "Computer not found"}), 404
|
|---|
| 499 |
|
|---|
| 500 | computer_id = computer_data[0]
|
|---|
| 501 |
|
|---|
| 502 | # Broj na site zapisi
|
|---|
| 503 | c.execute('SELECT COUNT(*) FROM computer_history WHERE computer_id = ?', (computer_id,))
|
|---|
| 504 | total_logs = c.fetchone()[0]
|
|---|
| 505 |
|
|---|
| 506 | # Site history zapisi
|
|---|
| 507 | c.execute('''SELECT cpu_usage, ram_usage, disk_usage, process_count, timestamp
|
|---|
| 508 | FROM computer_history
|
|---|
| 509 | WHERE computer_id = ?
|
|---|
| 510 | ORDER BY timestamp DESC LIMIT 100''', (computer_id,))
|
|---|
| 511 | history = []
|
|---|
| 512 | for row in c.fetchall():
|
|---|
| 513 | history.append({
|
|---|
| 514 | 'cpu_usage': row[0],
|
|---|
| 515 | 'ram_usage': row[1],
|
|---|
| 516 | 'disk_usage': row[2],
|
|---|
| 517 | 'process_count': row[3],
|
|---|
| 518 | 'timestamp': row[4]
|
|---|
| 519 | })
|
|---|
| 520 |
|
|---|
| 521 | # Posledni procesi
|
|---|
| 522 | c.execute('''SELECT pid, name, username, timestamp
|
|---|
| 523 | FROM computer_processes
|
|---|
| 524 | WHERE computer_id = ?
|
|---|
| 525 | ORDER BY timestamp DESC LIMIT 50''', (computer_id,))
|
|---|
| 526 | recent_processes = []
|
|---|
| 527 | for row in c.fetchall():
|
|---|
| 528 | recent_processes.append({
|
|---|
| 529 | 'pid': row[0],
|
|---|
| 530 | 'name': row[1],
|
|---|
| 531 | 'username': row[2],
|
|---|
| 532 | 'timestamp': row[3]
|
|---|
| 533 | })
|
|---|
| 534 |
|
|---|
| 535 | conn.close()
|
|---|
| 536 |
|
|---|
| 537 | return jsonify({
|
|---|
| 538 | 'computer': {
|
|---|
| 539 | 'id': computer_data[0],
|
|---|
| 540 | 'name': computer_data[1],
|
|---|
| 541 | 'user': computer_data[2],
|
|---|
| 542 | 'ip': computer_data[3],
|
|---|
| 543 | 'os': computer_data[4],
|
|---|
| 544 | 'first_seen': computer_data[5],
|
|---|
| 545 | 'last_seen': computer_data[6],
|
|---|
| 546 | 'total_logs': total_logs
|
|---|
| 547 | },
|
|---|
| 548 | 'history': history,
|
|---|
| 549 | 'recent_processes': recent_processes
|
|---|
| 550 | })
|
|---|
| 551 |
|
|---|
| 552 |
|
|---|
| 553 | @app.route('/ping')
|
|---|
| 554 | def ping():
|
|---|
| 555 | return jsonify({
|
|---|
| 556 | 'status': 'alive',
|
|---|
| 557 | 'server_time': datetime.now().isoformat()
|
|---|
| 558 | })
|
|---|
| 559 |
|
|---|
| 560 |
|
|---|
| 561 | def get_local_ip():
|
|---|
| 562 | try:
|
|---|
| 563 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|---|
| 564 | s.connect(("8.8.8.8", 80))
|
|---|
| 565 | local_ip = s.getsockname()[0]
|
|---|
| 566 | s.close()
|
|---|
| 567 | return local_ip
|
|---|
| 568 | except:
|
|---|
| 569 | return "127.0.0.1"
|
|---|
| 570 |
|
|---|
| 571 |
|
|---|
| 572 | if __name__ == '__main__':
|
|---|
| 573 | init_database()
|
|---|
| 574 |
|
|---|
| 575 | local_ip = get_local_ip()
|
|---|
| 576 |
|
|---|
| 577 | print("\n" + "=" * 70)
|
|---|
| 578 | print("🚀 FIXED LAN LOG SERVER")
|
|---|
| 579 | print("=" * 70)
|
|---|
| 580 | print(f"🌐 Server IP: {local_ip}:5555")
|
|---|
| 581 | print(f"📊 Dashboard: http://{local_ip}:5555/")
|
|---|
| 582 | print(f"📊 Alternative: http://localhost:5555/")
|
|---|
| 583 | print(f"🏥 Health check: http://{local_ip}:5555/ping")
|
|---|
| 584 | print("\n📡 Data will be grouped by computer name")
|
|---|
| 585 | print("✅ Each computer appears only once")
|
|---|
| 586 | print("✅ Click on computer to see all collected data")
|
|---|
| 587 | print("=" * 70 + "\n")
|
|---|
| 588 |
|
|---|
| 589 | app.run(host='0.0.0.0', port=5555, debug=False) |
|---|