@strapi/upload 5.36.0 → 5.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/admin/future/App.js +5 -12
  2. package/dist/admin/future/App.js.map +1 -1
  3. package/dist/admin/future/App.mjs +5 -12
  4. package/dist/admin/future/App.mjs.map +1 -1
  5. package/dist/admin/future/components/UploadProgressDialog.js +494 -0
  6. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -0
  7. package/dist/admin/future/components/UploadProgressDialog.mjs +473 -0
  8. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -0
  9. package/dist/admin/future/pages/Assets/AssetsPage.js +183 -181
  10. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  11. package/dist/admin/future/pages/Assets/AssetsPage.mjs +190 -188
  12. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  13. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +95 -13
  14. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  15. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +97 -15
  16. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  17. package/dist/admin/future/pages/Assets/components/AssetsTable.js +99 -6
  18. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +100 -7
  20. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.js +127 -0
  22. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.js.map +1 -0
  23. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.mjs +105 -0
  24. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.mjs.map +1 -0
  25. package/dist/admin/future/pages/Assets/hooks/useFolderInfo.js +50 -0
  26. package/dist/admin/future/pages/Assets/hooks/useFolderInfo.js.map +1 -0
  27. package/dist/admin/future/pages/Assets/hooks/useFolderInfo.mjs +48 -0
  28. package/dist/admin/future/pages/Assets/hooks/useFolderInfo.mjs.map +1 -0
  29. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +20 -0
  30. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -0
  31. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +18 -0
  32. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -0
  33. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +77 -0
  34. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -0
  35. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +74 -0
  36. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -0
  37. package/dist/admin/future/services/api.js +419 -9
  38. package/dist/admin/future/services/api.js.map +1 -1
  39. package/dist/admin/future/services/api.mjs +417 -9
  40. package/dist/admin/future/services/api.mjs.map +1 -1
  41. package/dist/admin/future/services/assets.js +32 -3
  42. package/dist/admin/future/services/assets.js.map +1 -1
  43. package/dist/admin/future/services/assets.mjs +32 -3
  44. package/dist/admin/future/services/assets.mjs.map +1 -1
  45. package/dist/admin/future/services/folders.js +101 -0
  46. package/dist/admin/future/services/folders.js.map +1 -0
  47. package/dist/admin/future/services/folders.mjs +98 -0
  48. package/dist/admin/future/services/folders.mjs.map +1 -0
  49. package/dist/admin/future/store/hooks.js +10 -0
  50. package/dist/admin/future/store/hooks.js.map +1 -0
  51. package/dist/admin/future/store/hooks.mjs +7 -0
  52. package/dist/admin/future/store/hooks.mjs.map +1 -0
  53. package/dist/admin/future/store/uploadProgress.js +156 -0
  54. package/dist/admin/future/store/uploadProgress.js.map +1 -0
  55. package/dist/admin/future/store/uploadProgress.mjs +143 -0
  56. package/dist/admin/future/store/uploadProgress.mjs.map +1 -0
  57. package/dist/admin/index.js +11 -0
  58. package/dist/admin/index.js.map +1 -1
  59. package/dist/admin/index.mjs +11 -0
  60. package/dist/admin/index.mjs.map +1 -1
  61. package/dist/admin/package.json.js +11 -10
  62. package/dist/admin/package.json.js.map +1 -1
  63. package/dist/admin/package.json.mjs +11 -10
  64. package/dist/admin/package.json.mjs.map +1 -1
  65. package/dist/admin/src/future/components/UploadProgressDialog.d.ts +1 -0
  66. package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +3 -1
  67. package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +3 -1
  68. package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZone.d.ts +10 -0
  69. package/dist/admin/src/future/pages/Assets/hooks/useFolderInfo.d.ts +5 -0
  70. package/dist/admin/src/future/pages/Assets/hooks/useFolderNavigation.d.ts +5 -0
  71. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +17 -0
  72. package/dist/admin/src/future/services/api.d.ts +21 -3
  73. package/dist/admin/src/future/services/folders.d.ts +16 -0
  74. package/dist/admin/src/future/store/hooks.d.ts +6 -0
  75. package/dist/admin/src/future/store/uploadProgress.d.ts +46 -0
  76. package/dist/admin/translations/en.json.js +24 -0
  77. package/dist/admin/translations/en.json.js.map +1 -1
  78. package/dist/admin/translations/en.json.mjs +24 -0
  79. package/dist/admin/translations/en.json.mjs.map +1 -1
  80. package/dist/server/controllers/admin-upload.js +151 -2
  81. package/dist/server/controllers/admin-upload.js.map +1 -1
  82. package/dist/server/controllers/admin-upload.mjs +151 -2
  83. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  84. package/dist/server/controllers/content-api.js +14 -6
  85. package/dist/server/controllers/content-api.js.map +1 -1
  86. package/dist/server/controllers/content-api.mjs +15 -7
  87. package/dist/server/controllers/content-api.mjs.map +1 -1
  88. package/dist/server/routes/admin.js +10 -0
  89. package/dist/server/routes/admin.js.map +1 -1
  90. package/dist/server/routes/admin.mjs +10 -0
  91. package/dist/server/routes/admin.mjs.map +1 -1
  92. package/dist/server/src/controllers/admin-upload.d.ts +12 -0
  93. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  94. package/dist/server/src/controllers/content-api.d.ts.map +1 -1
  95. package/dist/server/src/controllers/index.d.ts +1 -0
  96. package/dist/server/src/controllers/index.d.ts.map +1 -1
  97. package/dist/server/src/index.d.ts +1 -0
  98. package/dist/server/src/index.d.ts.map +1 -1
  99. package/dist/server/src/routes/admin.d.ts.map +1 -1
  100. package/dist/server/src/utils/mime-validation.d.ts +5 -0
  101. package/dist/server/src/utils/mime-validation.d.ts.map +1 -1
  102. package/dist/server/utils/mime-validation.js +7 -4
  103. package/dist/server/utils/mime-validation.js.map +1 -1
  104. package/dist/server/utils/mime-validation.mjs +7 -4
  105. package/dist/server/utils/mime-validation.mjs.map +1 -1
  106. package/dist/shared/contracts/files.d.ts +52 -0
  107. package/dist/shared/contracts/files.d.ts.map +1 -0
  108. package/dist/shared/contracts/folders.d.ts +2 -0
  109. package/package.json +11 -10
  110. package/dist/admin/future/pages/AIGenerationPage.js +0 -24
  111. package/dist/admin/future/pages/AIGenerationPage.js.map +0 -1
  112. package/dist/admin/future/pages/AIGenerationPage.mjs +0 -22
  113. package/dist/admin/future/pages/AIGenerationPage.mjs.map +0 -1
  114. package/dist/admin/future/pages/Assets/components/DropZone/DropZoneWithOverlay.js +0 -33
  115. package/dist/admin/future/pages/Assets/components/DropZone/DropZoneWithOverlay.js.map +0 -1
  116. package/dist/admin/future/pages/Assets/components/DropZone/DropZoneWithOverlay.mjs +0 -31
  117. package/dist/admin/future/pages/Assets/components/DropZone/DropZoneWithOverlay.mjs.map +0 -1
  118. package/dist/admin/src/future/pages/AIGenerationPage.d.ts +0 -1
  119. package/dist/admin/src/future/pages/Assets/components/DropZone/DropZoneWithOverlay.d.ts +0 -4
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sources":["../../../../admin/src/future/services/api.ts"],"sourcesContent":["import { adminApi } from '@strapi/admin/strapi-admin';\n\nimport type { CreateFile } from '../../../../shared/contracts/files';\n\nconst uploadApi = adminApi\n .enhanceEndpoints({\n addTagTypes: ['Asset', 'Folder'],\n })\n .injectEndpoints({\n endpoints: (builder) => ({\n uploadFiles: builder.mutation<CreateFile.Response, FormData>({\n query: (formData) => ({\n url: '/upload',\n method: 'POST',\n data: formData,\n }),\n invalidatesTags: ['Asset'],\n }),\n }),\n });\n\nexport const { useUploadFilesMutation } = uploadApi;\nexport { uploadApi };\n"],"names":["uploadApi","adminApi","enhanceEndpoints","addTagTypes","injectEndpoints","endpoints","builder","uploadFiles","mutation","query","formData","url","method","data","invalidatesTags","useUploadFilesMutation"],"mappings":";;;;AAIMA,MAAAA,SAAAA,GAAYC,oBACfC,CAAAA,gBAAgB,CAAC;IAChBC,WAAa,EAAA;AAAC,QAAA,OAAA;AAAS,QAAA;AAAS;AAClC,CAAA,CAAA,CACCC,eAAe,CAAC;IACfC,SAAW,EAAA,CAACC,WAAa;YACvBC,WAAaD,EAAAA,OAAAA,CAAQE,QAAQ,CAAgC;gBAC3DC,KAAO,EAAA,CAACC,YAAc;wBACpBC,GAAK,EAAA,SAAA;wBACLC,MAAQ,EAAA,MAAA;wBACRC,IAAMH,EAAAA;qBACR,CAAA;gBACAI,eAAiB,EAAA;AAAC,oBAAA;AAAQ;AAC5B,aAAA;SACF;AACF,CAAA;AAEW,MAAA,EAAEC,sBAAsB,EAAE,GAAGf;;;;;"}
1
+ {"version":3,"file":"api.js","sources":["../../../../admin/src/future/services/api.ts"],"sourcesContent":["import { Dispatch } from '@reduxjs/toolkit';\nimport { adminApi } from '@strapi/admin/strapi-admin';\n\nimport {\n openUploadProgress,\n setFileUploading,\n setFileComplete,\n setFileError,\n updateProgress,\n setUploadFailed,\n retryCancelledFiles,\n} from '../store/uploadProgress';\n\nimport type {\n CreateFilesStream,\n CreateFilesStreamEvents,\n} from '../../../../shared/contracts/files';\n\ninterface UploadFilesArgs {\n formData: FormData;\n totalFiles: number;\n}\n\ninterface RootState {\n admin_app: {\n token?: string | null;\n };\n uploadProgress: {\n uploadId: number;\n files: Array<{\n index: number;\n name: string;\n size: number;\n status: 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n }>;\n };\n}\n\n/**\n * Stores original File objects for retry functionality.\n *\n * Similar to abortControllers, File objects cannot be stored in Redux state\n * (they are not serializable). This Map allows us to retry cancelled uploads\n * by retrieving the original files using the uploadId.\n */\nconst uploadedFiles = new Map<number, File[]>();\n\n/**\n * Registers files for a specific upload to enable retry.\n */\nconst registerUploadedFiles = (uploadId: number, files: File[]) => {\n uploadedFiles.set(uploadId, files);\n};\n\n/**\n * Retrieves stored files for an upload.\n */\nconst getUploadedFiles = (uploadId: number): File[] | undefined => {\n return uploadedFiles.get(uploadId);\n};\n\n/**\n * Manages abort controllers for in-flight uploads.\n *\n * Design decision: Uses a Map to track uploads by their unique uploadId.\n * This approach is necessary because:\n * 1. Redux state cannot store function references (abort controllers)\n * 2. RTK Query's signal is only accessible within the queryFn\n * 3. The upload is triggered in AssetsPage but cancelled from UploadProgressDialog\n *\n * The uploadId ensures we abort the correct upload even if multiple uploads\n * are queued, though the current UI prevents simultaneous uploads.\n */\nconst abortControllers = new Map<number, AbortController>();\n\n/**\n * Registers an abort controller for a specific upload.\n * Called internally when an upload starts.\n */\nconst registerAbortController = (uploadId: number, controller: AbortController) => {\n abortControllers.set(uploadId, controller);\n};\n\n/**\n * Removes an abort controller when an upload completes or is aborted.\n */\nconst unregisterAbortController = (uploadId: number) => {\n abortControllers.delete(uploadId);\n};\n\n/**\n * Aborts an upload by its uploadId.\n * Called from the UploadProgressDialog when the user clicks cancel or close.\n */\nexport const abortUpload = (uploadId: number) => {\n const controller = abortControllers.get(uploadId);\n if (controller) {\n controller.abort();\n unregisterAbortController(uploadId);\n }\n};\n\n/**\n * Parses a raw SSE text chunk into event/data pairs.\n *\n * SSE format:\n * event: <eventName>\\n\n * data: <json>\\n\n * \\n\n */\nconst parseSSEEvents = (chunk: string): Array<{ event: string; data: string }> => {\n const events: Array<{ event: string; data: string }> = [];\n const blocks = chunk.split('\\n\\n').filter(Boolean);\n\n for (const block of blocks) {\n let event = '';\n let data = '';\n\n for (const line of block.split('\\n')) {\n if (line.startsWith('event: ')) {\n event = line.slice(7);\n } else if (line.startsWith('data: ')) {\n data = line.slice(6);\n }\n }\n\n if (event && data) {\n events.push({ event, data });\n }\n }\n\n return events;\n};\n\n/**\n * Makes a streaming upload request to the server.\n *\n * We use fetch directly instead of RTK Query's fetchBaseQuery because:\n * 1. We need access to the raw Response to read the body as a stream\n * 2. RTK Query's baseQuery awaits the full response and parses it as JSON,\n * which doesn't work for Server-Sent Events (SSE) streaming\n * 3. The stream must be read incrementally via response.body.getReader()\n * to dispatch progress updates as files upload\n */\nconst fetchUploadStream = async ({\n token,\n formData,\n signal,\n}: {\n token: string | null | undefined;\n formData: FormData;\n signal: AbortSignal;\n}): Promise<Response> => {\n const backendURL = window.strapi.backendURL;\n const headers: Record<string, string> = {};\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n return fetch(`${backendURL}/upload/unstable/stream`, {\n method: 'POST',\n headers,\n body: formData,\n signal,\n });\n};\n\n/**\n * Options for processing an SSE upload stream.\n */\ninterface ProcessSSEStreamOptions {\n response: Response;\n dispatch: Dispatch;\n indexMapper?: (serverIndex: number) => number;\n}\n\n/**\n * Processes an SSE stream from the upload endpoint.\n * Dispatches Redux actions for each file event and returns the final result.\n *\n * @param options.response - The fetch Response object with SSE body\n * @param options.dispatch - Redux dispatch function\n * @param options.indexMapper - Optional function to map server indices to state indices (for retry)\n * @param options.logPrefix - Optional prefix for console logs\n * @returns The stream result or null if no files completed\n */\nconst processSSEStream = async ({\n response,\n dispatch,\n indexMapper = (i) => i,\n}: ProcessSSEStreamOptions): Promise<CreateFilesStream.Response | null> => {\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let streamResult: CreateFilesStream.Response | null = null;\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events from the buffer\n const lastDoubleNewline = buffer.lastIndexOf('\\n\\n');\n if (lastDoubleNewline === -1) {\n // No complete events yet, keep buffering\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const completePart = buffer.slice(0, lastDoubleNewline + 2);\n buffer = buffer.slice(lastDoubleNewline + 2);\n\n const events = parseSSEEvents(completePart);\n\n for (const { event, data } of events) {\n const parsed = JSON.parse(data);\n const mappedIndex = indexMapper(parsed.index as number);\n\n switch (event) {\n case 'file:uploading': {\n const payload = parsed as CreateFilesStreamEvents.FileUploadingEvent;\n dispatch(\n setFileUploading({\n name: payload.name,\n index: mappedIndex,\n total: payload.total,\n size: payload.size,\n })\n );\n break;\n }\n case 'file:complete': {\n const payload = parsed as CreateFilesStreamEvents.FileCompleteEvent;\n dispatch(\n setFileComplete({\n index: mappedIndex,\n file: payload.file,\n })\n );\n break;\n }\n case 'file:error': {\n const payload = parsed as CreateFilesStreamEvents.FileErrorEvent;\n dispatch(\n setFileError({\n index: mappedIndex,\n name: payload.name,\n message: payload.message,\n })\n );\n break;\n }\n case 'stream:complete': {\n const payload = parsed as CreateFilesStreamEvents.StreamCompleteEvent;\n streamResult = {\n data: payload.data,\n errors: payload.errors,\n };\n break;\n }\n default:\n console.error(`[SSE Upload] unknown event: ${event}`, parsed);\n }\n }\n }\n\n return streamResult;\n};\n\nconst uploadApi = adminApi\n .enhanceEndpoints({\n addTagTypes: ['Asset', 'Folder'],\n })\n .injectEndpoints({\n endpoints: (builder) => ({\n /**\n * Stream upload files to the /upload/unstable/stream endpoint.\n * Reads SSE stream for per-file progress updates.\n */\n uploadFilesStream: builder.mutation<CreateFilesStream.Response, UploadFilesArgs>({\n queryFn: async ({ formData, totalFiles }, { dispatch, getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n\n // Extract file names and sizes from FormData\n const files = formData.getAll('files') as File[];\n const fileInfoJson = formData.get('fileInfo') as string;\n const fileInfo = JSON.parse(fileInfoJson) as Array<{ name: string }>;\n const fileNames = fileInfo.map((info) => info.name);\n const fileSizes = files.map((file) => file.size);\n\n // Open the progress dialog and get the uploadId\n dispatch(openUploadProgress({ totalFiles, fileNames, fileSizes }));\n dispatch(updateProgress(0));\n\n // Get the uploadId from state after dispatching\n const uploadId = (getState() as RootState).uploadProgress.uploadId;\n\n // Store original files for retry functionality\n registerUploadedFiles(uploadId, files);\n\n // Create abort controller for this upload\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n try {\n const response = await fetchUploadStream({\n token,\n formData,\n signal: abortController.signal,\n });\n\n if (!response.ok || !response.body) {\n unregisterAbortController(uploadId);\n\n // Try to parse error message from response\n let errorMessage = 'Upload request failed';\n try {\n const errorData = await response.json();\n if (errorData.error?.message) {\n errorMessage = errorData.error.message;\n } else if (errorData.message) {\n errorMessage = errorData.message;\n }\n } catch {\n // If we can't parse the error, use a generic message with status code\n errorMessage = `Upload failed with status ${response.status}`;\n }\n\n // Mark all files as failed in the UI\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n status: response.status,\n },\n };\n }\n\n const streamResult = await processSSEStream({\n response,\n dispatch,\n });\n\n unregisterAbortController(uploadId);\n\n if (streamResult && streamResult.data.length > 0) {\n return { data: streamResult };\n }\n\n // If stream ended without completing any files, mark all as failed\n const errorMessage = 'No files were uploaded successfully';\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n },\n };\n } catch (err) {\n unregisterAbortController(uploadId);\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n // Don't mark as failed for user-initiated cancellations\n return {\n error: { name: 'UnknownError', message: 'Upload cancelled' },\n };\n }\n\n // For network errors or other exceptions, mark all files as failed\n const errorMessage = err instanceof Error ? err.message : 'Network error occurred';\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n },\n };\n }\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n\n /**\n * Retry uploading cancelled files.\n * Retrieves original File objects and re-uploads only the cancelled ones.\n */\n retryCancelledFilesStream: builder.mutation<CreateFilesStream.Response, void>({\n queryFn: async (_, { dispatch, getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n const { uploadId, files: stateFiles } = (getState() as RootState).uploadProgress;\n\n // Get cancelled files with their original indices\n const cancelledFiles = stateFiles.filter((f) => f.status === 'cancelled');\n if (cancelledFiles.length === 0) {\n return { error: { name: 'UnknownError', message: 'No cancelled files to retry' } };\n }\n\n // Get the original File objects\n const originalFiles = getUploadedFiles(uploadId);\n if (!originalFiles) {\n return { error: { name: 'UnknownError', message: 'Original files not found' } };\n }\n\n // Build mapping from new index to original index\n const indexMapping = cancelledFiles.map((f) => f.index);\n const filesToRetry = cancelledFiles.map((f) => originalFiles[f.index]);\n\n // Reset cancelled files to pending\n dispatch(retryCancelledFiles());\n\n // Build FormData for retry\n const formData = new FormData();\n const fileInfoArray = filesToRetry.map((file) => ({\n name: file.name,\n caption: null,\n alternativeText: null,\n folder: null, // TODO: preserve folder from original upload if needed\n }));\n\n filesToRetry.forEach((file) => {\n formData.append('files', file);\n });\n formData.append('fileInfo', JSON.stringify(fileInfoArray));\n\n // Create abort controller for this retry\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n try {\n const response = await fetchUploadStream({\n token,\n formData,\n signal: abortController.signal,\n });\n\n if (!response.ok || !response.body) {\n unregisterAbortController(uploadId);\n\n let errorMessage = 'Retry request failed';\n try {\n const errorData = await response.json();\n if (errorData.error?.message) {\n errorMessage = errorData.error.message;\n } else if (errorData.message) {\n errorMessage = errorData.message;\n }\n } catch {\n errorMessage = `Retry failed with status ${response.status}`;\n }\n\n // Mark retried files as failed\n for (const originalIndex of indexMapping) {\n dispatch(\n setFileError({\n index: originalIndex,\n name: stateFiles[originalIndex].name,\n message: errorMessage,\n })\n );\n }\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n status: response.status,\n },\n };\n }\n\n const streamResult = await processSSEStream({\n response,\n dispatch,\n indexMapper: (serverIndex) => indexMapping[serverIndex],\n });\n\n unregisterAbortController(uploadId);\n\n if (streamResult && streamResult.data.length > 0) {\n return { data: streamResult };\n }\n\n return {\n error: {\n name: 'UnknownError',\n message: 'No files were uploaded successfully',\n },\n };\n } catch (err) {\n unregisterAbortController(uploadId);\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n return {\n error: { name: 'UnknownError', message: 'Retry cancelled' },\n };\n }\n\n const errorMessage = err instanceof Error ? err.message : 'Network error occurred';\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n },\n };\n }\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n }),\n });\n\nexport const { useUploadFilesStreamMutation, useRetryCancelledFilesStreamMutation } = uploadApi;\nexport { uploadApi };\n"],"names":["uploadedFiles","Map","registerUploadedFiles","uploadId","files","set","getUploadedFiles","get","abortControllers","registerAbortController","controller","unregisterAbortController","delete","abortUpload","abort","parseSSEEvents","chunk","events","blocks","split","filter","Boolean","block","event","data","line","startsWith","slice","push","fetchUploadStream","token","formData","signal","backendURL","window","strapi","headers","Authorization","fetch","method","body","processSSEStream","response","dispatch","indexMapper","i","reader","getReader","decoder","TextDecoder","streamResult","buffer","done","value","read","decode","stream","lastDoubleNewline","lastIndexOf","completePart","parsed","JSON","parse","mappedIndex","index","payload","setFileUploading","name","total","size","setFileComplete","file","setFileError","message","errors","console","error","uploadApi","adminApi","enhanceEndpoints","addTagTypes","injectEndpoints","endpoints","builder","uploadFilesStream","mutation","queryFn","totalFiles","getState","admin_app","getAll","fileInfoJson","fileInfo","fileNames","map","info","fileSizes","openUploadProgress","updateProgress","uploadProgress","abortController","AbortController","ok","errorMessage","errorData","json","status","setUploadFailed","length","err","DOMException","Error","invalidatesTags","type","id","retryCancelledFilesStream","_","stateFiles","cancelledFiles","f","originalFiles","indexMapping","filesToRetry","retryCancelledFiles","FormData","fileInfoArray","caption","alternativeText","folder","forEach","append","stringify","originalIndex","serverIndex","useUploadFilesStreamMutation","useRetryCancelledFilesStreamMutation"],"mappings":"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"}
@@ -1,5 +1,187 @@
1
1
  import { adminApi } from '@strapi/admin/strapi-admin';
2
+ import { openUploadProgress, updateProgress, setUploadFailed, retryCancelledFiles, setFileError, setFileComplete, setFileUploading } from '../store/uploadProgress.mjs';
2
3
 
4
+ /**
5
+ * Stores original File objects for retry functionality.
6
+ *
7
+ * Similar to abortControllers, File objects cannot be stored in Redux state
8
+ * (they are not serializable). This Map allows us to retry cancelled uploads
9
+ * by retrieving the original files using the uploadId.
10
+ */ const uploadedFiles = new Map();
11
+ /**
12
+ * Registers files for a specific upload to enable retry.
13
+ */ const registerUploadedFiles = (uploadId, files)=>{
14
+ uploadedFiles.set(uploadId, files);
15
+ };
16
+ /**
17
+ * Retrieves stored files for an upload.
18
+ */ const getUploadedFiles = (uploadId)=>{
19
+ return uploadedFiles.get(uploadId);
20
+ };
21
+ /**
22
+ * Manages abort controllers for in-flight uploads.
23
+ *
24
+ * Design decision: Uses a Map to track uploads by their unique uploadId.
25
+ * This approach is necessary because:
26
+ * 1. Redux state cannot store function references (abort controllers)
27
+ * 2. RTK Query's signal is only accessible within the queryFn
28
+ * 3. The upload is triggered in AssetsPage but cancelled from UploadProgressDialog
29
+ *
30
+ * The uploadId ensures we abort the correct upload even if multiple uploads
31
+ * are queued, though the current UI prevents simultaneous uploads.
32
+ */ const abortControllers = new Map();
33
+ /**
34
+ * Registers an abort controller for a specific upload.
35
+ * Called internally when an upload starts.
36
+ */ const registerAbortController = (uploadId, controller)=>{
37
+ abortControllers.set(uploadId, controller);
38
+ };
39
+ /**
40
+ * Removes an abort controller when an upload completes or is aborted.
41
+ */ const unregisterAbortController = (uploadId)=>{
42
+ abortControllers.delete(uploadId);
43
+ };
44
+ /**
45
+ * Aborts an upload by its uploadId.
46
+ * Called from the UploadProgressDialog when the user clicks cancel or close.
47
+ */ const abortUpload = (uploadId)=>{
48
+ const controller = abortControllers.get(uploadId);
49
+ if (controller) {
50
+ controller.abort();
51
+ unregisterAbortController(uploadId);
52
+ }
53
+ };
54
+ /**
55
+ * Parses a raw SSE text chunk into event/data pairs.
56
+ *
57
+ * SSE format:
58
+ * event: <eventName>\n
59
+ * data: <json>\n
60
+ * \n
61
+ */ const parseSSEEvents = (chunk)=>{
62
+ const events = [];
63
+ const blocks = chunk.split('\n\n').filter(Boolean);
64
+ for (const block of blocks){
65
+ let event = '';
66
+ let data = '';
67
+ for (const line of block.split('\n')){
68
+ if (line.startsWith('event: ')) {
69
+ event = line.slice(7);
70
+ } else if (line.startsWith('data: ')) {
71
+ data = line.slice(6);
72
+ }
73
+ }
74
+ if (event && data) {
75
+ events.push({
76
+ event,
77
+ data
78
+ });
79
+ }
80
+ }
81
+ return events;
82
+ };
83
+ /**
84
+ * Makes a streaming upload request to the server.
85
+ *
86
+ * We use fetch directly instead of RTK Query's fetchBaseQuery because:
87
+ * 1. We need access to the raw Response to read the body as a stream
88
+ * 2. RTK Query's baseQuery awaits the full response and parses it as JSON,
89
+ * which doesn't work for Server-Sent Events (SSE) streaming
90
+ * 3. The stream must be read incrementally via response.body.getReader()
91
+ * to dispatch progress updates as files upload
92
+ */ const fetchUploadStream = async ({ token, formData, signal })=>{
93
+ const backendURL = window.strapi.backendURL;
94
+ const headers = {};
95
+ if (token) {
96
+ headers.Authorization = `Bearer ${token}`;
97
+ }
98
+ return fetch(`${backendURL}/upload/unstable/stream`, {
99
+ method: 'POST',
100
+ headers,
101
+ body: formData,
102
+ signal
103
+ });
104
+ };
105
+ /**
106
+ * Processes an SSE stream from the upload endpoint.
107
+ * Dispatches Redux actions for each file event and returns the final result.
108
+ *
109
+ * @param options.response - The fetch Response object with SSE body
110
+ * @param options.dispatch - Redux dispatch function
111
+ * @param options.indexMapper - Optional function to map server indices to state indices (for retry)
112
+ * @param options.logPrefix - Optional prefix for console logs
113
+ * @returns The stream result or null if no files completed
114
+ */ const processSSEStream = async ({ response, dispatch, indexMapper = (i)=>i })=>{
115
+ const reader = response.body.getReader();
116
+ const decoder = new TextDecoder();
117
+ let streamResult = null;
118
+ let buffer = '';
119
+ while(true){
120
+ const { done, value } = await reader.read();
121
+ if (done) {
122
+ break;
123
+ }
124
+ buffer += decoder.decode(value, {
125
+ stream: true
126
+ });
127
+ // Process complete SSE events from the buffer
128
+ const lastDoubleNewline = buffer.lastIndexOf('\n\n');
129
+ if (lastDoubleNewline === -1) {
130
+ continue;
131
+ }
132
+ const completePart = buffer.slice(0, lastDoubleNewline + 2);
133
+ buffer = buffer.slice(lastDoubleNewline + 2);
134
+ const events = parseSSEEvents(completePart);
135
+ for (const { event, data } of events){
136
+ const parsed = JSON.parse(data);
137
+ const mappedIndex = indexMapper(parsed.index);
138
+ switch(event){
139
+ case 'file:uploading':
140
+ {
141
+ const payload = parsed;
142
+ dispatch(setFileUploading({
143
+ name: payload.name,
144
+ index: mappedIndex,
145
+ total: payload.total,
146
+ size: payload.size
147
+ }));
148
+ break;
149
+ }
150
+ case 'file:complete':
151
+ {
152
+ const payload = parsed;
153
+ dispatch(setFileComplete({
154
+ index: mappedIndex,
155
+ file: payload.file
156
+ }));
157
+ break;
158
+ }
159
+ case 'file:error':
160
+ {
161
+ const payload = parsed;
162
+ dispatch(setFileError({
163
+ index: mappedIndex,
164
+ name: payload.name,
165
+ message: payload.message
166
+ }));
167
+ break;
168
+ }
169
+ case 'stream:complete':
170
+ {
171
+ const payload = parsed;
172
+ streamResult = {
173
+ data: payload.data,
174
+ errors: payload.errors
175
+ };
176
+ break;
177
+ }
178
+ default:
179
+ console.error(`[SSE Upload] unknown event: ${event}`, parsed);
180
+ }
181
+ }
182
+ }
183
+ return streamResult;
184
+ };
3
185
  const uploadApi = adminApi.enhanceEndpoints({
4
186
  addTagTypes: [
5
187
  'Asset',
@@ -7,19 +189,245 @@ const uploadApi = adminApi.enhanceEndpoints({
7
189
  ]
8
190
  }).injectEndpoints({
9
191
  endpoints: (builder)=>({
10
- uploadFiles: builder.mutation({
11
- query: (formData)=>({
12
- url: '/upload',
13
- method: 'POST',
14
- data: formData
15
- }),
192
+ /**
193
+ * Stream upload files to the /upload/unstable/stream endpoint.
194
+ * Reads SSE stream for per-file progress updates.
195
+ */ uploadFilesStream: builder.mutation({
196
+ queryFn: async ({ formData, totalFiles }, { dispatch, getState })=>{
197
+ const token = getState().admin_app?.token;
198
+ // Extract file names and sizes from FormData
199
+ const files = formData.getAll('files');
200
+ const fileInfoJson = formData.get('fileInfo');
201
+ const fileInfo = JSON.parse(fileInfoJson);
202
+ const fileNames = fileInfo.map((info)=>info.name);
203
+ const fileSizes = files.map((file)=>file.size);
204
+ // Open the progress dialog and get the uploadId
205
+ dispatch(openUploadProgress({
206
+ totalFiles,
207
+ fileNames,
208
+ fileSizes
209
+ }));
210
+ dispatch(updateProgress(0));
211
+ // Get the uploadId from state after dispatching
212
+ const uploadId = getState().uploadProgress.uploadId;
213
+ // Store original files for retry functionality
214
+ registerUploadedFiles(uploadId, files);
215
+ // Create abort controller for this upload
216
+ const abortController = new AbortController();
217
+ registerAbortController(uploadId, abortController);
218
+ try {
219
+ const response = await fetchUploadStream({
220
+ token,
221
+ formData,
222
+ signal: abortController.signal
223
+ });
224
+ if (!response.ok || !response.body) {
225
+ unregisterAbortController(uploadId);
226
+ // Try to parse error message from response
227
+ let errorMessage = 'Upload request failed';
228
+ try {
229
+ const errorData = await response.json();
230
+ if (errorData.error?.message) {
231
+ errorMessage = errorData.error.message;
232
+ } else if (errorData.message) {
233
+ errorMessage = errorData.message;
234
+ }
235
+ } catch {
236
+ // If we can't parse the error, use a generic message with status code
237
+ errorMessage = `Upload failed with status ${response.status}`;
238
+ }
239
+ // Mark all files as failed in the UI
240
+ dispatch(setUploadFailed({
241
+ message: errorMessage
242
+ }));
243
+ return {
244
+ error: {
245
+ name: 'UnknownError',
246
+ message: errorMessage,
247
+ status: response.status
248
+ }
249
+ };
250
+ }
251
+ const streamResult = await processSSEStream({
252
+ response,
253
+ dispatch
254
+ });
255
+ unregisterAbortController(uploadId);
256
+ if (streamResult && streamResult.data.length > 0) {
257
+ return {
258
+ data: streamResult
259
+ };
260
+ }
261
+ // If stream ended without completing any files, mark all as failed
262
+ const errorMessage = 'No files were uploaded successfully';
263
+ dispatch(setUploadFailed({
264
+ message: errorMessage
265
+ }));
266
+ return {
267
+ error: {
268
+ name: 'UnknownError',
269
+ message: errorMessage
270
+ }
271
+ };
272
+ } catch (err) {
273
+ unregisterAbortController(uploadId);
274
+ if (err instanceof DOMException && err.name === 'AbortError') {
275
+ // Don't mark as failed for user-initiated cancellations
276
+ return {
277
+ error: {
278
+ name: 'UnknownError',
279
+ message: 'Upload cancelled'
280
+ }
281
+ };
282
+ }
283
+ // For network errors or other exceptions, mark all files as failed
284
+ const errorMessage = err instanceof Error ? err.message : 'Network error occurred';
285
+ dispatch(setUploadFailed({
286
+ message: errorMessage
287
+ }));
288
+ return {
289
+ error: {
290
+ name: 'UnknownError',
291
+ message: errorMessage
292
+ }
293
+ };
294
+ }
295
+ },
16
296
  invalidatesTags: [
17
- 'Asset'
297
+ {
298
+ type: 'Asset',
299
+ id: 'LIST'
300
+ }
301
+ ]
302
+ }),
303
+ /**
304
+ * Retry uploading cancelled files.
305
+ * Retrieves original File objects and re-uploads only the cancelled ones.
306
+ */ retryCancelledFilesStream: builder.mutation({
307
+ queryFn: async (_, { dispatch, getState })=>{
308
+ const token = getState().admin_app?.token;
309
+ const { uploadId, files: stateFiles } = getState().uploadProgress;
310
+ // Get cancelled files with their original indices
311
+ const cancelledFiles = stateFiles.filter((f)=>f.status === 'cancelled');
312
+ if (cancelledFiles.length === 0) {
313
+ return {
314
+ error: {
315
+ name: 'UnknownError',
316
+ message: 'No cancelled files to retry'
317
+ }
318
+ };
319
+ }
320
+ // Get the original File objects
321
+ const originalFiles = getUploadedFiles(uploadId);
322
+ if (!originalFiles) {
323
+ return {
324
+ error: {
325
+ name: 'UnknownError',
326
+ message: 'Original files not found'
327
+ }
328
+ };
329
+ }
330
+ // Build mapping from new index to original index
331
+ const indexMapping = cancelledFiles.map((f)=>f.index);
332
+ const filesToRetry = cancelledFiles.map((f)=>originalFiles[f.index]);
333
+ // Reset cancelled files to pending
334
+ dispatch(retryCancelledFiles());
335
+ // Build FormData for retry
336
+ const formData = new FormData();
337
+ const fileInfoArray = filesToRetry.map((file)=>({
338
+ name: file.name,
339
+ caption: null,
340
+ alternativeText: null,
341
+ folder: null
342
+ }));
343
+ filesToRetry.forEach((file)=>{
344
+ formData.append('files', file);
345
+ });
346
+ formData.append('fileInfo', JSON.stringify(fileInfoArray));
347
+ // Create abort controller for this retry
348
+ const abortController = new AbortController();
349
+ registerAbortController(uploadId, abortController);
350
+ try {
351
+ const response = await fetchUploadStream({
352
+ token,
353
+ formData,
354
+ signal: abortController.signal
355
+ });
356
+ if (!response.ok || !response.body) {
357
+ unregisterAbortController(uploadId);
358
+ let errorMessage = 'Retry request failed';
359
+ try {
360
+ const errorData = await response.json();
361
+ if (errorData.error?.message) {
362
+ errorMessage = errorData.error.message;
363
+ } else if (errorData.message) {
364
+ errorMessage = errorData.message;
365
+ }
366
+ } catch {
367
+ errorMessage = `Retry failed with status ${response.status}`;
368
+ }
369
+ // Mark retried files as failed
370
+ for (const originalIndex of indexMapping){
371
+ dispatch(setFileError({
372
+ index: originalIndex,
373
+ name: stateFiles[originalIndex].name,
374
+ message: errorMessage
375
+ }));
376
+ }
377
+ return {
378
+ error: {
379
+ name: 'UnknownError',
380
+ message: errorMessage,
381
+ status: response.status
382
+ }
383
+ };
384
+ }
385
+ const streamResult = await processSSEStream({
386
+ response,
387
+ dispatch,
388
+ indexMapper: (serverIndex)=>indexMapping[serverIndex]
389
+ });
390
+ unregisterAbortController(uploadId);
391
+ if (streamResult && streamResult.data.length > 0) {
392
+ return {
393
+ data: streamResult
394
+ };
395
+ }
396
+ return {
397
+ error: {
398
+ name: 'UnknownError',
399
+ message: 'No files were uploaded successfully'
400
+ }
401
+ };
402
+ } catch (err) {
403
+ unregisterAbortController(uploadId);
404
+ if (err instanceof DOMException && err.name === 'AbortError') {
405
+ return {
406
+ error: {
407
+ name: 'UnknownError',
408
+ message: 'Retry cancelled'
409
+ }
410
+ };
411
+ }
412
+ const errorMessage = err instanceof Error ? err.message : 'Network error occurred';
413
+ return {
414
+ error: {
415
+ name: 'UnknownError',
416
+ message: errorMessage
417
+ }
418
+ };
419
+ }
420
+ },
421
+ invalidatesTags: [
422
+ {
423
+ type: 'Asset',
424
+ id: 'LIST'
425
+ }
18
426
  ]
19
427
  })
20
428
  })
21
429
  });
22
- const { useUploadFilesMutation } = uploadApi;
430
+ const { useUploadFilesStreamMutation, useRetryCancelledFilesStreamMutation } = uploadApi;
23
431
 
24
- export { uploadApi, useUploadFilesMutation };
432
+ export { abortUpload, uploadApi, useRetryCancelledFilesStreamMutation, useUploadFilesStreamMutation };
25
433
  //# sourceMappingURL=api.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.mjs","sources":["../../../../admin/src/future/services/api.ts"],"sourcesContent":["import { adminApi } from '@strapi/admin/strapi-admin';\n\nimport type { CreateFile } from '../../../../shared/contracts/files';\n\nconst uploadApi = adminApi\n .enhanceEndpoints({\n addTagTypes: ['Asset', 'Folder'],\n })\n .injectEndpoints({\n endpoints: (builder) => ({\n uploadFiles: builder.mutation<CreateFile.Response, FormData>({\n query: (formData) => ({\n url: '/upload',\n method: 'POST',\n data: formData,\n }),\n invalidatesTags: ['Asset'],\n }),\n }),\n });\n\nexport const { useUploadFilesMutation } = uploadApi;\nexport { uploadApi };\n"],"names":["uploadApi","adminApi","enhanceEndpoints","addTagTypes","injectEndpoints","endpoints","builder","uploadFiles","mutation","query","formData","url","method","data","invalidatesTags","useUploadFilesMutation"],"mappings":";;AAIMA,MAAAA,SAAAA,GAAYC,QACfC,CAAAA,gBAAgB,CAAC;IAChBC,WAAa,EAAA;AAAC,QAAA,OAAA;AAAS,QAAA;AAAS;AAClC,CAAA,CAAA,CACCC,eAAe,CAAC;IACfC,SAAW,EAAA,CAACC,WAAa;YACvBC,WAAaD,EAAAA,OAAAA,CAAQE,QAAQ,CAAgC;gBAC3DC,KAAO,EAAA,CAACC,YAAc;wBACpBC,GAAK,EAAA,SAAA;wBACLC,MAAQ,EAAA,MAAA;wBACRC,IAAMH,EAAAA;qBACR,CAAA;gBACAI,eAAiB,EAAA;AAAC,oBAAA;AAAQ;AAC5B,aAAA;SACF;AACF,CAAA;AAEW,MAAA,EAAEC,sBAAsB,EAAE,GAAGf;;;;"}
1
+ {"version":3,"file":"api.mjs","sources":["../../../../admin/src/future/services/api.ts"],"sourcesContent":["import { Dispatch } from '@reduxjs/toolkit';\nimport { adminApi } from '@strapi/admin/strapi-admin';\n\nimport {\n openUploadProgress,\n setFileUploading,\n setFileComplete,\n setFileError,\n updateProgress,\n setUploadFailed,\n retryCancelledFiles,\n} from '../store/uploadProgress';\n\nimport type {\n CreateFilesStream,\n CreateFilesStreamEvents,\n} from '../../../../shared/contracts/files';\n\ninterface UploadFilesArgs {\n formData: FormData;\n totalFiles: number;\n}\n\ninterface RootState {\n admin_app: {\n token?: string | null;\n };\n uploadProgress: {\n uploadId: number;\n files: Array<{\n index: number;\n name: string;\n size: number;\n status: 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n }>;\n };\n}\n\n/**\n * Stores original File objects for retry functionality.\n *\n * Similar to abortControllers, File objects cannot be stored in Redux state\n * (they are not serializable). This Map allows us to retry cancelled uploads\n * by retrieving the original files using the uploadId.\n */\nconst uploadedFiles = new Map<number, File[]>();\n\n/**\n * Registers files for a specific upload to enable retry.\n */\nconst registerUploadedFiles = (uploadId: number, files: File[]) => {\n uploadedFiles.set(uploadId, files);\n};\n\n/**\n * Retrieves stored files for an upload.\n */\nconst getUploadedFiles = (uploadId: number): File[] | undefined => {\n return uploadedFiles.get(uploadId);\n};\n\n/**\n * Manages abort controllers for in-flight uploads.\n *\n * Design decision: Uses a Map to track uploads by their unique uploadId.\n * This approach is necessary because:\n * 1. Redux state cannot store function references (abort controllers)\n * 2. RTK Query's signal is only accessible within the queryFn\n * 3. The upload is triggered in AssetsPage but cancelled from UploadProgressDialog\n *\n * The uploadId ensures we abort the correct upload even if multiple uploads\n * are queued, though the current UI prevents simultaneous uploads.\n */\nconst abortControllers = new Map<number, AbortController>();\n\n/**\n * Registers an abort controller for a specific upload.\n * Called internally when an upload starts.\n */\nconst registerAbortController = (uploadId: number, controller: AbortController) => {\n abortControllers.set(uploadId, controller);\n};\n\n/**\n * Removes an abort controller when an upload completes or is aborted.\n */\nconst unregisterAbortController = (uploadId: number) => {\n abortControllers.delete(uploadId);\n};\n\n/**\n * Aborts an upload by its uploadId.\n * Called from the UploadProgressDialog when the user clicks cancel or close.\n */\nexport const abortUpload = (uploadId: number) => {\n const controller = abortControllers.get(uploadId);\n if (controller) {\n controller.abort();\n unregisterAbortController(uploadId);\n }\n};\n\n/**\n * Parses a raw SSE text chunk into event/data pairs.\n *\n * SSE format:\n * event: <eventName>\\n\n * data: <json>\\n\n * \\n\n */\nconst parseSSEEvents = (chunk: string): Array<{ event: string; data: string }> => {\n const events: Array<{ event: string; data: string }> = [];\n const blocks = chunk.split('\\n\\n').filter(Boolean);\n\n for (const block of blocks) {\n let event = '';\n let data = '';\n\n for (const line of block.split('\\n')) {\n if (line.startsWith('event: ')) {\n event = line.slice(7);\n } else if (line.startsWith('data: ')) {\n data = line.slice(6);\n }\n }\n\n if (event && data) {\n events.push({ event, data });\n }\n }\n\n return events;\n};\n\n/**\n * Makes a streaming upload request to the server.\n *\n * We use fetch directly instead of RTK Query's fetchBaseQuery because:\n * 1. We need access to the raw Response to read the body as a stream\n * 2. RTK Query's baseQuery awaits the full response and parses it as JSON,\n * which doesn't work for Server-Sent Events (SSE) streaming\n * 3. The stream must be read incrementally via response.body.getReader()\n * to dispatch progress updates as files upload\n */\nconst fetchUploadStream = async ({\n token,\n formData,\n signal,\n}: {\n token: string | null | undefined;\n formData: FormData;\n signal: AbortSignal;\n}): Promise<Response> => {\n const backendURL = window.strapi.backendURL;\n const headers: Record<string, string> = {};\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n return fetch(`${backendURL}/upload/unstable/stream`, {\n method: 'POST',\n headers,\n body: formData,\n signal,\n });\n};\n\n/**\n * Options for processing an SSE upload stream.\n */\ninterface ProcessSSEStreamOptions {\n response: Response;\n dispatch: Dispatch;\n indexMapper?: (serverIndex: number) => number;\n}\n\n/**\n * Processes an SSE stream from the upload endpoint.\n * Dispatches Redux actions for each file event and returns the final result.\n *\n * @param options.response - The fetch Response object with SSE body\n * @param options.dispatch - Redux dispatch function\n * @param options.indexMapper - Optional function to map server indices to state indices (for retry)\n * @param options.logPrefix - Optional prefix for console logs\n * @returns The stream result or null if no files completed\n */\nconst processSSEStream = async ({\n response,\n dispatch,\n indexMapper = (i) => i,\n}: ProcessSSEStreamOptions): Promise<CreateFilesStream.Response | null> => {\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let streamResult: CreateFilesStream.Response | null = null;\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE events from the buffer\n const lastDoubleNewline = buffer.lastIndexOf('\\n\\n');\n if (lastDoubleNewline === -1) {\n // No complete events yet, keep buffering\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const completePart = buffer.slice(0, lastDoubleNewline + 2);\n buffer = buffer.slice(lastDoubleNewline + 2);\n\n const events = parseSSEEvents(completePart);\n\n for (const { event, data } of events) {\n const parsed = JSON.parse(data);\n const mappedIndex = indexMapper(parsed.index as number);\n\n switch (event) {\n case 'file:uploading': {\n const payload = parsed as CreateFilesStreamEvents.FileUploadingEvent;\n dispatch(\n setFileUploading({\n name: payload.name,\n index: mappedIndex,\n total: payload.total,\n size: payload.size,\n })\n );\n break;\n }\n case 'file:complete': {\n const payload = parsed as CreateFilesStreamEvents.FileCompleteEvent;\n dispatch(\n setFileComplete({\n index: mappedIndex,\n file: payload.file,\n })\n );\n break;\n }\n case 'file:error': {\n const payload = parsed as CreateFilesStreamEvents.FileErrorEvent;\n dispatch(\n setFileError({\n index: mappedIndex,\n name: payload.name,\n message: payload.message,\n })\n );\n break;\n }\n case 'stream:complete': {\n const payload = parsed as CreateFilesStreamEvents.StreamCompleteEvent;\n streamResult = {\n data: payload.data,\n errors: payload.errors,\n };\n break;\n }\n default:\n console.error(`[SSE Upload] unknown event: ${event}`, parsed);\n }\n }\n }\n\n return streamResult;\n};\n\nconst uploadApi = adminApi\n .enhanceEndpoints({\n addTagTypes: ['Asset', 'Folder'],\n })\n .injectEndpoints({\n endpoints: (builder) => ({\n /**\n * Stream upload files to the /upload/unstable/stream endpoint.\n * Reads SSE stream for per-file progress updates.\n */\n uploadFilesStream: builder.mutation<CreateFilesStream.Response, UploadFilesArgs>({\n queryFn: async ({ formData, totalFiles }, { dispatch, getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n\n // Extract file names and sizes from FormData\n const files = formData.getAll('files') as File[];\n const fileInfoJson = formData.get('fileInfo') as string;\n const fileInfo = JSON.parse(fileInfoJson) as Array<{ name: string }>;\n const fileNames = fileInfo.map((info) => info.name);\n const fileSizes = files.map((file) => file.size);\n\n // Open the progress dialog and get the uploadId\n dispatch(openUploadProgress({ totalFiles, fileNames, fileSizes }));\n dispatch(updateProgress(0));\n\n // Get the uploadId from state after dispatching\n const uploadId = (getState() as RootState).uploadProgress.uploadId;\n\n // Store original files for retry functionality\n registerUploadedFiles(uploadId, files);\n\n // Create abort controller for this upload\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n try {\n const response = await fetchUploadStream({\n token,\n formData,\n signal: abortController.signal,\n });\n\n if (!response.ok || !response.body) {\n unregisterAbortController(uploadId);\n\n // Try to parse error message from response\n let errorMessage = 'Upload request failed';\n try {\n const errorData = await response.json();\n if (errorData.error?.message) {\n errorMessage = errorData.error.message;\n } else if (errorData.message) {\n errorMessage = errorData.message;\n }\n } catch {\n // If we can't parse the error, use a generic message with status code\n errorMessage = `Upload failed with status ${response.status}`;\n }\n\n // Mark all files as failed in the UI\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n status: response.status,\n },\n };\n }\n\n const streamResult = await processSSEStream({\n response,\n dispatch,\n });\n\n unregisterAbortController(uploadId);\n\n if (streamResult && streamResult.data.length > 0) {\n return { data: streamResult };\n }\n\n // If stream ended without completing any files, mark all as failed\n const errorMessage = 'No files were uploaded successfully';\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n },\n };\n } catch (err) {\n unregisterAbortController(uploadId);\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n // Don't mark as failed for user-initiated cancellations\n return {\n error: { name: 'UnknownError', message: 'Upload cancelled' },\n };\n }\n\n // For network errors or other exceptions, mark all files as failed\n const errorMessage = err instanceof Error ? err.message : 'Network error occurred';\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n },\n };\n }\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n\n /**\n * Retry uploading cancelled files.\n * Retrieves original File objects and re-uploads only the cancelled ones.\n */\n retryCancelledFilesStream: builder.mutation<CreateFilesStream.Response, void>({\n queryFn: async (_, { dispatch, getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n const { uploadId, files: stateFiles } = (getState() as RootState).uploadProgress;\n\n // Get cancelled files with their original indices\n const cancelledFiles = stateFiles.filter((f) => f.status === 'cancelled');\n if (cancelledFiles.length === 0) {\n return { error: { name: 'UnknownError', message: 'No cancelled files to retry' } };\n }\n\n // Get the original File objects\n const originalFiles = getUploadedFiles(uploadId);\n if (!originalFiles) {\n return { error: { name: 'UnknownError', message: 'Original files not found' } };\n }\n\n // Build mapping from new index to original index\n const indexMapping = cancelledFiles.map((f) => f.index);\n const filesToRetry = cancelledFiles.map((f) => originalFiles[f.index]);\n\n // Reset cancelled files to pending\n dispatch(retryCancelledFiles());\n\n // Build FormData for retry\n const formData = new FormData();\n const fileInfoArray = filesToRetry.map((file) => ({\n name: file.name,\n caption: null,\n alternativeText: null,\n folder: null, // TODO: preserve folder from original upload if needed\n }));\n\n filesToRetry.forEach((file) => {\n formData.append('files', file);\n });\n formData.append('fileInfo', JSON.stringify(fileInfoArray));\n\n // Create abort controller for this retry\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n try {\n const response = await fetchUploadStream({\n token,\n formData,\n signal: abortController.signal,\n });\n\n if (!response.ok || !response.body) {\n unregisterAbortController(uploadId);\n\n let errorMessage = 'Retry request failed';\n try {\n const errorData = await response.json();\n if (errorData.error?.message) {\n errorMessage = errorData.error.message;\n } else if (errorData.message) {\n errorMessage = errorData.message;\n }\n } catch {\n errorMessage = `Retry failed with status ${response.status}`;\n }\n\n // Mark retried files as failed\n for (const originalIndex of indexMapping) {\n dispatch(\n setFileError({\n index: originalIndex,\n name: stateFiles[originalIndex].name,\n message: errorMessage,\n })\n );\n }\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n status: response.status,\n },\n };\n }\n\n const streamResult = await processSSEStream({\n response,\n dispatch,\n indexMapper: (serverIndex) => indexMapping[serverIndex],\n });\n\n unregisterAbortController(uploadId);\n\n if (streamResult && streamResult.data.length > 0) {\n return { data: streamResult };\n }\n\n return {\n error: {\n name: 'UnknownError',\n message: 'No files were uploaded successfully',\n },\n };\n } catch (err) {\n unregisterAbortController(uploadId);\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n return {\n error: { name: 'UnknownError', message: 'Retry cancelled' },\n };\n }\n\n const errorMessage = err instanceof Error ? err.message : 'Network error occurred';\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n },\n };\n }\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n }),\n });\n\nexport const { useUploadFilesStreamMutation, useRetryCancelledFilesStreamMutation } = uploadApi;\nexport { uploadApi };\n"],"names":["uploadedFiles","Map","registerUploadedFiles","uploadId","files","set","getUploadedFiles","get","abortControllers","registerAbortController","controller","unregisterAbortController","delete","abortUpload","abort","parseSSEEvents","chunk","events","blocks","split","filter","Boolean","block","event","data","line","startsWith","slice","push","fetchUploadStream","token","formData","signal","backendURL","window","strapi","headers","Authorization","fetch","method","body","processSSEStream","response","dispatch","indexMapper","i","reader","getReader","decoder","TextDecoder","streamResult","buffer","done","value","read","decode","stream","lastDoubleNewline","lastIndexOf","completePart","parsed","JSON","parse","mappedIndex","index","payload","setFileUploading","name","total","size","setFileComplete","file","setFileError","message","errors","console","error","uploadApi","adminApi","enhanceEndpoints","addTagTypes","injectEndpoints","endpoints","builder","uploadFilesStream","mutation","queryFn","totalFiles","getState","admin_app","getAll","fileInfoJson","fileInfo","fileNames","map","info","fileSizes","openUploadProgress","updateProgress","uploadProgress","abortController","AbortController","ok","errorMessage","errorData","json","status","setUploadFailed","length","err","DOMException","Error","invalidatesTags","type","id","retryCancelledFilesStream","_","stateFiles","cancelledFiles","f","originalFiles","indexMapping","filesToRetry","retryCancelledFiles","FormData","fileInfoArray","caption","alternativeText","folder","forEach","append","stringify","originalIndex","serverIndex","useUploadFilesStreamMutation","useRetryCancelledFilesStreamMutation"],"mappings":"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"}
@@ -5,13 +5,42 @@ var api = require('./api.js');
5
5
  const assetsApi = api.uploadApi.injectEndpoints({
6
6
  endpoints: (builder)=>({
7
7
  getAssets: builder.query({
8
- query: (params = {})=>({
8
+ query: (params = {})=>{
9
+ const { folder, ...rest } = params;
10
+ const queryParams = {
11
+ ...rest
12
+ };
13
+ if (folder != null) {
14
+ queryParams['filters'] = {
15
+ $and: [
16
+ {
17
+ folder: {
18
+ id: folder
19
+ }
20
+ }
21
+ ]
22
+ };
23
+ } else {
24
+ queryParams['filters'] = {
25
+ $and: [
26
+ {
27
+ folder: {
28
+ id: {
29
+ $null: true
30
+ }
31
+ }
32
+ }
33
+ ]
34
+ };
35
+ }
36
+ return {
9
37
  url: '/upload/files',
10
38
  method: 'GET',
11
39
  config: {
12
- params
40
+ params: queryParams
13
41
  }
14
- }),
42
+ };
43
+ },
15
44
  transformResponse: (response)=>response,
16
45
  providesTags: (result)=>result ? [
17
46
  ...result.results.map(({ id })=>({