Read the duration from the MP4/MOV movie header (moov/mvhd) on both the
dispatcher media endpoint and the vendor portal completion upload, so a
direct request cannot bypass the browser check. Unreadable metadata still
never blocks an upload.
The 10-photo / 3-video limit only counted dispatcher attachments, so vendor
portal uploads could push a work order past it (and vice versa). Count the
work order's current vendor documents alongside its attachments on both the
dispatcher media endpoint and the vendor portal. A new completion version
does not count the version it replaces.
A misleading file name could move a file into a larger size class: a real
PDF or JPEG named .mp4/.mov got the 100 MB video cap on the vendor portal,
and a .jpg declared with a foreign video type got it on the media endpoint.
Classify by the same resolved type the signature check validates; the
extension only decides when no allowlisted type is known.
The Elastic Beanstalk nginx proxy kept its 1 MB default body limit, so every
media upload over ~1 MB got an nginx 413 before reaching the API. Ship a
.platform nginx override (120M) in the bundle and assert it in the bundle
contract.
Apply the client-confirmed contract: photos up to 10 MB (JPEG/PNG/HEIC),
videos up to 100 MB (MP4/MOV), at most 10 photos and 3 videos per work order,
with stable generic rejection messages. The request ceiling (110 MB) sits
between the per-kind caps and the proxy so oversize files get the generic
message. The vendor portal accepts the same photo/video types and caps.