From 18707a50d2c7baf43facde0883c9074a892c6488 Mon Sep 17 00:00:00 2001 From: jay Date: Mon, 8 Jun 2026 13:45:14 -0400 Subject: [PATCH] Feedback admin: 404 on missing id for read/delete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per audit: read-toggle and delete returned {"ok":true} even for a nonexistent id. Return 404 when no row is affected, so the optimistic UI can distinguish a stale/already-deleted row from a real success. (The postJSON/delJSON imports flagged in the audit were already present — verified in source + built bundle.) Co-Authored-By: Claude Opus 4.8 (1M context) --- goodnews/api.py | 12 +++++++----- tests/test_feedback.py | 6 ++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/goodnews/api.py b/goodnews/api.py index c50eb14..4bf8d30 100644 --- a/goodnews/api.py +++ b/goodnews/api.py @@ -796,19 +796,21 @@ def create_app() -> FastAPI: def admin_feedback_read(fid: int, body: FeedbackReadBody, request: Request) -> dict: with get_conn() as conn: _require_admin(conn, request) - if body.read: - conn.execute("UPDATE feedback SET read_at = CURRENT_TIMESTAMP WHERE id = ?", (fid,)) - else: - conn.execute("UPDATE feedback SET read_at = NULL WHERE id = ?", (fid,)) + ts = "CURRENT_TIMESTAMP" if body.read else "NULL" + cur = conn.execute(f"UPDATE feedback SET read_at = {ts} WHERE id = ?", (fid,)) conn.commit() + if cur.rowcount == 0: + raise HTTPException(status_code=404, detail="feedback not found") return {"ok": True, "read": body.read} @app.delete("/api/admin/feedback/{fid}") def admin_feedback_delete(fid: int, request: Request) -> dict: with get_conn() as conn: _require_admin(conn, request) - conn.execute("DELETE FROM feedback WHERE id = ?", (fid,)) + cur = conn.execute("DELETE FROM feedback WHERE id = ?", (fid,)) conn.commit() + if cur.rowcount == 0: + raise HTTPException(status_code=404, detail="feedback not found") return {"ok": True} @app.get("/api/admin/stats") diff --git a/tests/test_feedback.py b/tests/test_feedback.py index 57c8ce6..e2c288d 100644 --- a/tests/test_feedback.py +++ b/tests/test_feedback.py @@ -88,3 +88,9 @@ def test_feedback_admin_actions_gated(tmp_path, monkeypatch): anon = TestClient(app) assert anon.post("/api/admin/feedback/1/read", json={"read": True}).status_code == 401 assert anon.delete("/api/admin/feedback/1").status_code == 401 + + +def test_feedback_actions_404_on_missing_id(tmp_path, monkeypatch): + tc = _admin_client(tmp_path, monkeypatch) + assert tc.post("/api/admin/feedback/9999/read", json={"read": True}).status_code == 404 + assert tc.delete("/api/admin/feedback/9999").status_code == 404