diff --git a/src/contexts/AdminAssetsContext.jsx b/src/contexts/AdminAssetsContext.jsx index b8b3c62..bba5c25 100644 --- a/src/contexts/AdminAssetsContext.jsx +++ b/src/contexts/AdminAssetsContext.jsx @@ -315,20 +315,26 @@ export function AssetsProvider({ children }) { ); // ─── PATCH /api/admin/assets/:assetId ──────────────────────────────────── + // + // A replacement file (video thumbnail, or an image/audio asset's main + // file) goes out the same presigned direct-to-storage path as + // uploadAsset() above — this backend never buffers it. The PATCH body + // itself is plain JSON either way (storage_key when a file was replaced). const updateAsset = useCallback( (assetId, fields, file = null) => request(async () => { - const formData = new FormData(); - Object.entries(fields).forEach(([key, value]) => { - if (value !== undefined && value !== null) formData.append(key, value); - }); - if (file) formData.append("file", file); + let filePayload = {}; + if (file) { + const presigned = await presignAssetUpload(file); + await uploadPresigned(file, presigned); + filePayload = { + storage_key: presigned.key, + original_name: file.name, + mimetype: file.type || undefined, + }; + } - // No explicit Content-Type here — axios/the browser must set it - // itself so the multipart boundary is included. A hardcoded - // "multipart/form-data" header (no boundary) makes multer fail - // to parse the body, silently dropping the file and every field. - const res = await api.patch(`/admin/assets/${assetId}`, formData); + const res = await api.patch(`/admin/assets/${assetId}`, { ...fields, ...filePayload }); const asset = res.data?.data?.data ?? null; if (asset) {