From 8ca650cf118b1112d2feec57c55ac87dcc66a7a8 Mon Sep 17 00:00:00 2001 From: Nirodan Date: Tue, 17 Jun 2025 11:28:06 +0200 Subject: [PATCH] Funktionstrennung 1.0 --- backend/app.py | 2 +- backend/auth/__init__.py | 9 +++++++ backend/auth/login.py | 45 ++++++++++++++++++++++++++++++++ backend/auth/logout.py | 6 +++++ backend/auth/token.py | 22 ++++++++++++++++ backend/config/__init__.py | 1 + backend/config/db_config.py | 35 +++++++++++++++++++++++++ backend/config/setup_routes.py | 47 ++++++++++++++++++++++++++++++++++ backend/datenbankverbindung.py | 47 ---------------------------------- backend/tools/__init__.py | 1 + backend/tools/md5.py | 32 +++++++++++++++++++++++ 11 files changed, 199 insertions(+), 48 deletions(-) create mode 100644 backend/auth/__init__.py create mode 100644 backend/auth/logout.py create mode 100644 backend/auth/token.py create mode 100644 backend/config/__init__.py create mode 100644 backend/config/db_config.py create mode 100644 backend/config/setup_routes.py delete mode 100644 backend/datenbankverbindung.py create mode 100644 backend/tools/__init__.py create mode 100644 backend/tools/md5.py diff --git a/backend/app.py b/backend/app.py index 7bc0e16..7754dad 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,5 +1,5 @@ from flask import Flask, request, render_template, jsonify, redirect, send_from_directory -from datenbankverbindung import lade_db_config, speichere_db_config, teste_verbindung, initialisiere_admin_user +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 diff --git a/backend/auth/__init__.py b/backend/auth/__init__.py new file mode 100644 index 0000000..fa8decf --- /dev/null +++ b/backend/auth/__init__.py @@ -0,0 +1,9 @@ +from flask import Blueprint +from auth.login import login_route +from auth.logout import logout_route + +auth_bp = Blueprint('auth', __name__) + +# Endpunkte registrieren +auth_bp.add_url_rule('/api/login', view_func=login_route, methods=['POST']) +auth_bp.add_url_rule('/api/logout', view_func=logout_route, methods=['POST']) diff --git a/backend/auth/login.py b/backend/auth/login.py index e69de29..a82dc31 100644 --- a/backend/auth/login.py +++ b/backend/auth/login.py @@ -0,0 +1,45 @@ +from flask import request, jsonify +from mysql.connector import connect +from werkzeug.security import check_password_hash +from datetime import datetime, timedelta +import jwt + +from util.logger import logger +from util.db_config import load_db_config +from auth.token import SECRET_KEY + +def login_route(): + data = request.get_json() + username = data.get('username') + password = data.get('password') + + try: + config = load_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 successful: {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 failed: {username}") + return jsonify({"message": "Login failed"}), 401 + + except Exception as e: + logger.error(f"[Login Error] {e}") + return jsonify({"message": "Server error"}), 500 diff --git a/backend/auth/logout.py b/backend/auth/logout.py new file mode 100644 index 0000000..e970080 --- /dev/null +++ b/backend/auth/logout.py @@ -0,0 +1,6 @@ +from flask import jsonify +from util.logger import logger + +def logout_route(): + logger.info("๐Ÿ‘‹ Logout called") + return jsonify({"message": "Logout successful"}) diff --git a/backend/auth/token.py b/backend/auth/token.py new file mode 100644 index 0000000..6387256 --- /dev/null +++ b/backend/auth/token.py @@ -0,0 +1,22 @@ +from flask import request +from jwt import decode, ExpiredSignatureError, InvalidTokenError +from util.logger import logger + +SECRET_KEY = "bitte_hier_dein_geheimes_passwort_setzen" # spรคter .env verwenden + +def verify_token(): + auth_header = request.headers.get("Authorization", "") + if not auth_header.startswith("Bearer "): + logger.warning("๐Ÿ” Invalid Bearer header") + return None + + token = auth_header.replace("Bearer ", "") + try: + decoded = decode(token, SECRET_KEY, algorithms=["HS256"]) + return decoded + except ExpiredSignatureError: + logger.warning("๐Ÿ” Token expired") + return None + except InvalidTokenError: + logger.warning("๐Ÿ” Invalid token") + return None diff --git a/backend/config/__init__.py b/backend/config/__init__.py new file mode 100644 index 0000000..1639c01 --- /dev/null +++ b/backend/config/__init__.py @@ -0,0 +1 @@ +from .setup_routes import setup_blueprint diff --git a/backend/config/db_config.py b/backend/config/db_config.py new file mode 100644 index 0000000..2094aff --- /dev/null +++ b/backend/config/db_config.py @@ -0,0 +1,35 @@ +import os +import json +import mysql.connector +from util.logger import logger + +CONFIG_PATH = "config/db_config.json" + +def is_configured(): + return os.path.exists(CONFIG_PATH) + +def load_config(): + try: + with open(CONFIG_PATH, "r") as file: + return json.load(file) + except Exception as e: + logger.error(f"[DB-Config] Fehler beim Laden: {e}") + return None + +def save_config(config): + try: + os.makedirs("config", exist_ok=True) + with open(CONFIG_PATH, "w") as file: + json.dump(config, file, indent=2) + logger.info("[DB-Config] Konfiguration gespeichert.") + except Exception as e: + logger.error(f"[DB-Config] Fehler beim Speichern: {e}") + +def test_connection(config) -> bool: + try: + conn = mysql.connector.connect(**config) + conn.close() + return True + except Exception as e: + logger.warning(f"[DB-Check] Verbindung fehlgeschlagen: {e}") + return False diff --git a/backend/config/setup_routes.py b/backend/config/setup_routes.py new file mode 100644 index 0000000..9a222b4 --- /dev/null +++ b/backend/config/setup_routes.py @@ -0,0 +1,47 @@ +import time +import os +from flask import Blueprint, request, render_template, redirect, jsonify +from config.db_config import load_config, save_config, test_connection, is_configured +from auth.setup_admin import initialize_admin_user +from util.logger import logger + +setup_blueprint = Blueprint("setup", __name__, template_folder="../backend/templates") + +MAX_WAIT = 30 +WAIT_INTERVAL = 10 + +def wait_for_db(): + elapsed = 0 + config = load_config() + while not test_connection(config) and elapsed < MAX_WAIT: + logger.info(f"[SETUP] DB nicht erreichbar โ€“ warte {WAIT_INTERVAL}s...") + time.sleep(WAIT_INTERVAL) + elapsed += WAIT_INTERVAL + return elapsed < MAX_WAIT + +@setup_blueprint.route('/api/status') +def status(): + if not is_configured(): + return jsonify({"status": "init", "db_connected": False}) + elif test_connection(load_config()): + return jsonify({"status": "ready", "db_connected": True}) + else: + return jsonify({"status": "error", "db_connected": False}) + +@setup_blueprint.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'] + } + save_config(db_config) + if test_connection(db_config): + initialize_admin_user(db_config) + return redirect('/') + else: + return "Verbindung fehlgeschlagen. Bitte zurรผck und prรผfen.", 500 + return render_template('setup.html') diff --git a/backend/datenbankverbindung.py b/backend/datenbankverbindung.py deleted file mode 100644 index 96ac41f..0000000 --- a/backend/datenbankverbindung.py +++ /dev/null @@ -1,47 +0,0 @@ -import json -import mysql.connector -from werkzeug.security import generate_password_hash - - -def lade_db_config(pfad='config/db_config.json'): - with open(pfad, 'r') as f: - return json.load(f) - -def speichere_db_config(daten, pfad='config/db_config.json'): - with open(pfad, 'w') as f: - json.dump(daten, f, indent=2) - -def teste_verbindung(db_config): - try: - conn = mysql.connector.connect(**db_config) - conn.close() - return True - except mysql.connector.Error as e: - print(f"[Fehler] DB-Verbindung fehlgeschlagen: {e}") - return False - -def initialisiere_admin_user(db_config): - conn = mysql.connector.connect(**db_config) - cursor = conn.cursor() - - cursor.execute(""" - CREATE TABLE IF NOT EXISTS users ( - id INT AUTO_INCREMENT PRIMARY KEY, - username VARCHAR(255) UNIQUE NOT NULL, - password VARCHAR(255) NOT NULL, - role ENUM('user', 'admin') NOT NULL DEFAULT 'user' - ) - """) - - cursor.execute("SELECT id FROM users WHERE username = 'admin'") - if not cursor.fetchone(): - hashed_pw = generate_password_hash('admin') - cursor.execute(""" - INSERT INTO users (username, password, role) - VALUES (%s, %s, 'admin') - """, ('admin', hashed_pw)) - print("[INFO] Admin-Account wurde erstellt (gehashed): admin / admin") - - conn.commit() - cursor.close() - conn.close() diff --git a/backend/tools/__init__.py b/backend/tools/__init__.py new file mode 100644 index 0000000..45f169f --- /dev/null +++ b/backend/tools/__init__.py @@ -0,0 +1 @@ +from .md5 import md5_blueprint diff --git a/backend/tools/md5.py b/backend/tools/md5.py new file mode 100644 index 0000000..c17874c --- /dev/null +++ b/backend/tools/md5.py @@ -0,0 +1,32 @@ +from flask import Blueprint, request, jsonify +import hashlib +from util.logger import logger +from auth.token import verify_token + +md5_blueprint = Blueprint('md5_tool', __name__) + +@md5_blueprint.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", "") + + 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