📚 Improved backend structure & logs management 🎉
This commit is contained in:
336
backend/app.py
336
backend/app.py
@@ -423,6 +423,342 @@ def stats_page():
|
||||
"""Zeigt die Statistiken-Seite an"""
|
||||
return render_template("stats.html", title="Statistiken")
|
||||
|
||||
# ===== API-ENDPUNKTE FÜR FRONTEND-KOMPATIBILITÄT =====
|
||||
|
||||
@app.route("/api/jobs", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_jobs():
|
||||
"""API-Endpunkt für Jobs - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import get_jobs
|
||||
return get_jobs()
|
||||
|
||||
@app.route("/api/jobs", methods=["POST"])
|
||||
@login_required
|
||||
def api_create_job():
|
||||
"""API-Endpunkt für Job-Erstellung - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import create_job
|
||||
return create_job()
|
||||
|
||||
@app.route("/api/jobs/<int:job_id>", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_job(job_id):
|
||||
"""API-Endpunkt für einzelnen Job - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import get_job
|
||||
return get_job(job_id)
|
||||
|
||||
@app.route("/api/jobs/<int:job_id>", methods=["PUT"])
|
||||
@login_required
|
||||
def api_update_job(job_id):
|
||||
"""API-Endpunkt für Job-Update - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import update_job
|
||||
return update_job(job_id)
|
||||
|
||||
@app.route("/api/jobs/<int:job_id>", methods=["DELETE"])
|
||||
@login_required
|
||||
def api_delete_job(job_id):
|
||||
"""API-Endpunkt für Job-Löschung - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import delete_job
|
||||
return delete_job(job_id)
|
||||
|
||||
@app.route("/api/jobs/active", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_active_jobs():
|
||||
"""API-Endpunkt für aktive Jobs - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import get_active_jobs
|
||||
return get_active_jobs()
|
||||
|
||||
@app.route("/api/jobs/current", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_current_job():
|
||||
"""API-Endpunkt für aktuellen Job - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import get_current_job
|
||||
return get_current_job()
|
||||
|
||||
@app.route("/api/jobs/<int:job_id>/start", methods=["POST"])
|
||||
@login_required
|
||||
def api_start_job(job_id):
|
||||
"""API-Endpunkt für Job-Start - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import start_job
|
||||
return start_job(job_id)
|
||||
|
||||
@app.route("/api/jobs/<int:job_id>/pause", methods=["POST"])
|
||||
@login_required
|
||||
def api_pause_job(job_id):
|
||||
"""API-Endpunkt für Job-Pause - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import pause_job
|
||||
return pause_job(job_id)
|
||||
|
||||
@app.route("/api/jobs/<int:job_id>/resume", methods=["POST"])
|
||||
@login_required
|
||||
def api_resume_job(job_id):
|
||||
"""API-Endpunkt für Job-Resume - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import resume_job
|
||||
return resume_job(job_id)
|
||||
|
||||
@app.route("/api/jobs/<int:job_id>/finish", methods=["POST"])
|
||||
@login_required
|
||||
def api_finish_job(job_id):
|
||||
"""API-Endpunkt für Job-Finish - leitet an Jobs-Blueprint weiter"""
|
||||
from blueprints.jobs import finish_job
|
||||
return finish_job(job_id)
|
||||
|
||||
@app.route("/api/printers", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_printers():
|
||||
"""API-Endpunkt für Drucker-Liste"""
|
||||
try:
|
||||
from models import get_db_session, Printer
|
||||
|
||||
db_session = get_db_session()
|
||||
printers = db_session.query(Printer).filter(Printer.active == True).all()
|
||||
|
||||
printer_list = []
|
||||
for printer in printers:
|
||||
printer_dict = {
|
||||
"id": printer.id,
|
||||
"name": printer.name,
|
||||
"model": printer.model,
|
||||
"location": printer.location,
|
||||
"status": printer.status,
|
||||
"ip_address": printer.ip_address,
|
||||
"plug_ip": printer.plug_ip,
|
||||
"active": printer.active,
|
||||
"last_checked": printer.last_checked.isoformat() if printer.last_checked else None
|
||||
}
|
||||
printer_list.append(printer_dict)
|
||||
|
||||
db_session.close()
|
||||
|
||||
app_logger.info(f"✅ API: {len(printer_list)} Drucker abgerufen")
|
||||
return jsonify({"printers": printer_list})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ API-Fehler beim Abrufen der Drucker: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Laden der Drucker", "details": str(e)}), 500
|
||||
|
||||
@app.route("/api/printers/status", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_printer_status():
|
||||
"""API-Endpunkt für Drucker-Status"""
|
||||
try:
|
||||
from models import get_db_session, Printer
|
||||
from utils.tapo_controller import tapo_controller
|
||||
|
||||
db_session = get_db_session()
|
||||
printers = db_session.query(Printer).filter(Printer.active == True).all()
|
||||
|
||||
status_list = []
|
||||
for printer in printers:
|
||||
# Tapo-Steckdosen-Status prüfen
|
||||
if printer.plug_ip:
|
||||
try:
|
||||
reachable, plug_status = tapo_controller.check_outlet_status(
|
||||
printer.plug_ip,
|
||||
printer_id=printer.id
|
||||
)
|
||||
|
||||
status_dict = {
|
||||
"id": printer.id,
|
||||
"name": printer.name,
|
||||
"status": printer.status,
|
||||
"plug_status": plug_status,
|
||||
"plug_reachable": reachable,
|
||||
"plug_ip": printer.plug_ip,
|
||||
"location": printer.location
|
||||
}
|
||||
except Exception as e:
|
||||
app_logger.warning(f"⚠️ Fehler bei Steckdosen-Status für {printer.name}: {str(e)}")
|
||||
status_dict = {
|
||||
"id": printer.id,
|
||||
"name": printer.name,
|
||||
"status": "error",
|
||||
"plug_status": "unknown",
|
||||
"plug_reachable": False,
|
||||
"plug_ip": printer.plug_ip,
|
||||
"location": printer.location,
|
||||
"error": str(e)
|
||||
}
|
||||
else:
|
||||
status_dict = {
|
||||
"id": printer.id,
|
||||
"name": printer.name,
|
||||
"status": printer.status,
|
||||
"plug_status": "no_plug",
|
||||
"plug_reachable": False,
|
||||
"plug_ip": None,
|
||||
"location": printer.location
|
||||
}
|
||||
|
||||
status_list.append(status_dict)
|
||||
|
||||
db_session.close()
|
||||
|
||||
app_logger.info(f"✅ API: Status für {len(status_list)} Drucker abgerufen")
|
||||
return jsonify({"printers": status_list})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ API-Fehler beim Abrufen des Drucker-Status: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Laden des Drucker-Status", "details": str(e)}), 500
|
||||
|
||||
# ===== SESSION-API-ENDPUNKTE =====
|
||||
|
||||
@app.route("/api/session/status", methods=["GET"])
|
||||
@login_required
|
||||
def api_session_status():
|
||||
"""API-Endpunkt für Session-Status"""
|
||||
try:
|
||||
last_activity = session.get('last_activity')
|
||||
if last_activity:
|
||||
last_activity_time = datetime.fromisoformat(last_activity)
|
||||
time_since_activity = (datetime.now() - last_activity_time).total_seconds()
|
||||
time_left_seconds = max(0, SESSION_LIFETIME.total_seconds() - time_since_activity)
|
||||
else:
|
||||
time_left_seconds = SESSION_LIFETIME.total_seconds()
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"user": {
|
||||
"id": current_user.id,
|
||||
"email": current_user.email,
|
||||
"name": current_user.name,
|
||||
"is_admin": current_user.is_admin
|
||||
},
|
||||
"session": {
|
||||
"time_left_seconds": int(time_left_seconds),
|
||||
"max_inactive_minutes": int(SESSION_LIFETIME.total_seconds() / 60),
|
||||
"last_activity": last_activity or datetime.now().isoformat()
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ Session-Status-Fehler: {str(e)}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route("/api/session/heartbeat", methods=["POST"])
|
||||
@login_required
|
||||
def api_session_heartbeat():
|
||||
"""API-Endpunkt für Session-Heartbeat"""
|
||||
try:
|
||||
# Session-Aktivität aktualisieren
|
||||
session['last_activity'] = datetime.now().isoformat()
|
||||
session.permanent = True
|
||||
|
||||
# Verbleibende Zeit berechnen
|
||||
time_left_seconds = SESSION_LIFETIME.total_seconds()
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"time_left_seconds": int(time_left_seconds),
|
||||
"timestamp": datetime.now().isoformat()
|
||||
})
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ Session-Heartbeat-Fehler: {str(e)}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route("/api/session/extend", methods=["POST"])
|
||||
@login_required
|
||||
def api_session_extend():
|
||||
"""API-Endpunkt für Session-Verlängerung"""
|
||||
try:
|
||||
data = request.get_json() or {}
|
||||
extend_minutes = data.get('extend_minutes', 30)
|
||||
|
||||
# Session verlängern
|
||||
session['last_activity'] = datetime.now().isoformat()
|
||||
session.permanent = True
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"extended_minutes": extend_minutes,
|
||||
"new_expiry": (datetime.now() + SESSION_LIFETIME).isoformat()
|
||||
})
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ Session-Extend-Fehler: {str(e)}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@app.route("/api/jobs/recent", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_recent_jobs():
|
||||
"""API-Endpunkt für kürzlich erstellte Jobs"""
|
||||
try:
|
||||
from models import get_db_session, Job
|
||||
|
||||
db_session = get_db_session()
|
||||
|
||||
# Letzte 10 Jobs des Benutzers (oder alle für Admin)
|
||||
query = db_session.query(Job).order_by(Job.created_at.desc())
|
||||
|
||||
if not current_user.is_admin:
|
||||
query = query.filter(Job.user_id == current_user.id)
|
||||
|
||||
recent_jobs = query.limit(10).all()
|
||||
|
||||
job_list = []
|
||||
for job in recent_jobs:
|
||||
job_dict = {
|
||||
"id": job.id,
|
||||
"name": job.name,
|
||||
"status": job.status,
|
||||
"created_at": job.created_at.isoformat() if job.created_at else None,
|
||||
"start_at": job.start_at.isoformat() if job.start_at else None,
|
||||
"duration_minutes": job.duration_minutes,
|
||||
"printer_name": job.printer.name if job.printer else "Unbekannt"
|
||||
}
|
||||
job_list.append(job_dict)
|
||||
|
||||
db_session.close()
|
||||
|
||||
app_logger.info(f"✅ API: {len(job_list)} kürzliche Jobs abgerufen")
|
||||
return jsonify({"jobs": job_list})
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ API-Fehler beim Abrufen kürzlicher Jobs: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Laden kürzlicher Jobs", "details": str(e)}), 500
|
||||
|
||||
@app.route("/api/stats", methods=["GET"])
|
||||
@login_required
|
||||
def api_get_stats():
|
||||
"""API-Endpunkt für System-Statistiken"""
|
||||
try:
|
||||
from models import get_db_session, Job, Printer
|
||||
|
||||
db_session = get_db_session()
|
||||
|
||||
# Grundlegende Statistiken
|
||||
total_jobs = db_session.query(Job).count()
|
||||
active_jobs = db_session.query(Job).filter(Job.status.in_(["scheduled", "running"])).count()
|
||||
completed_jobs = db_session.query(Job).filter(Job.status == "finished").count()
|
||||
total_printers = db_session.query(Printer).filter(Printer.active == True).count()
|
||||
|
||||
# Benutzer-spezifische Statistiken
|
||||
if not current_user.is_admin:
|
||||
user_jobs = db_session.query(Job).filter(Job.user_id == current_user.id).count()
|
||||
user_active_jobs = db_session.query(Job).filter(
|
||||
Job.user_id == current_user.id,
|
||||
Job.status.in_(["scheduled", "running"])
|
||||
).count()
|
||||
else:
|
||||
user_jobs = total_jobs
|
||||
user_active_jobs = active_jobs
|
||||
|
||||
db_session.close()
|
||||
|
||||
stats = {
|
||||
"total_jobs": total_jobs,
|
||||
"active_jobs": active_jobs,
|
||||
"completed_jobs": completed_jobs,
|
||||
"total_printers": total_printers,
|
||||
"user_jobs": user_jobs,
|
||||
"user_active_jobs": user_active_jobs,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
app_logger.info(f"✅ API: Statistiken abgerufen")
|
||||
return jsonify(stats)
|
||||
|
||||
except Exception as e:
|
||||
app_logger.error(f"❌ API-Fehler beim Abrufen der Statistiken: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Laden der Statistiken", "details": str(e)}), 500
|
||||
|
||||
# Statische Seiten
|
||||
@app.route("/privacy")
|
||||
def privacy():
|
||||
|
Reference in New Issue
Block a user