Update flask_proxmenux_routes.py

This commit is contained in:
MacRimi
2026-04-16 20:13:13 +02:00
parent 2b306c9033
commit 7d4ea806a2
2 changed files with 253 additions and 30 deletions
+144 -26
View File
@@ -1,33 +1,97 @@
from flask import Blueprint, jsonify
from flask import Blueprint, jsonify, request
import json
import os
import re
proxmenux_bp = Blueprint('proxmenux', __name__)
# Tool descriptions mapping
TOOL_DESCRIPTIONS = {
'lvm_repair': 'LVM PV Headers Repair',
'repo_cleanup': 'Repository Cleanup',
'subscription_banner': 'Subscription Banner Removal',
'time_sync': 'Time Synchronization',
'apt_languages': 'APT Language Skip',
'journald': 'Journald Optimization',
'logrotate': 'Logrotate Optimization',
'system_limits': 'System Limits Increase',
'entropy': 'Entropy Generation (haveged)',
'memory_settings': 'Memory Settings Optimization',
'kernel_panic': 'Kernel Panic Configuration',
'apt_ipv4': 'APT IPv4 Force',
'kexec': 'kexec for quick reboots',
'network_optimization': 'Network Optimizations',
'bashrc_custom': 'Bashrc Customization',
'figurine': 'Figurine',
'fastfetch': 'Fastfetch',
'log2ram': 'Log2ram (SSD Protection)',
'amd_fixes': 'AMD CPU (Ryzen/EPYC) fixes',
'persistent_network': 'Setting persistent network interfaces'
# Tool metadata: description, function name in bash script, and version
# version: current version of the optimization function
# function: the bash function name that implements this optimization
TOOL_METADATA = {
'subscription_banner': {'name': 'Subscription Banner Removal', 'function': 'remove_subscription_banner', 'version': '1.0'},
'time_sync': {'name': 'Time Synchronization', 'function': 'configure_time_sync', 'version': '1.0'},
'apt_languages': {'name': 'APT Language Skip', 'function': 'skip_apt_languages', 'version': '1.0'},
'journald': {'name': 'Journald Optimization', 'function': 'optimize_journald', 'version': '1.0'},
'logrotate': {'name': 'Logrotate Optimization', 'function': 'optimize_logrotate', 'version': '1.0'},
'system_limits': {'name': 'System Limits Increase', 'function': 'increase_system_limits', 'version': '1.0'},
'entropy': {'name': 'Entropy Generation (haveged)', 'function': 'configure_entropy', 'version': '1.0'},
'memory_settings': {'name': 'Memory Settings Optimization', 'function': 'optimize_memory_settings', 'version': '1.0'},
'kernel_panic': {'name': 'Kernel Panic Configuration', 'function': 'configure_kernel_panic', 'version': '1.0'},
'apt_ipv4': {'name': 'APT IPv4 Force', 'function': 'force_apt_ipv4', 'version': '1.0'},
'kexec': {'name': 'kexec for quick reboots', 'function': 'enable_kexec', 'version': '1.0'},
'network_optimization': {'name': 'Network Optimizations', 'function': 'apply_network_optimizations', 'version': '1.0'},
'bashrc_custom': {'name': 'Bashrc Customization', 'function': 'customize_bashrc', 'version': '1.0'},
'figurine': {'name': 'Figurine', 'function': 'configure_figurine', 'version': '1.0'},
'fastfetch': {'name': 'Fastfetch', 'function': 'configure_fastfetch', 'version': '1.0'},
'log2ram': {'name': 'Log2ram (SSD Protection)', 'function': 'configure_log2ram', 'version': '1.0'},
'amd_fixes': {'name': 'AMD CPU (Ryzen/EPYC) fixes', 'function': 'apply_amd_fixes', 'version': '1.0'},
'persistent_network': {'name': 'Setting persistent network interfaces', 'function': 'setup_persistent_network', 'version': '1.0'},
'vfio_iommu': {'name': 'VFIO/IOMMU Passthrough', 'function': 'enable_vfio_iommu', 'version': '1.0'},
'lvm_repair': {'name': 'LVM PV Headers Repair', 'function': 'repair_lvm_headers', 'version': '1.0'},
'repo_cleanup': {'name': 'Repository Cleanup', 'function': 'cleanup_repos', 'version': '1.0'},
}
# Backward-compatible description mapping (used by get_installed_tools)
TOOL_DESCRIPTIONS = {k: v['name'] for k, v in TOOL_METADATA.items()}
# Scripts to search for function source code (in order of preference)
_SCRIPT_PATHS = [
'/usr/local/share/proxmenux/scripts/post_install/customizable_post_install.sh',
'/usr/local/share/proxmenux/scripts/post_install/auto_post_install.sh',
]
def _extract_bash_function(function_name: str) -> dict:
"""Extract a bash function's source code from the post-install scripts.
Searches each script for `function_name() {` and captures everything
until the matching closing `}` at column 0, respecting brace nesting.
Returns {'source': str, 'script': str, 'line_start': int, 'line_end': int}
or {'source': '', 'error': '...'} on failure.
"""
for script_path in _SCRIPT_PATHS:
if not os.path.isfile(script_path):
continue
try:
with open(script_path, 'r') as f:
lines = f.readlines()
# Find function start: "function_name() {" or "function_name () {"
pattern = re.compile(rf'^{re.escape(function_name)}\s*\(\)\s*\{{')
start_idx = None
for i, line in enumerate(lines):
if pattern.match(line):
start_idx = i
break
if start_idx is None:
continue # Try next script
# Capture until the closing } at indent level 0
brace_depth = 0
end_idx = start_idx
for i in range(start_idx, len(lines)):
brace_depth += lines[i].count('{') - lines[i].count('}')
if brace_depth <= 0:
end_idx = i
break
source = ''.join(lines[start_idx:end_idx + 1])
script_name = os.path.basename(script_path)
return {
'source': source,
'script': script_name,
'line_start': start_idx + 1,
'line_end': end_idx + 1,
}
except Exception:
continue
return {'source': '', 'error': 'Function not found in available scripts'}
@proxmenux_bp.route('/api/proxmenux/update-status', methods=['GET'])
def get_update_status():
"""Get ProxMenux update availability status from config.json"""
@@ -83,14 +147,17 @@ def get_installed_tools():
with open(installed_tools_path, 'r') as f:
data = json.load(f)
# Convert to list format with descriptions
# Convert to list format with descriptions and version
tools = []
for tool_key, enabled in data.items():
if enabled: # Only include enabled tools
meta = TOOL_METADATA.get(tool_key, {})
tools.append({
'key': tool_key,
'name': TOOL_DESCRIPTIONS.get(tool_key, tool_key.replace('_', ' ').title()),
'enabled': enabled
'name': meta.get('name', tool_key.replace('_', ' ').title()),
'enabled': enabled,
'version': meta.get('version', '1.0'),
'has_source': bool(meta.get('function')),
})
# Sort alphabetically by name
@@ -112,3 +179,54 @@ def get_installed_tools():
'success': False,
'error': str(e)
}), 500
@proxmenux_bp.route('/api/proxmenux/tool-source/<tool_key>', methods=['GET'])
def get_tool_source(tool_key):
"""Get the bash source code of a specific optimization function.
Returns the function body extracted from the post-install scripts,
so users can see exactly what code was executed on their server.
"""
try:
meta = TOOL_METADATA.get(tool_key)
if not meta:
return jsonify({
'success': False,
'error': f'Unknown tool: {tool_key}'
}), 404
func_name = meta.get('function')
if not func_name:
return jsonify({
'success': False,
'error': f'No function mapping for {tool_key}'
}), 404
result = _extract_bash_function(func_name)
if not result.get('source'):
return jsonify({
'success': False,
'error': result.get('error', 'Source code not available'),
'tool': tool_key,
'function': func_name,
}), 404
return jsonify({
'success': True,
'tool': tool_key,
'name': meta['name'],
'version': meta.get('version', '1.0'),
'function': func_name,
'source': result['source'],
'script': result['script'],
'line_start': result['line_start'],
'line_end': result['line_end'],
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500