from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect from typing import List from models import CreateOrderRequest, Order, DrinkType, MateType from database import db from websocket_manager import websocket_manager app = FastAPI( title="Tschunk Order API", description="Eine RESTful API für Tschunk-Bestellungen mit WebSocket-Unterstützung", version="1.0.0" ) @app.get("/") async def root(): """Root endpoint with API information.""" return { "message": "Willkommen zur Tschunk Order API!", "version": "1.0.0", "endpoints": { "POST /orders": "Neue Bestellung erstellen", "GET /orders": "Alle Bestellungen abrufen", "DELETE /orders/{order_id}": "Bestellung löschen", "WS /ws": "WebSocket für Echtzeit-Updates" }, "available_drinks": [drink.value for drink in DrinkType], "available_mate_types": [mate.value for mate in MateType] } @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): """ WebSocket-Endpunkt für Echtzeit-Updates der Bestellungen. Clients erhalten automatisch Updates bei: - Neuen Bestellungen - Gelöschten Bestellungen Nachrichten-Formate: - order_created: {"type": "order_created", "order": {...}} - order_deleted: {"type": "order_deleted", "order_id": "..."} - all_orders: {"type": "all_orders", "orders": [...]} """ await websocket_manager.connect(websocket) try: # Sende alle aktuellen Bestellungen beim Verbinden all_orders = [order.model_dump() for order in db.get_all_orders()] await websocket_manager.broadcast_all_orders(all_orders) # Halte die Verbindung aufrecht und warte auf Nachrichten while True: # Warte auf Nachrichten vom Client (kann für Pings/Pongs verwendet werden) data = await websocket.receive_text() # Optional: Echo für Pings if data == "ping": await websocket.send_text("pong") except WebSocketDisconnect: websocket_manager.disconnect(websocket) except Exception as e: print(f"WebSocket-Fehler: {e}") websocket_manager.disconnect(websocket) @app.post("/orders", response_model=Order, status_code=201) async def create_order(order_request: CreateOrderRequest): """ Erstellt eine neue Bestellung. - **drinks**: Liste der Getränke mit Typ und Mate-Sorte Alle verbundenen WebSocket-Clients erhalten automatisch ein Update. """ if not order_request.drinks: raise HTTPException(status_code=400, detail="Mindestens ein Getränk muss bestellt werden") order = await db.create_order(order_request.drinks) return order @app.get("/orders", response_model=List[Order]) async def get_all_orders(): """ Gibt alle Bestellungen zurück. """ orders = db.get_all_orders() return orders @app.delete("/orders/{order_id}") async def delete_order(order_id: str): """ Löscht eine spezifische Bestellung. - **order_id**: ID der zu löschenden Bestellung Alle verbundenen WebSocket-Clients erhalten automatisch ein Update. """ success = await db.delete_order(order_id) if not success: raise HTTPException(status_code=404, detail="Bestellung nicht gefunden") return {"message": f"Bestellung {order_id} wurde erfolgreich gelöscht"} @app.get("/drinks") async def get_available_drinks(): """ Gibt alle verfügbaren Getränketypen zurück. """ return { "drink_types": [drink.value for drink in DrinkType], "mate_types": [mate.value for mate in MateType] } if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)