We now have working api and simple webpages to view the data.

This commit is contained in:
Kalzu Rekku
2025-04-03 22:58:53 +03:00
parent 362d0e7b65
commit 0bee8c7fd3
9 changed files with 943 additions and 0 deletions

221
app/api/routes.py Normal file
View File

@ -0,0 +1,221 @@
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