from flask import Flask, render_template, jsonify, request, redirect, url_for, flash from cgminer_api import CgminerAPI from config_manager import ConfigManager import os import subprocess app = Flask(__name__) app.secret_key = 'necrohash_miner_control_secret' miner_api = CgminerAPI() config_mgr = ConfigManager() @app.route('/') def index(): return render_template('dashboard.html', active_page='dashboard') @app.route('/settings') def settings(): config = config_mgr.load_config() # Load mode mode = 'sha256' if os.path.exists('mining_mode.conf'): with open('mining_mode.conf', 'r') as f: mode = f.read().strip() return render_template('settings.html', config=config, active_page='settings', mode=mode) @app.route('/api/data') def api_data(): try: summary = miner_api.summary() devs = miner_api.devs() pools = miner_api.pools() stats = miner_api.stats() # Debug prints to console (visible in journalctl) if not summary: print("API Error: Summary is None") if not devs: print("API Error: Devs is None") # Process data for easier frontend consumption data = { 'summary': summary['SUMMARY'][0] if summary and 'SUMMARY' in summary else {}, 'devs': devs['DEVS'] if devs and 'DEVS' in devs else [], 'pools': pools['POOLS'] if pools and 'POOLS' in pools else [], 'stats': stats if stats else {} } return jsonify(data) except Exception as e: print(f"API Data Error: {e}") return jsonify({'error': str(e)}) @app.route('/api/log') def api_log(): log_path = 'cgminer.log' if not os.path.exists(log_path): return jsonify({'log': 'Kein Log gefunden.'}) try: # Read last 50 lines with open(log_path, 'r') as f: lines = f.readlines() return jsonify({'log': ''.join(lines[-50:])}) except Exception as e: return jsonify({'log': f'Fehler beim Lesen des Logs: {str(e)}'}) @app.route('/save_settings', methods=['POST']) def save_settings(): # Construct config object from form # Note: cgminer config structure handling new_config = config_mgr.load_config() # Start with existing # Save mode mode = request.form.get('mining_mode', 'sha256') try: with open('mining_mode.conf', 'w') as f: f.write(mode) except Exception as e: print(f"Error saving mode: {e}") # Update simple fields new_config['gridseed-options'] = f"freq={request.form.get('freq', '850')}" # Update pool (handling single pool for simplicity in this MVP, extended later) pool_url = request.form.get('pool_url') pool_user = request.form.get('pool_user') pool_pass = request.form.get('pool_pass') if pool_url: # Replace first pool or add if empty pool_data = {"url": pool_url, "user": pool_user, "pass": pool_pass} if new_config.get('pools'): new_config['pools'][0] = pool_data else: new_config['pools'] = [pool_data] if config_mgr.save_config(new_config): flash('Einstellungen gespeichert. Bitte Miner neu starten.', 'success') else: flash('Fehler beim Speichern.', 'danger') return redirect(url_for('settings')) @app.route('/save_raw_config', methods=['POST']) def save_raw_config(): try: raw_data = request.form.get('raw_config') config_data = json.loads(raw_data) if config_mgr.save_config(config_data): flash('Konfiguration gespeichert. Miner Neustart erforderlich.', 'success') else: flash('Fehler beim Speichern der Datei.', 'danger') except json.JSONDecodeError: flash('Ungültiges JSON Format.', 'danger') except Exception as e: flash(f'Fehler: {str(e)}', 'danger') return redirect(url_for('settings')) @app.route('/control/restart') def restart_miner(): # Attempt to restart via API first, if fails, might need system command result = miner_api.restart() if result: return jsonify({'status': 'success', 'message': 'Restart command sent'}) return jsonify({'status': 'error', 'message': 'Could not contact miner'}) if __name__ == '__main__': # Run on all interfaces # Port 80 requires authbind or root app.run(host='0.0.0.0', port=80, debug=True)