"""SuS-Profil-Routen: Login, Cockpit (Skelett), Logout. Etappe 1: Login-Flow mit bcrypt + persistenter Lockout (E-020 V7). Etappe 2: Cockpit (progressiv), Check-In, Paarung. Etappe 6: Projekt-Workflow. Etappe 7: Druckauftraege + Slots. """ from flask import Blueprint, render_template, request, redirect, url_for, session, flash, abort from database import get_db from services.auth import ( pin_pruefen, ist_gesperrt, fehlversuch_loggen, lockout_zuruecksetzen, login_required, ) from tiere import tier_emoji, tier_name bp = Blueprint("profil", __name__) # =========================================================================== # Login-Flow (Etappe 1) # =========================================================================== @bp.route("/login") def login(): """Kachel-Seite mit allen aktiven Profilen (Vorname + Tier-Icon).""" conn = get_db() profile = conn.execute( "SELECT id, vorname, tier FROM profil WHERE aktiv = 1 " "ORDER BY vorname COLLATE NOCASE, tier" ).fetchall() conn.close() return render_template("login.html", profile=profile) @bp.route("/login/", methods=["GET", "POST"]) def login_pin(profil_id): """PIN-Eingabe fuer ein gewaehltes Profil.""" conn = get_db() profil = conn.execute( "SELECT id, vorname, tier, pin_hash, aktiv FROM profil WHERE id = ?", (profil_id,) ).fetchone() conn.close() if not profil or not profil["aktiv"]: flash("Profil nicht gefunden.", "error") return redirect(url_for("profil.login")) # Lockout-Check (persistent in DB, V7) gesperrt, sek_rest = ist_gesperrt(profil_id) if gesperrt: min_rest = sek_rest // 60 + 1 return render_template( "login_pin.html", profil=profil, gesperrt=True, min_rest=min_rest ) if request.method == "POST": eingabe_pin = (request.form.get("pin") or "").strip() if pin_pruefen(eingabe_pin, profil["pin_hash"]): # Erfolg: Lockout zuruecksetzen, Session befuellen lockout_zuruecksetzen(profil_id) session.clear() session["profil_id"] = profil["id"] session["vorname"] = profil["vorname"] session["tier"] = profil["tier"] return redirect(url_for("profil.cockpit")) else: fehlversuch_loggen(profil_id) flash("Falsche PIN. Versuch's nochmal.", "error") return render_template("login_pin.html", profil=profil, gesperrt=False) @bp.route("/logout") def logout(): session.clear() flash("Du bist ausgeloggt.", "success") return redirect(url_for("oeffentlich.index")) # =========================================================================== # Cockpit (Etappe 1: Minimal-Stub, voll in Etappe 2 mit progressivem Layout) # =========================================================================== @bp.route("/cockpit") @login_required def cockpit(): """Minimal-Stub fuer Etappe 1. Etappe 2 macht das progressiv (V1): - Onboarding nicht erledigt: nur Onboarding-Karte + Check-In-Trigger - L1 fertig: zusaetzlich Drucker-Buchung - Hat aktive Projekte: zusaetzlich Projekt-Karten, Druckauftraege, Slots """ return render_template( "cockpit_stub.html", vorname=session.get("vorname"), tier=session.get("tier"), ) # Spaetere Etappen — Stubs damit Navigation nicht bricht @bp.route("/cockpit/check-in", methods=["POST"]) @login_required def check_in(): abort(501) @bp.route("/cockpit/lektion//status", methods=["POST"]) @login_required def lektion_status(nummer): abort(501) @bp.route("/cockpit/slots") @login_required def slots(): abort(501) @bp.route("/cockpit/druckauftraege") @login_required def druckauftraege(): abort(501)