fix: Error Handling + Context Manager für DB-Verbindungen

- try/finally für Connection/Cleanup in allen Endpunkten
- fetch_events() Helper-Funktion reduziert Code-Duplikation
- Exception Logging bei DB-Fehlern
- HTTPException 500 bei Datenbankfehlern
This commit is contained in:
2026-08-01 16:20:32 +02:00
parent b30c02beb8
commit 3b1801634c
+51 -67
View File
@@ -1,17 +1,22 @@
import logging
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from fastapi import FastAPI, Query from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from pydantic import BaseModel from pydantic import BaseModel
from api.database import get_connection from api.database import get_connection
log = logging.getLogger("calendar-api")
app = FastAPI(title="Calendar Sync API") app = FastAPI(title="Calendar Sync API")
templates = Jinja2Templates(directory=Path(__file__).parent / "templates") templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
SELECT_COLUMNS = "id, summary, description, location, start_at, end_at, all_day, status"
class EventResponse(BaseModel): class EventResponse(BaseModel):
id: int id: int
@@ -43,6 +48,32 @@ def row_to_event(row) -> EventResponse:
) )
def fetch_events(limit: int = 10, search: str | None = None) -> list[dict]:
conn = get_connection()
try:
cur = conn.cursor()
try:
if search:
cur.execute(
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
"WHERE deleted = 0 AND start_at >= NOW() AND summary LIKE %s "
"ORDER BY start_at ASC LIMIT %s",
(f"%{search}%", limit),
)
else:
cur.execute(
f"SELECT {SELECT_COLUMNS} FROM calendar_events "
"WHERE deleted = 0 AND start_at >= NOW() "
"ORDER BY start_at ASC LIMIT %s",
(limit,),
)
return cur.fetchall()
finally:
cur.close()
finally:
conn.close()
@app.get("/api/health") @app.get("/api/health")
def health(): def health():
return {"status": "ok"} return {"status": "ok"}
@@ -53,35 +84,11 @@ def get_events(
limit: int = Query(default=10, ge=1, le=50), limit: int = Query(default=10, ge=1, le=50),
search: str | None = Query(default=None), search: str | None = Query(default=None),
): ):
conn = get_connection() try:
cur = conn.cursor() rows = fetch_events(limit=limit, search=search)
except Exception:
if search: log.exception("DB-Fehler bei /api/events")
cur.execute( raise HTTPException(status_code=500, detail="Database error")
"""
SELECT id, summary, description, location, start_at, end_at, all_day, status
FROM calendar_events
WHERE deleted = 0 AND start_at >= NOW() AND summary LIKE %s
ORDER BY start_at ASC
LIMIT %s
""",
(f"%{search}%", limit),
)
else:
cur.execute(
"""
SELECT id, summary, description, location, start_at, end_at, all_day, status
FROM calendar_events
WHERE deleted = 0 AND start_at >= NOW()
ORDER BY start_at ASC
LIMIT %s
""",
(limit,),
)
rows = cur.fetchall()
cur.close()
conn.close()
events = [row_to_event(row) for row in rows] events = [row_to_event(row) for row in rows]
@@ -95,23 +102,24 @@ def get_events(
@app.get("/api/events/{event_id}", response_model=EventResponse) @app.get("/api/events/{event_id}", response_model=EventResponse)
def get_event(event_id: int): def get_event(event_id: int):
conn = get_connection() conn = get_connection()
try:
cur = conn.cursor() cur = conn.cursor()
try:
cur.execute( cur.execute(
""" f"SELECT {SELECT_COLUMNS} FROM calendar_events "
SELECT id, summary, description, location, start_at, end_at, all_day, status "WHERE id = %s AND deleted = 0",
FROM calendar_events
WHERE id = %s AND deleted = 0
""",
(event_id,), (event_id,),
) )
row = cur.fetchone() row = cur.fetchone()
finally:
cur.close() cur.close()
except Exception:
log.exception("DB-Fehler bei /api/events/%d", event_id)
raise HTTPException(status_code=500, detail="Database error")
finally:
conn.close() conn.close()
if not row: if not row:
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="Event not found") raise HTTPException(status_code=404, detail="Event not found")
return row_to_event(row) return row_to_event(row)
@@ -122,35 +130,11 @@ def index(
search: str | None = Query(default=None), search: str | None = Query(default=None),
limit: int = Query(default=10, ge=1, le=50), limit: int = Query(default=10, ge=1, le=50),
): ):
conn = get_connection() try:
cur = conn.cursor() rows = fetch_events(limit=limit, search=search)
except Exception:
if search: log.exception("DB-Fehler bei /")
cur.execute( raise HTTPException(status_code=500, detail="Database error")
"""
SELECT id, summary, description, location, start_at, end_at, all_day, status
FROM calendar_events
WHERE deleted = 0 AND start_at >= NOW() AND summary LIKE %s
ORDER BY start_at ASC
LIMIT %s
""",
(f"%{search}%", limit),
)
else:
cur.execute(
"""
SELECT id, summary, description, location, start_at, end_at, all_day, status
FROM calendar_events
WHERE deleted = 0 AND start_at >= NOW()
ORDER BY start_at ASC
LIMIT %s
""",
(limit,),
)
rows = cur.fetchall()
cur.close()
conn.close()
events = [] events = []
for row in rows: for row in rows: