diff --git a/backend/routes/forum.py b/backend/routes/forum.py index 33eb726..58d03b4 100644 --- a/backend/routes/forum.py +++ b/backend/routes/forum.py @@ -166,8 +166,22 @@ async def list_threads( # ------------------------------------------------------------------ # POST /api/forum/threads # ------------------------------------------------------------------ -def _check_post_limits(user_id: int, conn, text: str, user_created_at: str | None = None, is_thread: bool = False): - """Cooldown, Stunden-Limit und Duplikat-Check für Forum-Posts.""" +def _check_post_limits(user_id: int, conn, text: str, user_created_at: str | None = None, + is_thread: bool = False, now_client: str | None = None): + """Cooldown, Stunden-Limit und Duplikat-Check für Forum-Posts. + + WICHTIG: created_at wird als Client-Lokalzeit gespeichert (safe_client_time). + Alle Zeit-Checks müssen daher gegen die gleiche Zeitbasis rechnen — sonst + sorgt der UTC/Lokalzeit-Versatz (z.B. CEST = UTC+2) dafür, dass der Cooldown + dauerhaft greift (diff wird negativ → immer < 30). Referenz ist die + Client-Zeit dieses Requests (now_client), Fallback UTC. + """ + from datetime import datetime as _dt, timedelta as _td + try: + now_dt = _dt.fromisoformat(now_client) if now_client else _dt.utcnow() + except (ValueError, TypeError): + now_dt = _dt.utcnow() + # 30-Sekunden-Cooldown zwischen beliebigen Posts last = conn.execute( """SELECT MAX(created_at) AS last FROM ( @@ -179,25 +193,25 @@ def _check_post_limits(user_id: int, conn, text: str, user_created_at: str | Non ).fetchone()["last"] if last: try: - from datetime import datetime as _dt - diff = (_dt.utcnow() - _dt.fromisoformat(last)).total_seconds() - if diff < 30: + diff = (now_dt - _dt.fromisoformat(last)).total_seconds() + if 0 <= diff < 30: raise HTTPException(429, "Bitte warte einen Moment bevor du erneut postest.") except (ValueError, TypeError): pass - # Stunden-Limit + # Stunden-Limit (gleiche Zeitbasis wie created_at) + hour_ago = (now_dt - _td(hours=1)).strftime("%Y-%m-%d %H:%M:%S") if is_thread: count = conn.execute( - "SELECT COUNT(*) FROM forum_threads WHERE user_id=? AND created_at > datetime('now','-1 hour')", - (user_id,), + "SELECT COUNT(*) FROM forum_threads WHERE user_id=? AND created_at > ?", + (user_id, hour_ago), ).fetchone()[0] if count >= 5: raise HTTPException(429, "Du hast in dieser Stunde bereits 5 Threads erstellt. Bitte warte etwas.") else: count = conn.execute( - "SELECT COUNT(*) FROM forum_posts WHERE user_id=? AND created_at > datetime('now','-1 hour')", - (user_id,), + "SELECT COUNT(*) FROM forum_posts WHERE user_id=? AND created_at > ?", + (user_id, hour_ago), ).fetchone()[0] if count >= 20: raise HTTPException(429, "Du hast in dieser Stunde bereits 20 Antworten geschrieben. Bitte warte etwas.") @@ -223,8 +237,8 @@ async def create_thread(data: ThreadCreate, user=Depends(get_current_user)): if data.kategorie not in KATEGORIEN: raise HTTPException(400, "Ungültige Kategorie.") with db() as conn: - _check_post_limits(user["id"], conn, data.text.strip(), user.get("created_at"), is_thread=True) ct = safe_client_time(data.client_time) + _check_post_limits(user["id"], conn, data.text.strip(), user.get("created_at"), is_thread=True, now_client=ct) cur = conn.execute( """INSERT INTO forum_threads (user_id, kategorie, titel, text, thread_lat, thread_lon, thread_ort, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)""", @@ -370,9 +384,9 @@ async def create_post(thread_id: int, data: PostCreate, user=Depends(get_current if thread['is_deleted']: raise HTTPException(404, "Thread nicht gefunden.") - _check_post_limits(user["id"], conn, data.text.strip(), user.get("created_at"), is_thread=False) - ct = safe_client_time(data.client_time) + _check_post_limits(user["id"], conn, data.text.strip(), user.get("created_at"), is_thread=False, now_client=ct) + cur = conn.execute( "INSERT INTO forum_posts (thread_id, user_id, text, created_at) VALUES (?, ?, ?, ?)", (thread_id, user['id'], data.text.strip(), ct)