From 9709a78dc6a2dbe34a254c5f5fa2c40983e0731c Mon Sep 17 00:00:00 2001 From: Petru Paler Date: Fri, 2 Jan 2026 09:49:45 +0000 Subject: [PATCH] fix: UserRole.admin typo and defensive JSON parsing in JS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix UserRole.admin to UserRole.ADMIN in events.py delete route - Add content-type check before parsing JSON in event delete handler - Add error handling and content-type check in animal selection hash computation - Audited codebase: no other enum case issues found 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/animaltrack/web/routes/events.py | 2 +- src/animaltrack/web/templates/animal_select.py | 17 ++++++++++++++--- src/animaltrack/web/templates/event_detail.py | 10 ++++++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/animaltrack/web/routes/events.py b/src/animaltrack/web/routes/events.py index c585a63..0693e4a 100644 --- a/src/animaltrack/web/routes/events.py +++ b/src/animaltrack/web/routes/events.py @@ -335,7 +335,7 @@ async def event_delete(request: Request, event_id: str): if not auth: return JSONResponse({"error": "Not authenticated"}, status_code=401) - if auth.role != UserRole.admin: + if auth.role != UserRole.ADMIN: return JSONResponse({"error": "Admin role required"}, status_code=403) # Parse form data diff --git a/src/animaltrack/web/templates/animal_select.py b/src/animaltrack/web/templates/animal_select.py index aeb232c..3b3a582 100644 --- a/src/animaltrack/web/templates/animal_select.py +++ b/src/animaltrack/web/templates/animal_select.py @@ -157,13 +157,24 @@ def selection_script(total_count: int) -> Div: from_location_id: fromLoc }}) }}) - .then(response => response.json()) + .then(response => {{ + if (!response.ok) {{ + console.error('Hash computation failed:', response.status); + return {{}}; + }} + var contentType = response.headers.get('content-type'); + if (contentType && contentType.includes('application/json')) {{ + return response.json(); + }} + return {{}}; + }}) .then(data => {{ var hashField = document.getElementById('roster-hash-field'); - if (hashField) {{ + if (hashField && data.roster_hash) {{ hashField.value = data.roster_hash; }} - }}); + }}) + .catch(err => console.error('Hash computation error:', err)); }} // Initialize hash on load diff --git a/src/animaltrack/web/templates/event_detail.py b/src/animaltrack/web/templates/event_detail.py index 4f4c11f..aa76d8e 100644 --- a/src/animaltrack/web/templates/event_detail.py +++ b/src/animaltrack/web/templates/event_detail.py @@ -409,7 +409,12 @@ def delete_script() -> Script: body: 'reason=Deleted via UI' }); - const data = await response.json(); + // Try to parse JSON, but handle non-JSON responses gracefully + let data = {}; + const contentType = response.headers.get('content-type'); + if (contentType && contentType.includes('application/json')) { + data = await response.json(); + } if (response.ok) { statusEl.innerHTML = 'Event deleted successfully!'; @@ -422,7 +427,8 @@ def delete_script() -> Script: statusEl.innerHTML = '' + data.message + ''; deleteBtn.disabled = false; } else { - statusEl.innerHTML = 'Error: ' + (data.error || 'Unknown error') + ''; + const errorMsg = data.error || 'Server error (' + response.status + ')'; + statusEl.innerHTML = 'Error: ' + errorMsg + ''; deleteBtn.disabled = false; } } catch (err) {