Validate config JSON body shape
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## 0.29.5 - 2026-05-16
|
||||
|
||||
- Fixed `/api/config` malformed-body handling so non-object JSON payloads now return `400 Bad Request` instead of silently resetting saved settings to defaults.
|
||||
- Expanded regression coverage for invalid config payload shapes.
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A local web UI for a system-wide `ani-cli` install.
|
||||
|
||||
Current version: `0.29.4`
|
||||
Current version: `0.29.5`
|
||||
|
||||
## Project Layout
|
||||
|
||||
@@ -60,6 +60,7 @@ The app currently includes:
|
||||
- Episode loading for the selected result.
|
||||
- Queueing downloads with editable library name, season, episode range, quality, and target folder.
|
||||
- A dedicated Config page for saved default folder, mode, and quality settings.
|
||||
- Malformed config save requests now return `400 Bad Request` when the JSON body is not an object, instead of resetting values back to defaults.
|
||||
- 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.
|
||||
|
||||
+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.4"
|
||||
VERSION = "0.29.5"
|
||||
ALLANIME_BASE = "allanime.day"
|
||||
ALLANIME_API = f"https://api.{ALLANIME_BASE}"
|
||||
ALLANIME_REFERER = "https://allmanga.to"
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ class Handler(BaseHTTPRequestHandler):
|
||||
try:
|
||||
self.ensure_client_access()
|
||||
if parsed.path == "/api/config":
|
||||
self.update_config(self.body_json())
|
||||
Handler.update_config(self, Handler.require_json_object(self, self.body_json()))
|
||||
elif parsed.path == "/api/queue":
|
||||
runtime = Handler._runtime(self)
|
||||
self.json(runtime["download_queue"].add(self.body_json()), HTTPStatus.CREATED)
|
||||
|
||||
@@ -1103,6 +1103,13 @@ class HandlerRouteTests(unittest.TestCase):
|
||||
APP.Handler.body_json(handler)
|
||||
self.assertIn("utf-8", str(ctx.exception).lower())
|
||||
|
||||
def test_config_post_rejects_non_object_json_body(self):
|
||||
body = b"[]"
|
||||
handler = DummyHandler("/api/config", 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_remove_post_missing_show_id_returns_bad_request(self):
|
||||
handler = DummyHandler("/api/watchlist/remove", body=b"{}", content_length=2)
|
||||
APP.Handler.do_POST(handler)
|
||||
|
||||
Reference in New Issue
Block a user