""" Consent API Endpoints für BreakPilot Stellt die Consent-Funktionalität über das BreakPilot Backend bereit """ from fastapi import APIRouter, HTTPException, Header, Query from typing import Optional, List from pydantic import BaseModel from consent_client import consent_client, DocumentType, generate_demo_token, generate_jwt_token router = APIRouter(prefix="/consent", tags=["consent"]) # Request/Response Models class ConsentRequest(BaseModel): document_type: str version_id: str consented: bool = True class CookieConsentItem(BaseModel): category_id: str consented: bool class CookieConsentRequest(BaseModel): categories: List[CookieConsentItem] class DataDeletionRequest(BaseModel): reason: Optional[str] = None # Helper für JWT Token def get_token(authorization: Optional[str], allow_demo: bool = True) -> str: """ Extrahiert JWT Token aus Authorization Header. Falls kein Token vorhanden und allow_demo=True, wird ein Demo-Token generiert. """ if authorization: parts = authorization.split(" ") if len(parts) == 2 and parts[0] == "Bearer": return parts[1] if allow_demo: # Generiere einen Demo-Token für nicht-authentifizierte Benutzer return generate_demo_token() raise HTTPException(status_code=401, detail="Authorization header required") # ========================================== # Token Endpoints # ========================================== @router.get("/token/demo") async def get_demo_token(): """ Generiert einen Demo-Token für nicht-authentifizierte Benutzer. Dieser Token ermöglicht das Lesen von öffentlichen Dokumenten. """ token = generate_demo_token() return { "token": token, "type": "Bearer", "expires_in": 86400 # 24 Stunden } # ========================================== # Public Endpoints # ========================================== @router.get("/check/{document_type}") async def check_consent( document_type: str, language: str = Query("de"), authorization: Optional[str] = Header(None) ): """ Prüft ob der Benutzer einem Dokument zugestimmt hat. Gibt zurück ob Zustimmung vorliegt und ob sie aktualisiert werden muss. """ token = get_token(authorization) try: doc_type = DocumentType(document_type) except ValueError: raise HTTPException(status_code=400, detail=f"Invalid document type: {document_type}") status = await consent_client.check_consent(token, doc_type, language) return { "has_consent": status.has_consent, "current_version_id": status.current_version_id, "consented_version": status.consented_version, "needs_update": status.needs_update, "consented_at": status.consented_at } @router.get("/pending") async def get_pending_consents( language: str = Query("de"), authorization: Optional[str] = Header(None) ): """ Gibt alle Dokumente zurück, die noch Zustimmung benötigen. Nützlich für Anzeige beim Login oder in den Einstellungen. """ token = get_token(authorization) pending = await consent_client.get_pending_consents(token, language) return { "pending_consents": pending, "has_pending": len(pending) > 0 } @router.get("/documents/{document_type}/latest") async def get_latest_document( document_type: str, language: str = Query("de"), authorization: Optional[str] = Header(None) ): """Holt die aktuellste Version eines rechtlichen Dokuments""" token = get_token(authorization) doc = await consent_client.get_latest_document(token, document_type, language) if not doc: raise HTTPException(status_code=404, detail="Document not found") return { "id": doc.id, "document_id": doc.document_id, "version": doc.version, "language": doc.language, "title": doc.title, "content": doc.content, "summary": doc.summary } @router.post("/give") async def give_consent( request: ConsentRequest, authorization: Optional[str] = Header(None) ): """Speichert die Zustimmung des Benutzers zu einem Dokument""" token = get_token(authorization) success = await consent_client.give_consent( token, request.document_type, request.version_id, request.consented ) if not success: raise HTTPException(status_code=500, detail="Failed to save consent") return {"success": True, "message": "Consent saved successfully"} # ========================================== # Cookie Consent Endpoints # ========================================== @router.get("/cookies/categories") async def get_cookie_categories( language: str = Query("de"), authorization: Optional[str] = Header(None) ): """Holt alle Cookie-Kategorien für das Cookie-Banner""" token = get_token(authorization) categories = await consent_client.get_cookie_categories(token, language) return {"categories": categories} @router.post("/cookies") async def set_cookie_consent( request: CookieConsentRequest, authorization: Optional[str] = Header(None) ): """Speichert die Cookie-Präferenzen des Benutzers""" token = get_token(authorization) categories = [ {"category_id": cat.category_id, "consented": cat.consented} for cat in request.categories ] success = await consent_client.set_cookie_consent(token, categories) if not success: raise HTTPException(status_code=500, detail="Failed to save cookie preferences") return {"success": True, "message": "Cookie preferences saved"} # ========================================== # GDPR / Privacy Endpoints # ========================================== @router.get("/privacy/my-data") async def get_my_data(authorization: Optional[str] = Header(None)): """ GDPR Art. 15: Auskunftsrecht Gibt alle über den Benutzer gespeicherten Daten zurück. """ token = get_token(authorization) data = await consent_client.get_my_data(token) if not data: raise HTTPException(status_code=500, detail="Failed to retrieve data") return data @router.post("/privacy/export") async def request_data_export(authorization: Optional[str] = Header(None)): """ GDPR Art. 20: Recht auf Datenübertragbarkeit Fordert einen Export aller Benutzerdaten an. """ token = get_token(authorization) request_id = await consent_client.request_data_export(token) if not request_id: raise HTTPException(status_code=500, detail="Failed to create export request") return { "success": True, "request_id": request_id, "message": "Export request created. You will be notified when ready." } @router.post("/privacy/delete") async def request_data_deletion( request: DataDeletionRequest, authorization: Optional[str] = Header(None) ): """ GDPR Art. 17: Recht auf Löschung Fordert die Löschung aller Benutzerdaten an. """ token = get_token(authorization) request_id = await consent_client.request_data_deletion(token, request.reason) if not request_id: raise HTTPException(status_code=500, detail="Failed to create deletion request") return { "success": True, "request_id": request_id, "message": "Deletion request created. We will process it within 30 days." } # ========================================== # Health Check # ========================================== @router.get("/health") async def consent_health(): """Prüft die Verbindung zum Consent Service""" is_healthy = await consent_client.health_check() return { "consent_service": "healthy" if is_healthy else "unavailable", "connected": is_healthy }