# app/routes/admin.py import os import json import logging from datetime import datetime from flask import Blueprint, render_template, request, jsonify, redirect, url_for, current_app from app.state import current_config from app.utils.helpers import load_configs, load_default_sounds, load_soundboard_configs admin_bp = Blueprint('admin', __name__) logger = logging.getLogger(__name__) @admin_bp.route('/') def admin(): configs = load_configs() sb_configs = load_soundboard_configs(current_app.config['SOUNDBOARD_CONFIG_DIR']) return render_template('admin.html', configs=configs, sb_configs=sb_configs) @admin_bp.route('/edit/', methods=['GET', 'POST']) def admin_edit_config(filename): path = os.path.join(current_app.config['CONFIG_DIR'], filename) if request.method == 'POST': try: new_content = request.form.get('config_content') if not new_content: raise ValueError("Kein Inhalt übermittelt") # Validierung: versuche zu parsen json.loads(new_content) with open(path, 'w', encoding='utf-8') as f: f.write(new_content) logger.info(f"Config {filename} erfolgreich gespeichert") return redirect(url_for('admin')) except json.JSONDecodeError as e: logger.error(f"Ungültiges JSON in {filename}: {e}") error_msg = f"Ungültiges JSON: {str(e)}" except Exception as e: logger.error(f"Speicherfehler {filename}: {e}") error_msg = f"Fehler beim Speichern: {str(e)}" # Bei Fehler: zurück zum Formular mit Fehlermeldung with open(path, 'r', encoding='utf-8') as f: content = f.read() return render_template('admin_edit.html', filename=filename, content=content, error=error_msg) # GET: Formular laden if not os.path.exists(path): return "Konfiguration nicht gefunden", 404 with open(path, 'r', encoding='utf-8') as f: content = f.read() return render_template('admin_edit.html', filename=filename, content=content) pass @admin_bp.route('/delete/', methods=['POST']) def admin_delete_config(filename): path = os.path.join(current_app.config['CONFIG_DIR'], filename) if os.path.exists(path): try: os.remove(path) logger.info(f"Config gelöscht: {filename}") return jsonify({"success": True, "message": f"{filename} gelöscht"}) except Exception as e: logger.error(f"Löschfehler {filename}: {e}") return jsonify({"success": False, "message": str(e)}), 500 return jsonify({"success": False, "message": "Datei nicht gefunden"}), 404 pass @admin_bp.route('/logs') @admin_bp.route('/logs/') def admin_logs(date=None): if date is None: date = datetime.now().strftime('%d') today = datetime.now().strftime('%Y-%m') # ← Hier definieren! current_month = datetime.now().strftime('%Y-%m') log_dir = os.path.join(current_app.config['LOG_DIR'], today) if not os.path.exists(log_dir): return render_template('admin_logs.html', logs="Keine Logs für diesen Tag.", date=date, dates=[], today=today) # Verfügbare Tage ... dates = sorted([ f.split('-')[0] for f in os.listdir(log_dir) if f.endswith('-info.log') or f.endswith('-error.log') ], reverse=True) # Logs laden ... info_path = os.path.join(log_dir, f"{date}-info.log") error_path = os.path.join(log_dir, f"{date}-error.log") logs = [] if os.path.exists(info_path): with open(info_path, 'r', encoding='utf-8') as f: logs.append("=== INFO-LOG ===\n" + f.read()) if os.path.exists(error_path): with open(error_path, 'r', encoding='utf-8') as f: logs.append("=== ERROR-LOG ===\n" + f.read()) log_content = "\n\n".join(logs) if logs else "Keine Logs für diesen Tag." return render_template('admin_logs.html', logs=log_content, date=date, dates=dates, today=current_month) pass def _slugify(name: str) -> str: return "".join(c.lower() if c.isalnum() else "_" for c in name).strip("_") or "config" @admin_bp.route('/create_config', methods=['POST']) def admin_create_config(): data = request.get_json() or {} name = data.get('name') or 'Neue Lok' hub_id = int(data.get('hub_id', 0)) hub_type = data.get('hub_type', '4channel') image = data.get('image') filename = _slugify(name) + '.json' path = os.path.join(current_app.config['CONFIG_DIR'], filename) if os.path.exists(path): return jsonify({"success": False, "message": "Datei existiert bereits"}), 400 template = { "name": name, "image": image or "", "hub_id": hub_id, "hub_type": hub_type, "channels": [], "sounds": [] } os.makedirs(current_app.config['CONFIG_DIR'], exist_ok=True) with open(path, 'w', encoding='utf-8') as f: json.dump(template, f, ensure_ascii=False, indent=2) logger.info(f"Neue Config angelegt: {filename}") return jsonify({"success": True, "filename": filename}) @admin_bp.route('/create_theme', methods=['POST']) def admin_create_theme(): data = request.get_json() or {} name = data.get('name') or 'Neues Theme' folder = data.get('folder') or _slugify(name) filename = data.get('filename') or 'theme.json' base_dir = current_app.config['SOUNDBOARD_CONFIG_DIR'] theme_dir = os.path.join(base_dir, folder) os.makedirs(theme_dir, exist_ok=True) path = os.path.join(theme_dir, filename) if os.path.exists(path): return jsonify({"success": False, "message": "Theme-Datei existiert bereits"}), 400 template = { "name": name, "backgrounds": [], "sounds": [], "random_pool": [], "random_limit_per_hour": 2, "random_window_minutes": 60 } with open(path, 'w', encoding='utf-8') as f: json.dump(template, f, ensure_ascii=False, indent=2) logger.info(f"Neues Theme angelegt: {os.path.relpath(path, base_dir)}") return jsonify({"success": True, "path": os.path.relpath(path, base_dir)})