from flask import Blueprint, request, jsonify, current_app from datetime import datetime bp = Blueprint('api', __name__, url_prefix='/api') def authenticate_node(node_name, api_key): """Authenticate a node using its name and API key.""" auth_query = """ SELECT node_id FROM node_auth WHERE node_name = ? AND api_key = ? AND status = 'active' """ result = current_app.db.execute_read_sync(auth_query, (node_name, api_key)) return result.data[0][0] if result.success and result.data else None def authenticate_admin(api_key): """Authenticate an admin using the API key.""" return api_key == current_app.config['ADMIN_API_KEY'] @bp.route('/node-stats', methods=['POST']) def node_stats(): # Use current_app instead of bp.app # app = bp.app # Remove this line # Expected JSON payload example: # { # "node_name": "node1", # "api_key": "xxxx", # "hostname": "node1.local", # "cpu_percent": 45.5, # "memory_used_mb": 2048, # "memory_free_mb": 4096, # "disk_used_gb": 100, # "disk_free_gb": 400, # "bytes_sent": 102400, # "bytes_received": 51200 # } try: data = request.get_json() if not data or not isinstance(data, dict): return jsonify({'error': 'Invalid JSON payload'}), 400 # Required fields required = ['node_name', 'api_key'] if not all(field in data for field in required): return jsonify({'error': 'Missing required fields'}), 400 # Authenticate node node_id = authenticate_node(data['node_name'], data['api_key']) if not node_id: return jsonify({'error': 'Authentication failed'}), 401 # Update or insert node_info node_info_query = """ INSERT OR REPLACE INTO node_info ( node_id, hostname, node_nickname, ip_address, os_type, cpu_cores, total_memory_mb, total_disk_gb, manufacturer, model, location, last_updated ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """ current_app.db.execute_write(node_info_query, ( node_id, data.get('hostname', ''), data.get('node_nickname'), data.get('ip_address'), data.get('os_type'), data.get('cpu_cores'), data.get('total_memory_mb'), data.get('total_disk_gb'), data.get('manufacturer'), data.get('model'), data.get('location'), datetime.utcnow().isoformat() )) # Insert CPU usage if 'cpu_percent' in data or 'cpu_temp_celsius' in data: cpu_query = """ INSERT INTO cpu_usage (node_id, cpu_percent, cpu_temp_celsius, timestamp) VALUES (?, ?, ?, ?) """ current_app.db.execute_write(cpu_query, ( node_id, data.get('cpu_percent'), data.get('cpu_temp_celsius'), datetime.utcnow().isoformat() )) # Insert memory usage if any(k in data for k in ['memory_used_mb', 'memory_free_mb', 'memory_percent']): memory_query = """ INSERT INTO memory_usage (node_id, memory_used_mb, memory_free_mb, memory_percent, timestamp) VALUES (?, ?, ?, ?, ?) """ current_app.db.execute_write(memory_query, ( node_id, data.get('memory_used_mb', 0), data.get('memory_free_mb', 0), data.get('memory_percent', 0), datetime.utcnow().isoformat() )) # Insert disk usage if any(k in data for k in ['disk_used_gb', 'disk_free_gb', 'disk_percent', 'partition_name']): disk_query = """ INSERT INTO disk_usage (node_id, disk_used_gb, disk_free_gb, disk_percent, partition_name, timestamp) VALUES (?, ?, ?, ?, ?, ?) """ current_app.db.execute_write(disk_query, ( node_id, data.get('disk_used_gb', 0), data.get('disk_free_gb', 0), data.get('disk_percent', 0), data.get('partition_name'), datetime.utcnow().isoformat() )) # Insert network usage if any(k in data for k in ['bytes_sent', 'bytes_received', 'packets_sent', 'packets_received']): network_query = """ INSERT INTO network_usage (node_id, bytes_sent, bytes_received, packets_sent, packets_received, timestamp) VALUES (?, ?, ?, ?, ?, ?) """ current_app.db.execute_write(network_query, ( node_id, data.get('bytes_sent', 0), data.get('bytes_received', 0), data.get('packets_sent', 0), data.get('packets_received', 0), datetime.utcnow().isoformat() )) # Insert process stats if 'process_count' in data or 'zombie_process_count' in data: process_query = """ INSERT INTO process_stats (node_id, process_count, zombie_process_count, timestamp) VALUES (?, ?, ?, ?) """ current_app.db.execute_write(process_query, ( node_id, data.get('process_count', 0), data.get('zombie_process_count', 0), datetime.utcnow().isoformat() )) # Insert node connections (if provided) if 'target_node_name' in data and 'ping_latency_ms' in data: target_node_query = "SELECT node_id FROM node_auth WHERE node_name = ? AND status = 'active'" target_result = current_app.db.execute_read_sync(target_node_query, (data['target_node_name'],)) if target_result.success and target_result.data: target_node_id = target_result.data[0][0] conn_query = """ INSERT INTO node_connections (source_node_id, target_node_id, ping_latency_ms, packet_loss_percent, timestamp) VALUES (?, ?, ?, ?, ?) """ current_app.db.execute_write(conn_query, ( node_id, target_node_id, data.get('ping_latency_ms'), data.get('packet_loss_percent', 0), datetime.utcnow().isoformat() )) return jsonify({'status': 'success', 'message': 'Stats recorded'}), 201 except Exception as e: return jsonify({'error': f'Internal server error: {str(e)}'}), 500 @bp.route('/admin/nodes', methods=['POST']) def manage_nodes(): try: data = request.get_json() if not data or 'api_key' not in data: return jsonify({'error': 'Admin API key required'}), 400 if not authenticate_admin(data['api_key']): return jsonify({'error': 'Admin authentication failed'}), 401 # Expected payload: # { # "api_key": "admin-key", # "action": "add|modify|delete", # "node_name": "node1", # "node_api_key": "xxxx" (for add/modify) # } action = data.get('action') node_name = data.get('node_name') if action == 'add': node_api_key = data.get('node_api_key') if not node_name or not node_api_key: return jsonify({'error': 'node_name and node_api_key required'}), 400 query = """ INSERT INTO node_auth (node_name, api_key, status) VALUES (?, ?, 'active') ON CONFLICT(node_name) DO UPDATE SET api_key = ?, status = 'active' """ current_app.db.execute_write(query, (node_name, node_api_key, node_api_key)) return jsonify({'status': 'success', 'message': f'Node {node_name} added/updated'}), 201 elif action == 'modify': node_api_key = data.get('node_api_key') if not node_name or not node_api_key: return jsonify({'error': 'node_name and node_api_key required'}), 400 query = "UPDATE node_auth SET api_key = ? WHERE node_name = ?" current_app.db.execute_write(query, (node_api_key, node_name)) return jsonify({'status': 'success', 'message': f'Node {node_name} modified'}), 200 elif action == 'delete': if not node_name: return jsonify({'error': 'node_name required'}), 400 query = "UPDATE node_auth SET status = 'inactive' WHERE node_name = ?" current_app.db.execute_write(query, (node_name,)) return jsonify({'status': 'success', 'message': f'Node {node_name} deleted'}), 200 else: return jsonify({'error': 'Invalid action'}), 400 except Exception as e: return jsonify({'error': f'Internal server error: {str(e)}'}), 500