From 4c6872a866fe4c3af80d84c47d5d2f971f7f7041 Mon Sep 17 00:00:00 2001 From: Nirodan Date: Tue, 17 Jun 2025 11:36:12 +0200 Subject: [PATCH] neuschreiben der app.py nach refectoring --- backend/app.py | 202 ++++--------------------------------------------- 1 file changed, 15 insertions(+), 187 deletions(-) diff --git a/backend/app.py b/backend/app.py index 7754dad..c279130 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,112 +1,29 @@ -from flask import Flask, request, render_template, jsonify, redirect, send_from_directory -from backend.db_config import lade_db_config, speichere_db_config, teste_verbindung, initialisiere_admin_user -from datetime import datetime, timedelta -from jwt import decode, ExpiredSignatureError, InvalidTokenError - -import time +from flask import Flask, send_from_directory, redirect import os -import jwt -import logging -from logging.handlers import RotatingFileHandler -app = Flask(__name__) -app.template_folder = "templates" -SECRET_KEY = "bitte_hier_dein_geheimes_passwort_setzen" # Achtung: später z. B. aus .env +from util.logger import logger +from util.db_config import is_configured, load_config, test_connection +from util.setup_routes import setup_blueprint +from auth import auth_bp +from tools import md5_blueprint -CONFIG_PATH = "config/db_config.json" -MAX_WAIT = 30 # In Sekunden -WAIT_INTERVAL = 10 +app = Flask(__name__, template_folder="templates") +# 📦 Blueprints registrieren +app.register_blueprint(setup_blueprint) +app.register_blueprint(auth_bp) +app.register_blueprint(md5_blueprint) -# Logging-Zielpfade -log_dir = "Tools" -os.makedirs(log_dir, exist_ok=True) - -# App-Log (alles ab INFO) -app_log_handler = RotatingFileHandler( - os.path.join(log_dir, "app.log"), maxBytes=1_000_000, backupCount=3 -) -app_log_handler.setLevel(logging.INFO) -app_log_handler.setFormatter(logging.Formatter( - "%(asctime)s [%(levelname)s] %(message)s")) - -# Error-Log (nur ERROR) -error_log_handler = RotatingFileHandler( - os.path.join(log_dir, "error.log"), maxBytes=1_000_000, backupCount=3 -) -error_log_handler.setLevel(logging.ERROR) -error_log_handler.setFormatter(logging.Formatter( - "%(asctime)s [%(levelname)s] %(message)s")) - -# Logging-Grundkonfiguration -logging.basicConfig( - level=logging.INFO, - handlers=[ - app_log_handler, - error_log_handler, - logging.StreamHandler() - ] -) - -logger = logging.getLogger(__name__) - -def ist_konfiguriert(): - return os.path.exists(CONFIG_PATH) - -def versuche_verbindung_mit_warten(): - elapsed = 0 - if not ist_konfiguriert(): - logger.warning("⚠️ Noch keine Konfiguration gefunden.") - return False - config = lade_db_config() - while not teste_verbindung(config) and elapsed < MAX_WAIT: - logger.info(f"🔄 DB nicht erreichbar – warte {WAIT_INTERVAL}s...") - time.sleep(WAIT_INTERVAL) - elapsed += WAIT_INTERVAL - if elapsed >= MAX_WAIT: - logger.error("❌ Maximale Wartezeit überschritten – DB nicht erreichbar.") - return elapsed < MAX_WAIT - -@app.route('/api/status') -def status(): - if not ist_konfiguriert(): - return jsonify({"status": "init", "db_connected": False}) - elif teste_verbindung(lade_db_config()): - return jsonify({"status": "ready", "db_connected": True}) - else: - return jsonify({"status": "error", "db_connected": False}) - -@app.route('/setup', methods=['GET', 'POST']) -def setup(): - if request.method == 'POST': - db_config = { - "host": request.form['host'], - "port": int(request.form['port']), - "user": request.form['user'], - "password": request.form['password'], - "database": request.form['database'] - } - speichere_db_config(db_config) - if teste_verbindung(db_config): - initialisiere_admin_user(db_config) - logger.info("✅ Setup erfolgreich, Admin-User erstellt.") - return redirect('/') - else: - logger.error("❌ Setup fehlgeschlagen – DB-Verbindung nicht möglich.") - return "Verbindung fehlgeschlagen. Bitte zurück und prüfen.", 500 - return render_template('setup.html') - +# 🌐 React-Frontend ausliefern @app.route('/', defaults={'path': ''}) @app.route('/') -def serve_react(path): - if not ist_konfiguriert() or not teste_verbindung(lade_db_config()): +def serve_frontend(path): + if not is_configured() or not test_connection(load_config()): return redirect('/setup') - # setup und API dürfen nicht von React überdeckt werden if path.startswith('setup') or path.startswith('api'): return redirect(f'/{path}') - # Pfad zu frontend/dist absolut auflösen dist_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'frontend', 'dist')) file_path = os.path.join(dist_dir, path) @@ -115,96 +32,7 @@ def serve_react(path): else: return send_from_directory(dist_dir, 'index.html') -@app.route('/api/login', methods=['POST']) -def login(): - from mysql.connector import connect - from werkzeug.security import check_password_hash - - data = request.get_json() - username = data.get('username') - password = data.get('password') - - try: - config = lade_db_config() - conn = connect(**config) - cursor = conn.cursor(dictionary=True) - cursor.execute("SELECT * FROM users WHERE username = %s", (username,)) - user = cursor.fetchone() - cursor.close() - conn.close() - - if user and check_password_hash(user['password'], password): - logger.info(f"✅ Login erfolgreich: {username}") - - payload = { - "username": user['username'], - "role": user['role'], - "exp": datetime.utcnow() + timedelta(minutes=60) - } - token = jwt.encode(payload, SECRET_KEY, algorithm="HS256") - - return jsonify({ - "token": token, - "role": user['role'] - }) - - logger.warning(f"⛔ Login fehlgeschlagen: {username}") - return jsonify({"message": "Login fehlgeschlagen"}), 401 - - except Exception as e: - logger.error(f"[Login-Fehler] {e}") - return jsonify({"message": "Serverfehler"}), 500 - -@app.route('/api/logout', methods=['POST']) -def logout(): - return jsonify({"message": "Logout erfolgreich"}) - -def verify_token(): - auth_header = request.headers.get("Authorization", "") - if not auth_header.startswith("Bearer "): - logger.warning("🔐 Kein gültiger Bearer-Header") - return None - - token = auth_header.replace("Bearer ", "") - try: - decoded = decode(token, SECRET_KEY, algorithms=["HS256"]) - return decoded - except ExpiredSignatureError: - logger.warning("🔐 Token abgelaufen") - return None - except InvalidTokenError: - logger.warning("🔐 Ungültiger Token") - return None - - -@app.route('/api/hash/md5', methods=['POST']) -def hash_md5(): - logger.info("🔁 /api/hash/md5 aufgerufen") - - user = verify_token() - if not user: - logger.warning("⛔ Nicht autorisiert") - return jsonify({"message": "Nicht autorisiert"}), 401 - - try: - data = request.get_json() - logger.debug(f"📩 Payload: {data}") - password = data.get("password", "") - - import hashlib - result = hashlib.md5(password.encode()).hexdigest() - - logger.info(f"✅ Hash erstellt von {user['username']}") - return jsonify({ - "md5": result, - "by": user['username'] - }) - - except Exception as e: - logger.error(f"❌ Fehler beim Hashen: {e}") - return jsonify({"message": "Fehler beim Hashen"}), 500 - if __name__ == '__main__': os.makedirs("config", exist_ok=True) - app.run(host='0.0.0.0', port=5000, debug=True) # debug=True nur für Entwicklung, nicht in Produktion verwenden! + app.run(host='0.0.0.0', port=5000, debug=True)