Validate watchlist add JSON body shape
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## 0.29.4 - 2026-05-16
|
||||
|
||||
- Fixed `/api/watchlist/add` malformed-body handling so non-object JSON payloads now return `400 Bad Request` instead of surfacing as an internal server error.
|
||||
- Expanded regression coverage for invalid watchlist add payload shapes.
|
||||
|
||||
## 0.29.3 - 2026-05-16
|
||||
|
||||
- Fixed duplicate watchlist add responses so already-tracked shows now return `200 OK` with `created: false` instead of the misleading `201 Created`.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A local web UI for a system-wide `ani-cli` install.
|
||||
|
||||
Current version: `0.29.3`
|
||||
Current version: `0.29.4`
|
||||
|
||||
## Project Layout
|
||||
|
||||
@@ -63,6 +63,7 @@ The app currently includes:
|
||||
- A paged SQLite-backed download queue with retry, cancel, remove, retry-failed, and clear-finished actions.
|
||||
- A paged SQLite-backed watchlist with summary cards, per-show refresh, bulk refresh, source links, and thumbnail tools.
|
||||
- Duplicate watchlist adds now return `200 OK` with `created: false`, keeping the API status aligned with the no-op result.
|
||||
- Malformed watchlist add requests now also return `400 Bad Request` when the JSON body is not an object.
|
||||
- Watchlist category tabs for `Watching`, `Planned`, `Finished`, and `Dropped`, with per-anime category editing.
|
||||
- AnimeSchedule-backed watchlist completion tracking that compares live sub/dub availability with the expected total episode count when known.
|
||||
- Automatic watchlist sync after successful downloads, including downloaded-vs-manual finished markers.
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ from uuid import uuid4
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent
|
||||
ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli"
|
||||
APP_NAME = "ani-cli-web"
|
||||
VERSION = "0.29.3"
|
||||
VERSION = "0.29.4"
|
||||
ALLANIME_BASE = "allanime.day"
|
||||
ALLANIME_API = f"https://api.{ALLANIME_BASE}"
|
||||
ALLANIME_REFERER = "https://allmanga.to"
|
||||
|
||||
+1
-1
@@ -242,7 +242,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.json(actions[action](job_id))
|
||||
elif parsed.path in {"/api/watchlist", "/api/watchlist/add"}:
|
||||
Handler._runtime(self)
|
||||
result = Handler._context(self).add_to_watchlist(self.body_json())
|
||||
result = Handler._context(self).add_to_watchlist(Handler.require_json_object(self, self.body_json()))
|
||||
status = HTTPStatus.CREATED if result.get("created") else HTTPStatus.OK
|
||||
self.json(result, status)
|
||||
elif parsed.path == "/api/watchlist/ensure-thumbnails":
|
||||
|
||||
@@ -1125,6 +1125,13 @@ class HandlerRouteTests(unittest.TestCase):
|
||||
self.assertEqual(handler.json_payload, result)
|
||||
self.assertEqual(handler.json_status, HTTPStatus.OK)
|
||||
|
||||
def test_watchlist_add_rejects_non_object_json_body(self):
|
||||
body = b"[]"
|
||||
handler = DummyHandler("/api/watchlist/add", body=body, content_length=len(body))
|
||||
APP.Handler.do_POST(handler)
|
||||
self.assertEqual(handler.error_status, HTTPStatus.BAD_REQUEST)
|
||||
self.assertIn("json object", handler.error_message.lower())
|
||||
|
||||
def test_ensure_thumbnails_rejects_non_array_show_ids(self):
|
||||
body = b'{"show_ids":"show-1"}'
|
||||
handler = DummyHandler("/api/watchlist/ensure-thumbnails", body=body, content_length=len(body))
|
||||
|
||||
Reference in New Issue
Block a user