from flask import Flask, request, render_template, jsonify, redirect, url_for import sqlite3 from datetime import datetime from urllib.parse import unquote import requests import threading app = Flask(__name__) DATABASE = 'data/database.db' # Updated path for the database # Route to handle log submissions @app.route('/logs', methods=['POST']) @app.route('/log', methods=['POST']) def log_event(): try: #print(f"Connecting to database at: {DATABASE}") # Get the JSON payload data = request.json if not data: return {"error": "Invalid or missing JSON payload"}, 400 #print(f"Received request data: {data}") # Extract fields from the JSON payload hostname = data.get('hostname') device_ip = data.get('device_ip') nume_masa = data.get('nume_masa') log_message = data.get('log_message') timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # Validate required fields if not hostname or not device_ip or not nume_masa or not log_message: print("Validation failed: Missing required fields") return {"error": "Missing required fields"}, 400 # Save the log to the database with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() cursor.execute(''' INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description) VALUES (?, ?, ?, ?, ?) ''', (hostname, device_ip, nume_masa, timestamp, log_message)) conn.commit() print("Log saved successfully") return {"message": "Log saved successfully"}, 201 except sqlite3.Error as e: print(f"Database error: {e}") return {"error": f"Database connection failed: {e}"}, 500 except Exception as e: print(f"Unexpected error: {e}") return {"error": "An unexpected error occurred"}, 500 # Route to display the dashboard (excluding server logs) @app.route('/dashboard', methods=['GET']) def dashboard(): with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() # Fetch the last 60 logs excluding server logs, ordered by timestamp in descending order cursor.execute(''' SELECT hostname, device_ip, nume_masa, timestamp, event_description FROM logs WHERE hostname != 'SERVER' ORDER BY timestamp DESC LIMIT 60 ''') logs = cursor.fetchall() return render_template('dashboard.html', logs=logs) # Route to display logs for a specific device (excluding server logs) @app.route('/device_logs/', methods=['GET']) def device_logs(nume_masa): nume_masa = unquote(nume_masa) # Decode URL-encoded value with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() # Order logs by timestamp in descending order, excluding server logs cursor.execute(''' SELECT hostname, nume_masa, timestamp, event_description FROM logs WHERE nume_masa = ? AND hostname != 'SERVER' ORDER BY timestamp DESC ''', (nume_masa,)) logs = cursor.fetchall() return render_template('device_logs.html', logs=logs, nume_masa=nume_masa) @app.route('/unique_devices', methods=['GET']) def unique_devices(): with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() # Query to get unique devices with their most recent log (excluding server logs) cursor.execute(''' SELECT hostname, device_ip, MAX(timestamp) AS last_log, event_description FROM logs WHERE hostname != 'SERVER' GROUP BY hostname, device_ip ORDER BY last_log DESC ''') devices = cursor.fetchall() return render_template('unique_devices.html', devices=devices) @app.route('/hostname_logs/', methods=['GET']) def hostname_logs(hostname): with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() # Fetch logs for the specified hostname (excluding server logs) cursor.execute(''' SELECT hostname, nume_masa, timestamp, event_description FROM logs WHERE hostname = ? AND hostname != 'SERVER' ORDER BY timestamp DESC ''', (hostname,)) logs = cursor.fetchall() return render_template('hostname_logs.html', logs=logs, hostname=hostname) # Route to display server logs only @app.route('/server_logs', methods=['GET']) def server_logs(): with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() # Fetch only server logs, ordered by timestamp in descending order cursor.execute(''' SELECT hostname, device_ip, nume_masa, timestamp, event_description FROM logs WHERE hostname = 'SERVER' ORDER BY timestamp DESC ''') logs = cursor.fetchall() return render_template('server_logs.html', logs=logs) # Function to execute command on a remote device def execute_command_on_device(device_ip, command): """ Send command to a device for execution """ try: url = f"http://{device_ip}:80/execute_command" payload = {"command": command} response = requests.post(url, json=payload, timeout=30) if response.status_code == 200: result = response.json() return {"success": True, "result": result} else: error_data = response.json() if response.content else {"error": "Unknown error"} return {"success": False, "error": error_data} except requests.exceptions.RequestException as e: return {"success": False, "error": f"Connection error: {str(e)}"} except Exception as e: return {"success": False, "error": f"Unexpected error: {str(e)}"} # Function to get device status def get_device_status(device_ip): """ Get status information from a device """ try: url = f"http://{device_ip}:80/status" response = requests.get(url, timeout=10) if response.status_code == 200: return {"success": True, "status": response.json()} else: return {"success": False, "error": "Failed to get device status"} except requests.exceptions.RequestException as e: return {"success": False, "error": f"Connection error: {str(e)}"} except Exception as e: return {"success": False, "error": f"Unexpected error: {str(e)}"} # Route to display device management page (excluding server) @app.route('/device_management', methods=['GET']) def device_management(): with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() # Get unique devices excluding server cursor.execute(''' SELECT hostname, device_ip, MAX(timestamp) AS last_log FROM logs WHERE hostname != 'SERVER' GROUP BY hostname, device_ip ORDER BY last_log DESC ''') devices = cursor.fetchall() return render_template('device_management.html', devices=devices) # Route to execute command on a specific device @app.route('/execute_command', methods=['POST']) def execute_command(): try: data = request.json device_ip = data.get('device_ip') command = data.get('command') if not device_ip or not command: return jsonify({"error": "device_ip and command are required"}), 400 # Execute command on device result = execute_command_on_device(device_ip, command) # Log the command execution attempt with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() status = "SUCCESS" if result['success'] else "FAILED" log_message = f"Command '{command}' {status}" if not result['success']: log_message += f" - Error: {result['error']}" cursor.execute(''' INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description) VALUES (?, ?, ?, ?, ?) ''', ("SERVER", device_ip, "COMMAND", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message)) conn.commit() return jsonify(result), 200 if result['success'] else 400 except Exception as e: return jsonify({"error": f"Server error: {str(e)}"}), 500 # Route to get device status @app.route('/device_status/', methods=['GET']) def device_status(device_ip): result = get_device_status(device_ip) return jsonify(result), 200 if result['success'] else 400 # Route to execute command on multiple devices @app.route('/execute_command_bulk', methods=['POST']) def execute_command_bulk(): try: data = request.json device_ips = data.get('device_ips', []) command = data.get('command') if not device_ips or not command: return jsonify({"error": "device_ips and command are required"}), 400 results = {} threads = [] def execute_on_device(ip): results[ip] = execute_command_on_device(ip, command) # Execute commands in parallel for ip in device_ips: thread = threading.Thread(target=execute_on_device, args=(ip,)) threads.append(thread) thread.start() # Wait for all threads to complete for thread in threads: thread.join() # Log bulk command execution with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() for ip, result in results.items(): status = "SUCCESS" if result['success'] else "FAILED" log_message = f"Bulk command '{command}' {status}" if not result['success']: log_message += f" - Error: {result['error']}" cursor.execute(''' INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description) VALUES (?, ?, ?, ?, ?) ''', ("SERVER", ip, "BULK_COMMAND", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message)) conn.commit() return jsonify({"results": results}), 200 except Exception as e: return jsonify({"error": f"Server error: {str(e)}"}), 500 @app.route('/auto_update_devices', methods=['POST']) def auto_update_devices(): """ Trigger auto-update on selected devices """ try: data = request.json device_ips = data.get('device_ips', []) if not device_ips: return jsonify({"error": "device_ips list is required"}), 400 results = [] for ip in device_ips: try: # Send auto-update command to device response = requests.post( f'http://{ip}:80/auto_update', json={}, timeout=10 ) if response.status_code == 200: result_data = response.json() results.append({ "device_ip": ip, "success": True, "status": result_data.get('status'), "message": result_data.get('message'), "old_version": result_data.get('old_version'), "new_version": result_data.get('new_version') }) # Log successful update with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() log_message = f"Auto-update: {result_data.get('message', 'Update initiated')}" cursor.execute(''' INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description) VALUES (?, ?, ?, ?, ?) ''', ("SERVER", ip, "AUTO_UPDATE", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message)) conn.commit() else: error_msg = f"HTTP {response.status_code}" try: error_data = response.json() error_msg = error_data.get('error', error_msg) except: pass results.append({ "device_ip": ip, "success": False, "error": error_msg }) except requests.exceptions.Timeout: results.append({ "device_ip": ip, "success": False, "error": "Request timeout" }) except requests.exceptions.ConnectionError: results.append({ "device_ip": ip, "success": False, "error": "Connection failed - device may be offline" }) except Exception as e: results.append({ "device_ip": ip, "success": False, "error": str(e) }) # Log failed update attempt if not results[-1]['success']: with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() log_message = f"Auto-update failed: {results[-1]['error']}" cursor.execute(''' INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description) VALUES (?, ?, ?, ?, ?) ''', ("SERVER", ip, "AUTO_UPDATE", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), log_message)) conn.commit() return jsonify({"results": results}), 200 except Exception as e: return jsonify({"error": f"Server error: {str(e)}"}), 500 # Route to clear and reset the database @app.route('/reset_database', methods=['POST']) def reset_database(): """ Clear all data from the database and reinitialize with fresh schema """ try: with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() # Get the count of logs before deletion for logging cursor.execute('SELECT COUNT(*) FROM logs') log_count = cursor.fetchone()[0] # Drop the existing logs table cursor.execute('DROP TABLE IF EXISTS logs') # Recreate the logs table with fresh schema cursor.execute(''' CREATE TABLE logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, hostname TEXT NOT NULL, device_ip TEXT NOT NULL, nume_masa TEXT NOT NULL, timestamp TEXT NOT NULL, event_description TEXT NOT NULL ) ''') # Insert a system log entry to mark the database reset reset_timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') cursor.execute(''' INSERT INTO logs (hostname, device_ip, nume_masa, timestamp, event_description) VALUES (?, ?, ?, ?, ?) ''', ("SERVER", "127.0.0.1", "SYSTEM", reset_timestamp, f"Database cleared and reinitialized - {log_count} logs deleted")) conn.commit() return jsonify({ "success": True, "message": "Database successfully cleared and reinitialized", "timestamp": reset_timestamp, "deleted_count": log_count }), 200 except sqlite3.Error as e: return jsonify({ "success": False, "error": f"Database error: {str(e)}" }), 500 except Exception as e: return jsonify({ "success": False, "error": f"Unexpected error: {str(e)}" }), 500 # Route to get database statistics @app.route('/database_stats', methods=['GET']) def database_stats(): """ Get database statistics including log count """ try: with sqlite3.connect(DATABASE) as conn: cursor = conn.cursor() cursor.execute('SELECT COUNT(*) FROM logs') total_logs = cursor.fetchone()[0] cursor.execute('SELECT COUNT(DISTINCT hostname) FROM logs') unique_devices = cursor.fetchone()[0] cursor.execute('SELECT MIN(timestamp), MAX(timestamp) FROM logs') date_range = cursor.fetchone() return jsonify({ "success": True, "total_logs": total_logs, "unique_devices": unique_devices, "earliest_log": date_range[0], "latest_log": date_range[1] }), 200 except sqlite3.Error as e: return jsonify({ "success": False, "error": f"Database error: {str(e)}" }), 500 except Exception as e: return jsonify({ "success": False, "error": f"Unexpected error: {str(e)}" }), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=80)