feat: Einführung neuer API-Endpunkte zur Verwaltung von Benutzereinstellungen und Druckerstatus. Implementierung von Funktionen zur Überprüfung wartender Jobs und zur Aktualisierung aller Drucker. Verbesserung der Benutzeroberfläche durch optimierte Ladeanzeigen und Warnungen für Offline-Drucker. Anpassungen in den Templates zur Unterstützung neuer Funktionen und zur Verbesserung der Benutzererfahrung.

This commit is contained in:
2025-05-27 12:19:03 +02:00
parent cbe1864678
commit e9071c7b57
11 changed files with 1101 additions and 17 deletions

View File

@@ -773,6 +773,47 @@ def user_update_settings():
finally:
db_session.close()
@app.route("/api/user/settings", methods=["GET"])
@login_required
def get_user_settings():
"""Holt die aktuellen Benutzereinstellungen"""
try:
# Einstellungen aus Session oder Datenbank laden
user_settings = session.get('user_settings', {})
# Standard-Einstellungen falls keine vorhanden
default_settings = {
"theme": "system",
"reduced_motion": False,
"contrast": "normal",
"notifications": {
"new_jobs": True,
"job_updates": True,
"system": True,
"email": False
},
"privacy": {
"activity_logs": True,
"two_factor": False,
"auto_logout": 60
}
}
# Merge mit Standard-Einstellungen
settings = {**default_settings, **user_settings}
return jsonify({
"success": True,
"settings": settings
})
except Exception as e:
user_logger.error(f"Fehler beim Laden der Benutzereinstellungen: {str(e)}")
return jsonify({
"success": False,
"error": "Fehler beim Laden der Einstellungen"
}), 500
@app.route("/user/change-password", methods=["POST"])
@login_required
def user_change_password():
@@ -1461,6 +1502,59 @@ def get_job(job_id):
db_session.close()
return jsonify({"error": "Interner Serverfehler"}), 500
@app.route('/api/jobs/check-waiting', methods=['POST'])
@login_required
def check_waiting_jobs():
"""Überprüft wartende Jobs und startet sie, wenn Drucker online gehen."""
try:
db_session = get_db_session()
# Alle wartenden Jobs finden
waiting_jobs = db_session.query(Job).filter(
Job.status == "waiting_for_printer"
).all()
if not waiting_jobs:
db_session.close()
return jsonify({
"message": "Keine wartenden Jobs gefunden",
"updated_jobs": []
})
updated_jobs = []
for job in waiting_jobs:
# Drucker-Status prüfen
printer = db_session.query(Printer).get(job.printer_id)
if printer and printer.plug_ip:
status, active = check_printer_status(printer.plug_ip)
if status == "online" and active:
# Drucker ist jetzt online - Job kann geplant werden
job.status = "scheduled"
updated_jobs.append({
"id": job.id,
"name": job.name,
"printer_name": printer.name,
"status": "scheduled"
})
jobs_logger.info(f"Job {job.id} von 'waiting_for_printer' zu 'scheduled' geändert - Drucker {printer.name} ist online")
if updated_jobs:
db_session.commit()
db_session.close()
return jsonify({
"message": f"{len(updated_jobs)} Jobs aktualisiert",
"updated_jobs": updated_jobs
})
except Exception as e:
jobs_logger.error(f"Fehler beim Überprüfen wartender Jobs: {str(e)}")
return jsonify({"error": "Interner Serverfehler"}), 500
@app.route('/api/jobs/active', methods=['GET'])
@login_required
def get_active_jobs():
@@ -1548,6 +1642,15 @@ def create_job():
db_session.close()
return jsonify({"error": "Drucker nicht gefunden"}), 404
# Prüfen, ob der Drucker online ist
printer_status, printer_active = check_printer_status(printer.plug_ip if printer.plug_ip else "")
# Status basierend auf Drucker-Verfügbarkeit setzen
if printer_status == "online" and printer_active:
job_status = "scheduled"
else:
job_status = "waiting_for_printer"
# Neuen Job erstellen
new_job = Job(
name=name,
@@ -1556,7 +1659,7 @@ def create_job():
owner_id=current_user.id,
start_at=start_at,
end_at=end_at,
status="scheduled",
status=job_status,
file_path=file_path,
duration_minutes=duration_minutes
)
@@ -3526,6 +3629,244 @@ def clear_printer_cache():
"error": f"Fehler beim Löschen des Cache: {str(e)}"
}), 500
# ===== FEHLENDE ADMIN-API-ENDPUNKTE =====
@app.route('/api/admin/cache/clear', methods=['POST'])
@admin_required
def clear_admin_cache():
"""Leert den System-Cache"""
try:
# Cache-Verzeichnisse leeren
import shutil
import os
cache_dirs = [
os.path.join(os.path.dirname(__file__), 'static', 'cache'),
os.path.join(os.path.dirname(__file__), '__pycache__'),
]
cleared_items = 0
for cache_dir in cache_dirs:
if os.path.exists(cache_dir):
for item in os.listdir(cache_dir):
item_path = os.path.join(cache_dir, item)
try:
if os.path.isfile(item_path):
os.unlink(item_path)
cleared_items += 1
elif os.path.isdir(item_path):
shutil.rmtree(item_path)
cleared_items += 1
except Exception as e:
app_logger.warning(f"Konnte Cache-Element nicht löschen: {item_path} - {str(e)}")
# Modell-Cache leeren
from models import clear_cache
clear_cache()
app_logger.info(f"System-Cache geleert: {cleared_items} Elemente entfernt")
return jsonify({
"success": True,
"message": f"Cache erfolgreich geleert ({cleared_items} Elemente)",
"cleared_items": cleared_items
})
except Exception as e:
app_logger.error(f"Fehler beim Leeren des Cache: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Leeren des Cache: {str(e)}"
}), 500
@app.route('/api/admin/system/restart', methods=['POST'])
@admin_required
def restart_admin_system():
"""Startet das System neu (nur für Entwicklung)"""
try:
import os
import signal
app_logger.warning("System-Neustart durch Admin angefordert")
# In Produktionsumgebung sollte dies anders gehandhabt werden
if os.environ.get('FLASK_ENV') == 'development':
# Graceful shutdown für Development
def shutdown_server():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
shutdown_server()
return jsonify({
"success": True,
"message": "System wird neugestartet..."
})
else:
# Für Produktion - Signal an Parent Process
os.kill(os.getpid(), signal.SIGTERM)
return jsonify({
"success": True,
"message": "Neustart-Signal gesendet"
})
except Exception as e:
app_logger.error(f"Fehler beim System-Neustart: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Neustart: {str(e)}"
}), 500
@app.route('/api/admin/printers/update-all', methods=['POST'])
@admin_required
def update_all_printers():
"""Aktualisiert den Status aller Drucker"""
try:
db_session = get_db_session()
printers = db_session.query(Printer).all()
updated_printers = []
for printer in printers:
if printer.plug_ip:
try:
status, active = check_printer_status(printer.plug_ip)
old_status = printer.status
printer.update_status(status, active)
updated_printers.append({
"id": printer.id,
"name": printer.name,
"old_status": old_status,
"new_status": status,
"active": active
})
except Exception as e:
printers_logger.warning(f"Fehler beim Aktualisieren von Drucker {printer.name}: {str(e)}")
db_session.commit()
db_session.close()
app_logger.info(f"Status von {len(updated_printers)} Druckern aktualisiert")
return jsonify({
"success": True,
"message": f"Status von {len(updated_printers)} Druckern aktualisiert",
"updated_printers": updated_printers
})
except Exception as e:
app_logger.error(f"Fehler beim Aktualisieren aller Drucker: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Aktualisieren: {str(e)}"
}), 500
@app.route('/api/admin/settings', methods=['GET'])
@admin_required
def get_admin_settings():
"""Holt die aktuellen Admin-Einstellungen"""
try:
from config.settings import (
FLASK_HOST, FLASK_PORT, FLASK_DEBUG, SESSION_LIFETIME,
SCHEDULER_INTERVAL, SCHEDULER_ENABLED, SSL_ENABLED
)
settings = {
"server": {
"host": FLASK_HOST,
"port": FLASK_PORT,
"debug": FLASK_DEBUG,
"ssl_enabled": SSL_ENABLED
},
"session": {
"lifetime_minutes": SESSION_LIFETIME.total_seconds() / 60
},
"scheduler": {
"interval_seconds": SCHEDULER_INTERVAL,
"enabled": SCHEDULER_ENABLED
}
}
return jsonify({
"success": True,
"settings": settings
})
except Exception as e:
app_logger.error(f"Fehler beim Laden der Admin-Einstellungen: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Laden der Einstellungen: {str(e)}"
}), 500
@app.route('/api/admin/settings', methods=['POST'])
@admin_required
def update_admin_settings():
"""Aktualisiert die Admin-Einstellungen"""
try:
data = request.get_json()
if not data:
return jsonify({
"success": False,
"message": "Keine Daten empfangen"
}), 400
# Hier würden normalerweise die Einstellungen in einer Konfigurationsdatei gespeichert
# Für diese Demo loggen wir nur die Änderungen
app_logger.info(f"Admin-Einstellungen aktualisiert: {data}")
return jsonify({
"success": True,
"message": "Einstellungen erfolgreich aktualisiert"
})
except Exception as e:
app_logger.error(f"Fehler beim Aktualisieren der Admin-Einstellungen: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Aktualisieren: {str(e)}"
}), 500
@app.route('/api/admin/logs/export', methods=['GET'])
@admin_required
def export_admin_logs():
"""Exportiert System-Logs"""
try:
import os
import zipfile
import tempfile
from datetime import datetime
# Temporäre ZIP-Datei erstellen
temp_dir = tempfile.mkdtemp()
zip_filename = f"myp_logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
zip_path = os.path.join(temp_dir, zip_filename)
log_dir = os.path.join(os.path.dirname(__file__), 'logs')
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(log_dir):
for file in files:
if file.endswith('.log'):
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, log_dir)
zipf.write(file_path, arcname)
app_logger.info("System-Logs exportiert")
return send_file(zip_path, as_attachment=True, download_name=zip_filename)
except Exception as e:
app_logger.error(f"Fehler beim Exportieren der Logs: {str(e)}")
return jsonify({
"success": False,
"message": f"Fehler beim Exportieren: {str(e)}"
}), 500
# ===== ENDE FEHLENDE ADMIN-API-ENDPUNKTE =====
# ===== STARTUP UND MAIN =====
if __name__ == "__main__":