fix(api): parse form-urlencoded POST body in fleet-dashboard handler
Some checks are pending
Static Analysis / static (push) Waiting to run
Tests / test (push) Waiting to run

The Fleet Trips SPA posts application/x-www-form-urlencoded, but the
POST /webhook/fleet-dashboard handler read the body with request.json().
That threw on every request, the except swallowed it to body={}, and all
filters (vehicle_numbers, cost_centre, assigned_city) plus period/dates
were dropped — so every query returned the full unfiltered fleet (1,266
trips) regardless of the dropdowns. The map/KPIs/trips never changed,
which read as "the dropdowns don't work."

Parse by Content-Type: urllib.parse.parse_qs for form bodies (no new
dependency — avoids python-multipart), JSON still accepted defensively
for n8n-compat callers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
david kiania 2026-06-05 13:23:10 +03:00
parent 26fa1a4dc5
commit f1387d1476

View file

@ -31,9 +31,11 @@ is the base URL (the `N8N_BASE` constant in each dashboard SPA):
from __future__ import annotations
import json
import os
from contextlib import asynccontextmanager
from datetime import date, datetime, timedelta, timezone
from urllib.parse import parse_qs
import psycopg2.extras
from fastapi import FastAPI, Request
@ -202,13 +204,23 @@ def _preset_to_range(period: str | None, start_date, end_date):
@app.post("/webhook/fleet-dashboard")
async def fleet_trips(request: Request):
# The dashboard SPA posts application/x-www-form-urlencoded (not JSON), so
# parse by content-type. Reading the raw body + parse_qs avoids pulling in
# python-multipart. JSON is still accepted defensively (n8n-compat callers).
body: dict = {}
ctype = request.headers.get("content-type", "").lower()
try:
body = await request.json()
raw = await request.body()
if "application/json" in ctype:
parsed = json.loads(raw or b"{}")
body = parsed if isinstance(parsed, dict) else {}
else:
# x-www-form-urlencoded — parse_qs yields lists; keep the last value.
body = {k: v[-1] for k, v in parse_qs(raw.decode("utf-8", "replace")).items()}
except Exception:
body = {}
if not isinstance(body, dict):
body = {}
body = body.get("body", body) if isinstance(body.get("body"), dict) else body
if isinstance(body.get("body"), dict):
body = body["body"]
start, end = _preset_to_range(
body.get("period"), body.get("start_date"), body.get("end_date")