feat: Tutorial-Fortschritt serverseitig persistieren (Resume/Restart)
- Neuer Router /api/tutorial mit GET/PUT/DELETE für Fortschritt pro User - DB-Migration: tutorial_step + tutorial_completed in users-Tabelle - Resume-Dialog bei abgebrochenem Tutorial (Fortsetzen/Neu starten) - Chat-Hinweis passt sich dem Tutorial-Status dynamisch an - API-Methoden: getTutorialState, saveTutorialState, resetTutorialState Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
77
src/routers/tutorial.py
Normale Datei
77
src/routers/tutorial.py
Normale Datei
@@ -0,0 +1,77 @@
|
||||
"""Tutorial-Router: Fortschritt serverseitig pro User speichern."""
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends
|
||||
from auth import get_current_user
|
||||
from database import db_dependency
|
||||
import aiosqlite
|
||||
|
||||
logger = logging.getLogger("osint.tutorial")
|
||||
|
||||
router = APIRouter(prefix="/api/tutorial", tags=["tutorial"])
|
||||
|
||||
|
||||
@router.get("/state")
|
||||
async def get_tutorial_state(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
db: aiosqlite.Connection = Depends(db_dependency),
|
||||
):
|
||||
"""Tutorial-Fortschritt des aktuellen Nutzers abrufen."""
|
||||
cursor = await db.execute(
|
||||
"SELECT tutorial_step, tutorial_completed FROM users WHERE id = ?",
|
||||
(current_user["id"],),
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
if not row:
|
||||
return {"current_step": None, "completed": False}
|
||||
return {
|
||||
"current_step": row["tutorial_step"],
|
||||
"completed": bool(row["tutorial_completed"]),
|
||||
}
|
||||
|
||||
|
||||
@router.put("/state")
|
||||
async def save_tutorial_state(
|
||||
body: dict,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
db: aiosqlite.Connection = Depends(db_dependency),
|
||||
):
|
||||
"""Tutorial-Fortschritt speichern (current_step und/oder completed)."""
|
||||
updates = []
|
||||
params = []
|
||||
|
||||
if "current_step" in body:
|
||||
step = body["current_step"]
|
||||
if step is not None and (not isinstance(step, int) or step < 0 or step > 31):
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=422, detail="current_step muss 0-31 oder null sein")
|
||||
updates.append("tutorial_step = ?")
|
||||
params.append(step)
|
||||
|
||||
if "completed" in body:
|
||||
updates.append("tutorial_completed = ?")
|
||||
params.append(1 if body["completed"] else 0)
|
||||
|
||||
if not updates:
|
||||
return {"ok": True}
|
||||
|
||||
params.append(current_user["id"])
|
||||
await db.execute(
|
||||
f"UPDATE users SET {', '.join(updates)} WHERE id = ?",
|
||||
params,
|
||||
)
|
||||
await db.commit()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.delete("/state")
|
||||
async def reset_tutorial_state(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
db: aiosqlite.Connection = Depends(db_dependency),
|
||||
):
|
||||
"""Tutorial-Fortschritt zuruecksetzen (fuer Neustart)."""
|
||||
await db.execute(
|
||||
"UPDATE users SET tutorial_step = NULL, tutorial_completed = 0 WHERE id = ?",
|
||||
(current_user["id"],),
|
||||
)
|
||||
await db.commit()
|
||||
return {"ok": True}
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren