"feat: Added debug server and related components for improved development experience"

This commit is contained in:
2025-05-23 07:24:51 +02:00
parent d457a8d86b
commit 9f6219832c
189 changed files with 35730 additions and 133 deletions

View File

@@ -1,5 +1,4 @@
from flask import Flask, request, jsonify, g, redirect, url_for, session as flask_session, render_template, flash
from flask_cors import CORS
from flask import Flask, request, jsonify, g, redirect, url_for, session as flask_session, render_template, flash, send_from_directory
from werkzeug.security import generate_password_hash, check_password_hash
import secrets # Für bessere Salt-Generierung
from functools import wraps
@@ -17,20 +16,82 @@ from datetime import timedelta
from PyP100 import PyP100
from dotenv import load_dotenv
# Importiere Konfiguration
from config import config
# Importiere Netzwerkkonfiguration
from network_config import NetworkConfig
# Importiere Frontend V2 Blueprint
from frontend_v2_routes import frontend_v2, set_app_functions
# Lade Umgebungsvariablen
load_dotenv()
# Initialisierung
def create_app(config_name=None):
"""
Application Factory Pattern für die Flask-Anwendung.
Args:
config_name: Name der zu verwendenden Konfiguration ('development', 'production', 'testing')
Returns:
Flask: Konfigurierte Flask-Anwendung
"""
app = Flask(__name__)
# Bestimme Konfiguration
if config_name is None:
config_name = os.environ.get('FLASK_ENV', 'development')
# Lade Konfiguration
config_object = config.get(config_name, config['default'])
app.config.from_object(config_object)
# Initialisiere Konfiguration
config_object.init_app(app)
# Initialisiere Netzwerkkonfiguration
network_config = NetworkConfig(app)
# Registriere Blueprint
app.register_blueprint(frontend_v2, url_prefix='/frontend_v2')
# Konfiguriere statische Dateien für Frontend v2
@app.route('/frontend_v2/static/<path:filename>')
def frontend_v2_static(filename):
return send_from_directory(os.path.join(app.root_path, 'frontend_v2/static'), filename)
# Globale Variablen
app.config['PRINTERS'] = json.loads(app.config.get('PRINTERS', '{}'))
# Database functions registrieren
register_database_functions(app)
# Authentifizierung registrieren
register_auth_functions(app)
# API-Routen registrieren
register_api_routes(app)
# Web-UI-Routen registrieren
register_web_routes(app)
# Error-Handler registrieren
register_error_handlers(app)
# Hintergrund-Tasks registrieren
register_background_tasks(app)
return app
# Initialisierung - wird später durch create_app ersetzt
app = Flask(__name__)
CORS(app, supports_credentials=True)
# Initialisiere Netzwerkkonfiguration
network_config = NetworkConfig(app)
# Konfiguration
# Temporäre Konfiguration für Legacy-Code
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev_secret_key')
app.config['DATABASE'] = os.environ.get('DATABASE_PATH', 'instance/myp.db')
app.config['SESSION_COOKIE_HTTPONLY'] = True
@@ -39,11 +100,65 @@ app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
app.config['JOB_CHECK_INTERVAL'] = int(os.environ.get('JOB_CHECK_INTERVAL', '60')) # Sekunden
# Registriere Frontend V2 Blueprint
app.register_blueprint(frontend_v2, url_prefix='/frontend_v2')
# Übergebe Funktionen an das Frontend v2
def setup_frontend_v2():
app_functions = {
'get_current_user': get_current_user,
'get_user_by_id': get_user_by_id,
'get_socket_by_id': get_socket_by_id,
'get_job_by_id': get_job_by_id,
'get_all_sockets': get_all_sockets,
'get_all_users': get_all_users,
'get_all_jobs': get_all_jobs,
'get_jobs_by_user': get_jobs_by_user,
'login_required': login_required,
'admin_required': admin_required,
'delete_session': delete_session,
'socket_to_dict': socket_to_dict,
'job_to_dict': job_to_dict,
'user_to_dict': user_to_dict
}
set_app_functions(app_functions)
# Konfiguriere statische Dateien für Frontend v2
@app.route('/frontend_v2/static/<path:filename>')
def frontend_v2_static(filename):
return send_from_directory(os.path.join(app.root_path, 'frontend_v2/static'), filename)
# Steckdosen-Konfiguration
TAPO_USERNAME = os.environ.get('TAPO_USERNAME')
TAPO_PASSWORD = os.environ.get('TAPO_PASSWORD')
# Logging
def setup_logging(app):
"""
Konfiguriert das Logging basierend auf der Umgebung.
Args:
app: Flask-Anwendung
"""
if not app.debug and not app.testing:
# Production logging
if not os.path.exists('logs'):
os.mkdir('logs')
file_handler = RotatingFileHandler('logs/myp.log', maxBytes=10240, backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('MYP Backend starting in production mode')
else:
# Development logging
app.logger.setLevel(logging.DEBUG)
app.logger.info('MYP Backend starting in development mode')
# Logging - Legacy (wird durch setup_logging ersetzt)
if not os.path.exists('logs'):
os.mkdir('logs')
file_handler = RotatingFileHandler('logs/myp.log', maxBytes=10240, backupCount=10)
@@ -1686,9 +1801,33 @@ def stats_page():
return redirect(url_for('index'))
return render_template('stats.html', current_user=current_user, active_page='stats')
# Initialisierung und Start des Hintergrund-Threads beim ersten Request
with app.app_context():
# Diese Funktion wird nach dem App-Start aber vor dem ersten Request ausgeführt
# Registrierungsfunktionen für modularen Aufbau
def register_database_functions(app):
"""Registriert Database-Funktionen und Teardown-Handler."""
app.teardown_appcontext(close_db)
def register_auth_functions(app):
"""Registriert Authentifizierungsfunktionen."""
# Authentifizierungsfunktionen sind bereits global definiert
pass
def register_api_routes(app):
"""Registriert alle API-Routen."""
# API-Routen sind bereits global definiert
pass
def register_web_routes(app):
"""Registriert alle Web-UI-Routen."""
# Web-Routen sind bereits global definiert
pass
def register_error_handlers(app):
"""Registriert Error-Handler."""
# Error-Handler sind bereits global definiert
pass
def register_background_tasks(app):
"""Registriert Hintergrund-Tasks."""
@app.before_request
def initialize_background_tasks():
"""Startet den Hintergrund-Thread für Job-Überprüfung beim ersten Request."""
@@ -1711,6 +1850,7 @@ with app.app_context():
# Server starten
if __name__ == '__main__':
# Legacy-Modus für direkte Ausführung
with app.app_context():
init_db()
if PRINTERS:
@@ -1721,5 +1861,21 @@ if __name__ == '__main__':
job_thread = threading.Thread(target=background_job_checker, daemon=True, name='job_checker_thread')
job_thread.start()
app.logger.info("Hintergrund-Thread für Job-Überprüfung gestartet")
setup_frontend_v2()
app.run(debug=True, host='0.0.0.0')
# Produktionsmodus aktivieren
flask_env = os.environ.get('FLASK_ENV', 'development')
debug_mode = flask_env == 'development'
app.run(host='0.0.0.0', port=5000, debug=debug_mode)
else:
# Für WSGI-Server wie Gunicorn - verwende Application Factory
flask_env = os.environ.get('FLASK_ENV', 'production')
app = create_app(flask_env)
with app.app_context():
init_db()
printers_config = json.loads(app.config.get('PRINTERS', '{}'))
if printers_config:
init_printers()
setup_frontend_v2()