@strapi/upload 5.47.0 → 5.48.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.
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js +12 -2
- package/dist/admin/components/EditAssetDialog/EditAssetContent.js.map +1 -1
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs +12 -2
- package/dist/admin/components/EditAssetDialog/EditAssetContent.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js +1 -0
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs +1 -0
- package/dist/admin/components/UploadAssetDialog/UploadAssetDialog.mjs.map +1 -1
- package/dist/admin/future/components/Drawer.js +7 -2
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +7 -2
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +33 -29
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +36 -32
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.js +2 -2
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +3 -3
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +733 -148
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +737 -155
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js +25 -5
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs +25 -5
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetPreview.mjs.map +1 -1
- package/dist/admin/future/services/api.js +124 -200
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +124 -200
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +88 -1
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +86 -2
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +33 -1
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +33 -2
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/settings.js +18 -0
- package/dist/admin/future/services/settings.js.map +1 -0
- package/dist/admin/future/services/settings.mjs +16 -0
- package/dist/admin/future/services/settings.mjs.map +1 -0
- package/dist/admin/future/services/uploadFileViaXHR.js +92 -0
- package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -0
- package/dist/admin/future/services/uploadFileViaXHR.mjs +88 -0
- package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -0
- package/dist/admin/future/store/uploadProgress.js +32 -26
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +32 -27
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/future/utils/createRafBatcher.js +42 -0
- package/dist/admin/future/utils/createRafBatcher.js.map +1 -0
- package/dist/admin/future/utils/createRafBatcher.mjs +40 -0
- package/dist/admin/future/utils/createRafBatcher.mjs.map +1 -0
- package/dist/admin/future/utils/downloadFile.js +19 -0
- package/dist/admin/future/utils/downloadFile.js.map +1 -0
- package/dist/admin/future/utils/downloadFile.mjs +17 -0
- package/dist/admin/future/utils/downloadFile.mjs.map +1 -0
- package/dist/admin/hooks/useAssets.js +5 -3
- package/dist/admin/hooks/useAssets.js.map +1 -1
- package/dist/admin/hooks/useAssets.mjs +5 -3
- package/dist/admin/hooks/useAssets.mjs.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/EditAssetDialog/EditAssetContent.d.ts +2 -1
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +22 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetPreview.d.ts +4 -1
- package/dist/admin/src/future/services/api.d.ts +9 -8
- package/dist/admin/src/future/services/assets.d.ts +11 -2
- package/dist/admin/src/future/services/folders.d.ts +1 -1
- package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +34 -0
- package/dist/admin/src/future/store/uploadProgress.d.ts +17 -4
- package/dist/admin/src/future/utils/createRafBatcher.d.ts +23 -0
- package/dist/admin/src/future/utils/downloadFile.d.ts +6 -0
- package/dist/admin/translations/{dk.json.js → da.json.js} +3 -3
- package/dist/admin/translations/{dk.json.js.map → da.json.js.map} +1 -1
- package/dist/admin/translations/{dk.json.mjs → da.json.mjs} +3 -3
- package/dist/admin/translations/{dk.json.mjs.map → da.json.mjs.map} +1 -1
- package/dist/admin/translations/en.json.js +26 -1
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +26 -1
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/bootstrap.js +0 -3
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs +0 -3
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-upload.js +69 -118
- package/dist/server/controllers/admin-upload.js.map +1 -1
- package/dist/server/controllers/admin-upload.mjs +69 -118
- package/dist/server/controllers/admin-upload.mjs.map +1 -1
- package/dist/server/routes/admin.js +2 -2
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +2 -2
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata-jobs.js +0 -23
- package/dist/server/services/ai-metadata-jobs.js.map +1 -1
- package/dist/server/services/ai-metadata-jobs.mjs +0 -23
- package/dist/server/services/ai-metadata-jobs.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +16 -8
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +16 -8
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/services/upload.js +1 -1
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +1 -1
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-upload.d.ts +6 -8
- package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +1 -1
- package/dist/server/src/index.d.ts +1 -2
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata-jobs.d.ts +0 -1
- package/dist/server/src/services/ai-metadata-jobs.d.ts.map +1 -1
- package/dist/server/src/services/image-manipulation.d.ts +5 -0
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +0 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/dist/server/src/types.d.ts +2 -2
- package/dist/server/src/types.d.ts.map +1 -1
- package/dist/shared/contracts/files.d.ts +19 -2
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -1,45 +1,30 @@
|
|
|
1
1
|
import { adminApi } from '@strapi/admin/strapi-admin';
|
|
2
|
-
import { openUploadProgress,
|
|
2
|
+
import { openUploadProgress, setUploadFailed, retryCancelledFiles, setFileError, setFileComplete, setFileUploading, setFileProgress } from '../store/uploadProgress.mjs';
|
|
3
|
+
import { createRafBatcher } from '../utils/createRafBatcher.mjs';
|
|
3
4
|
import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
5
|
+
import { uploadFileViaXHR, UploadAbortedError } from './uploadFileViaXHR.mjs';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
* Stores original
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* by retrieving the original files using the uploadId.
|
|
11
|
-
*/ const uploadedFiles = new Map();
|
|
12
|
-
/**
|
|
13
|
-
* Registers files for a specific upload to enable retry.
|
|
14
|
-
*/ const registerUploadedFiles = (uploadId, files)=>{
|
|
15
|
-
uploadedFiles.set(uploadId, files);
|
|
8
|
+
* Stores the original upload entries for a batch (keyed by uploadId) to enable retry.
|
|
9
|
+
*/ const uploadRegistry = new Map();
|
|
10
|
+
const registerUploadEntries = (uploadId, entries)=>{
|
|
11
|
+
uploadRegistry.set(uploadId, entries);
|
|
16
12
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/ const getUploadedFiles = (uploadId)=>{
|
|
20
|
-
return uploadedFiles.get(uploadId);
|
|
13
|
+
const getUploadEntries = (uploadId)=>{
|
|
14
|
+
return uploadRegistry.get(uploadId);
|
|
21
15
|
};
|
|
22
16
|
/**
|
|
23
17
|
* Manages abort controllers for in-flight uploads.
|
|
24
18
|
*
|
|
25
19
|
* Design decision: Uses a Map to track uploads by their unique uploadId.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* 3. The upload is triggered in AssetsPage but cancelled from UploadProgressDialog
|
|
30
|
-
*
|
|
31
|
-
* The uploadId ensures we abort the correct upload even if multiple uploads
|
|
32
|
-
* are queued, though the current UI prevents simultaneous uploads.
|
|
20
|
+
* Redux state cannot store function references (abort controllers), RTK Query's
|
|
21
|
+
* signal is only accessible within the queryFn, and the upload is triggered in
|
|
22
|
+
* AssetsPage but cancelled from UploadProgressDialog.
|
|
33
23
|
*/ const abortControllers = new Map();
|
|
34
|
-
|
|
35
|
-
* Registers an abort controller for a specific upload.
|
|
36
|
-
* Called internally when an upload starts.
|
|
37
|
-
*/ const registerAbortController = (uploadId, controller)=>{
|
|
24
|
+
const registerAbortController = (uploadId, controller)=>{
|
|
38
25
|
abortControllers.set(uploadId, controller);
|
|
39
26
|
};
|
|
40
|
-
|
|
41
|
-
* Removes an abort controller when an upload completes or is aborted.
|
|
42
|
-
*/ const unregisterAbortController = (uploadId)=>{
|
|
27
|
+
const unregisterAbortController = (uploadId)=>{
|
|
43
28
|
abortControllers.delete(uploadId);
|
|
44
29
|
};
|
|
45
30
|
/**
|
|
@@ -53,6 +38,68 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
53
38
|
}
|
|
54
39
|
};
|
|
55
40
|
/**
|
|
41
|
+
* Uploads the given entry indices one at a time through the single-file endpoint.
|
|
42
|
+
*
|
|
43
|
+
* For each file: dispatches "uploading", wires `XHR.upload.onprogress` through a
|
|
44
|
+
* per-frame batcher to `setFileProgress`, awaits the XHR, then dispatches
|
|
45
|
+
* "complete" or "error". Each file is wrapped in its own try/catch so one failure
|
|
46
|
+
* does not stop the batch. An abort stops the loop without starting further files.
|
|
47
|
+
*/ const runSequentialUpload = async ({ entries, indices, token, uploadId, abortController, dispatch })=>{
|
|
48
|
+
const url = `${window.strapi.backendURL}/upload/unstable/upload-file`;
|
|
49
|
+
const uploaded = [];
|
|
50
|
+
for (const index of indices){
|
|
51
|
+
if (abortController.signal.aborted) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
const entry = entries[index];
|
|
55
|
+
if (!entry) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const fileName = entry.fileInfo?.name ?? entry.file.name;
|
|
59
|
+
dispatch(setFileUploading({
|
|
60
|
+
name: fileName,
|
|
61
|
+
index,
|
|
62
|
+
size: entry.file.size
|
|
63
|
+
}));
|
|
64
|
+
const formData = new FormData();
|
|
65
|
+
formData.append('files', entry.file);
|
|
66
|
+
formData.append('fileInfo', JSON.stringify(entry.fileInfo));
|
|
67
|
+
// Coalesce high-frequency progress events into one dispatch per frame.
|
|
68
|
+
const batcher = createRafBatcher((bytes)=>{
|
|
69
|
+
dispatch(setFileProgress({
|
|
70
|
+
index,
|
|
71
|
+
bytes
|
|
72
|
+
}));
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
const file = await uploadFileViaXHR(url, token, formData, abortController.signal, (bytes)=>batcher.schedule(bytes));
|
|
76
|
+
batcher.cancel();
|
|
77
|
+
uploaded.push(file);
|
|
78
|
+
dispatch(setFileComplete({
|
|
79
|
+
index,
|
|
80
|
+
file
|
|
81
|
+
}));
|
|
82
|
+
} catch (err) {
|
|
83
|
+
batcher.cancel();
|
|
84
|
+
if (err instanceof UploadAbortedError) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
const message = err instanceof Error ? err.message : 'Upload failed';
|
|
88
|
+
dispatch(setFileError({
|
|
89
|
+
index,
|
|
90
|
+
name: fileName,
|
|
91
|
+
message
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
unregisterAbortController(uploadId);
|
|
96
|
+
return {
|
|
97
|
+
data: uploaded
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
/* -------------------------------------------------------------------------------------------------
|
|
101
|
+
* URL upload flow (SSE) — kept as-is for this iteration
|
|
102
|
+
* -----------------------------------------------------------------------------------------------*/ /**
|
|
56
103
|
* Parses a raw SSE text chunk into event/data pairs.
|
|
57
104
|
*
|
|
58
105
|
* SSE format:
|
|
@@ -81,28 +128,6 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
81
128
|
}
|
|
82
129
|
return events;
|
|
83
130
|
};
|
|
84
|
-
/**
|
|
85
|
-
* Makes a streaming upload request to the server.
|
|
86
|
-
*
|
|
87
|
-
* We use fetch directly instead of RTK Query's fetchBaseQuery because:
|
|
88
|
-
* 1. We need access to the raw Response to read the body as a stream
|
|
89
|
-
* 2. RTK Query's baseQuery awaits the full response and parses it as JSON,
|
|
90
|
-
* which doesn't work for Server-Sent Events (SSE) streaming
|
|
91
|
-
* 3. The stream must be read incrementally via response.body.getReader()
|
|
92
|
-
* to dispatch progress updates as files upload
|
|
93
|
-
*/ const fetchUploadStream = async ({ token, formData, signal })=>{
|
|
94
|
-
const backendURL = window.strapi.backendURL;
|
|
95
|
-
const headers = {};
|
|
96
|
-
if (token) {
|
|
97
|
-
headers.Authorization = `Bearer ${token}`;
|
|
98
|
-
}
|
|
99
|
-
return fetch(`${backendURL}/upload/unstable/stream`, {
|
|
100
|
-
method: 'POST',
|
|
101
|
-
headers,
|
|
102
|
-
body: formData,
|
|
103
|
-
signal
|
|
104
|
-
});
|
|
105
|
-
};
|
|
106
131
|
/**
|
|
107
132
|
* Makes a streaming upload-from-URLs request to the server.
|
|
108
133
|
* Sends URLs as JSON body instead of FormData.
|
|
@@ -125,15 +150,9 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
125
150
|
});
|
|
126
151
|
};
|
|
127
152
|
/**
|
|
128
|
-
* Processes an SSE stream from the upload endpoint.
|
|
153
|
+
* Processes an SSE stream from the URL upload endpoint.
|
|
129
154
|
* Dispatches Redux actions for each file event and returns the final result.
|
|
130
|
-
|
|
131
|
-
* @param options.response - The fetch Response object with SSE body
|
|
132
|
-
* @param options.dispatch - Redux dispatch function
|
|
133
|
-
* @param options.indexMapper - Optional function to map server indices to state indices (for retry)
|
|
134
|
-
* @param options.logPrefix - Optional prefix for console logs
|
|
135
|
-
* @returns The stream result or null if no files completed
|
|
136
|
-
*/ const processSSEStream = async ({ response, dispatch, indexMapper = (i)=>i })=>{
|
|
155
|
+
*/ const processSSEStream = async ({ response, dispatch })=>{
|
|
137
156
|
const reader = response.body.getReader();
|
|
138
157
|
const decoder = new TextDecoder();
|
|
139
158
|
let streamResult = null;
|
|
@@ -156,15 +175,14 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
156
175
|
const events = parseSSEEvents(completePart);
|
|
157
176
|
for (const { event, data } of events){
|
|
158
177
|
const parsed = JSON.parse(data);
|
|
159
|
-
const
|
|
178
|
+
const index = parsed.index;
|
|
160
179
|
switch(event){
|
|
161
180
|
case 'file:fetching':
|
|
162
181
|
{
|
|
163
182
|
// URL is being fetched server-side - mark as uploading (processing)
|
|
164
183
|
dispatch(setFileUploading({
|
|
165
184
|
name: parsed.url,
|
|
166
|
-
index
|
|
167
|
-
total: parsed.total,
|
|
185
|
+
index,
|
|
168
186
|
size: 0
|
|
169
187
|
}));
|
|
170
188
|
break;
|
|
@@ -174,8 +192,7 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
174
192
|
const payload = parsed;
|
|
175
193
|
dispatch(setFileUploading({
|
|
176
194
|
name: payload.name,
|
|
177
|
-
index
|
|
178
|
-
total: payload.total,
|
|
195
|
+
index,
|
|
179
196
|
size: payload.size
|
|
180
197
|
}));
|
|
181
198
|
break;
|
|
@@ -184,7 +201,7 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
184
201
|
{
|
|
185
202
|
const payload = parsed;
|
|
186
203
|
dispatch(setFileComplete({
|
|
187
|
-
index
|
|
204
|
+
index,
|
|
188
205
|
file: payload.file
|
|
189
206
|
}));
|
|
190
207
|
break;
|
|
@@ -193,7 +210,7 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
193
210
|
{
|
|
194
211
|
const payload = parsed;
|
|
195
212
|
dispatch(setFileError({
|
|
196
|
-
index
|
|
213
|
+
index,
|
|
197
214
|
name: payload.name,
|
|
198
215
|
message: payload.message
|
|
199
216
|
}));
|
|
@@ -215,80 +232,6 @@ import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
|
215
232
|
}
|
|
216
233
|
return streamResult;
|
|
217
234
|
};
|
|
218
|
-
/**
|
|
219
|
-
* Performs the actual streaming upload to the server.
|
|
220
|
-
* Shared by uploadFilesStream, retryCancelledFilesStream, and uploadFromUrls.
|
|
221
|
-
*
|
|
222
|
-
* @param options - Upload configuration
|
|
223
|
-
* @returns The upload result or error
|
|
224
|
-
*/ const performStreamUpload = async ({ token, formData, abortController, uploadId, dispatch, indexMapper = (i)=>i, onUploadFailed = (message)=>dispatch(setUploadFailed({
|
|
225
|
-
message
|
|
226
|
-
})) })=>{
|
|
227
|
-
try {
|
|
228
|
-
const response = await fetchUploadStream({
|
|
229
|
-
token,
|
|
230
|
-
formData,
|
|
231
|
-
signal: abortController.signal
|
|
232
|
-
});
|
|
233
|
-
if (!response.ok || !response.body) {
|
|
234
|
-
unregisterAbortController(uploadId);
|
|
235
|
-
let errorMessage = 'Upload request failed';
|
|
236
|
-
try {
|
|
237
|
-
const errorData = await response.json();
|
|
238
|
-
if (errorData.error?.message) {
|
|
239
|
-
errorMessage = errorData.error.message;
|
|
240
|
-
} else if (errorData.message) {
|
|
241
|
-
errorMessage = errorData.message;
|
|
242
|
-
}
|
|
243
|
-
} catch {
|
|
244
|
-
errorMessage = `Upload failed with status ${response.status}`;
|
|
245
|
-
}
|
|
246
|
-
onUploadFailed(errorMessage);
|
|
247
|
-
return {
|
|
248
|
-
error: {
|
|
249
|
-
name: 'UnknownError',
|
|
250
|
-
message: errorMessage,
|
|
251
|
-
status: response.status
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
const streamResult = await processSSEStream({
|
|
256
|
-
response,
|
|
257
|
-
dispatch,
|
|
258
|
-
indexMapper
|
|
259
|
-
});
|
|
260
|
-
unregisterAbortController(uploadId);
|
|
261
|
-
if (streamResult && streamResult.data.length > 0) {
|
|
262
|
-
return {
|
|
263
|
-
data: streamResult
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
data: {
|
|
268
|
-
data: [],
|
|
269
|
-
errors: []
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
} catch (err) {
|
|
273
|
-
unregisterAbortController(uploadId);
|
|
274
|
-
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
275
|
-
return {
|
|
276
|
-
error: {
|
|
277
|
-
name: 'UnknownError',
|
|
278
|
-
message: 'Upload cancelled'
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
const errorMessage = err instanceof Error ? err.message : 'Network error occurred';
|
|
283
|
-
onUploadFailed(errorMessage);
|
|
284
|
-
return {
|
|
285
|
-
error: {
|
|
286
|
-
name: 'UnknownError',
|
|
287
|
-
message: errorMessage
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
235
|
const uploadApi = adminApi.enhanceEndpoints({
|
|
293
236
|
addTagTypes: [
|
|
294
237
|
'Asset',
|
|
@@ -297,36 +240,45 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
297
240
|
}).injectEndpoints({
|
|
298
241
|
endpoints: (builder)=>({
|
|
299
242
|
/**
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*/
|
|
243
|
+
* Upload files sequentially, one request per file, to `/upload/unstable/upload-file`.
|
|
244
|
+
* Real per-file byte progress comes from `XHR.upload.onprogress`.
|
|
245
|
+
*/ uploadFiles: builder.mutation({
|
|
303
246
|
queryFn: async ({ formData, totalFiles }, { dispatch, getState })=>{
|
|
304
247
|
const token = getState().admin_app?.token;
|
|
305
|
-
// Extract
|
|
248
|
+
// Extract the original files and their per-file fileInfo from the combined FormData.
|
|
306
249
|
const files = formData.getAll('files');
|
|
307
250
|
const fileInfoJson = formData.get('fileInfo');
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
|
|
251
|
+
const fileInfoArray = JSON.parse(fileInfoJson);
|
|
252
|
+
const entries = files.map((file, index)=>({
|
|
253
|
+
file,
|
|
254
|
+
fileInfo: fileInfoArray[index] ?? {
|
|
255
|
+
name: file.name,
|
|
256
|
+
caption: null,
|
|
257
|
+
alternativeText: null,
|
|
258
|
+
folder: null
|
|
259
|
+
}
|
|
260
|
+
}));
|
|
261
|
+
const fileNames = entries.map((entry)=>entry.fileInfo.name ?? entry.file.name);
|
|
262
|
+
const fileSizes = entries.map((entry)=>entry.file.size);
|
|
311
263
|
// Open the progress dialog
|
|
312
264
|
dispatch(openUploadProgress({
|
|
313
265
|
totalFiles,
|
|
314
266
|
fileNames,
|
|
315
267
|
fileSizes
|
|
316
268
|
}));
|
|
317
|
-
dispatch(updateProgress(0));
|
|
318
269
|
// Get the uploadId from state after dispatching
|
|
319
270
|
const uploadId = getState().uploadProgress.uploadId;
|
|
320
|
-
// Store original
|
|
321
|
-
|
|
322
|
-
//
|
|
271
|
+
// Store original entries for retry functionality
|
|
272
|
+
registerUploadEntries(uploadId, entries);
|
|
273
|
+
// One AbortController per batch
|
|
323
274
|
const abortController = new AbortController();
|
|
324
275
|
registerAbortController(uploadId, abortController);
|
|
325
|
-
return
|
|
276
|
+
return runSequentialUpload({
|
|
277
|
+
entries,
|
|
278
|
+
indices: entries.map((_, index)=>index),
|
|
326
279
|
token,
|
|
327
|
-
formData,
|
|
328
|
-
abortController,
|
|
329
280
|
uploadId,
|
|
281
|
+
abortController,
|
|
330
282
|
dispatch
|
|
331
283
|
});
|
|
332
284
|
},
|
|
@@ -339,14 +291,14 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
339
291
|
}),
|
|
340
292
|
/**
|
|
341
293
|
* Retry uploading cancelled files.
|
|
342
|
-
*
|
|
343
|
-
|
|
294
|
+
* Maps cancelled rows back to their original entries and re-runs only those
|
|
295
|
+
* through the same sequential loop with a fresh AbortController.
|
|
296
|
+
*/ retryCancelledFiles: builder.mutation({
|
|
344
297
|
queryFn: async (_, { dispatch, getState })=>{
|
|
345
|
-
const token = getState().admin_app?.token;
|
|
346
298
|
const { uploadId, files: stateFiles } = getState().uploadProgress;
|
|
347
|
-
|
|
348
|
-
const
|
|
349
|
-
if (
|
|
299
|
+
const token = getState().admin_app?.token;
|
|
300
|
+
const cancelledIndices = stateFiles.filter((f)=>f.status === 'cancelled').map((f)=>f.index);
|
|
301
|
+
if (cancelledIndices.length === 0) {
|
|
350
302
|
return {
|
|
351
303
|
error: {
|
|
352
304
|
name: 'UnknownError',
|
|
@@ -354,9 +306,8 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
354
306
|
}
|
|
355
307
|
};
|
|
356
308
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (!originalFiles) {
|
|
309
|
+
const entries = getUploadEntries(uploadId);
|
|
310
|
+
if (!entries) {
|
|
360
311
|
return {
|
|
361
312
|
error: {
|
|
362
313
|
name: 'UnknownError',
|
|
@@ -364,44 +315,18 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
364
315
|
}
|
|
365
316
|
};
|
|
366
317
|
}
|
|
367
|
-
//
|
|
368
|
-
const indexMapping = cancelledFiles.map((f)=>f.index);
|
|
369
|
-
const filesToRetry = cancelledFiles.map((f)=>originalFiles[f.index]);
|
|
370
|
-
// Reset cancelled files to pending
|
|
318
|
+
// Reset cancelled files back to pending
|
|
371
319
|
dispatch(retryCancelledFiles());
|
|
372
|
-
//
|
|
373
|
-
const formData = new FormData();
|
|
374
|
-
const fileInfoArray = filesToRetry.map((file)=>({
|
|
375
|
-
name: file.name,
|
|
376
|
-
caption: null,
|
|
377
|
-
alternativeText: null,
|
|
378
|
-
folder: null
|
|
379
|
-
}));
|
|
380
|
-
filesToRetry.forEach((file)=>{
|
|
381
|
-
formData.append('files', file);
|
|
382
|
-
});
|
|
383
|
-
formData.append('fileInfo', JSON.stringify(fileInfoArray));
|
|
384
|
-
// Create abort controller for this retry
|
|
320
|
+
// Fresh AbortController for the retry run
|
|
385
321
|
const abortController = new AbortController();
|
|
386
322
|
registerAbortController(uploadId, abortController);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
dispatch(setFileError({
|
|
391
|
-
index: originalIndex,
|
|
392
|
-
name: stateFiles[originalIndex].name,
|
|
393
|
-
message
|
|
394
|
-
}));
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
return performStreamUpload({
|
|
323
|
+
return runSequentialUpload({
|
|
324
|
+
entries,
|
|
325
|
+
indices: cancelledIndices,
|
|
398
326
|
token,
|
|
399
|
-
formData,
|
|
400
|
-
abortController,
|
|
401
327
|
uploadId,
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
onUploadFailed
|
|
328
|
+
abortController,
|
|
329
|
+
dispatch
|
|
405
330
|
});
|
|
406
331
|
},
|
|
407
332
|
invalidatesTags: [
|
|
@@ -413,7 +338,7 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
413
338
|
}),
|
|
414
339
|
/**
|
|
415
340
|
* Upload files from URLs.
|
|
416
|
-
* Sends URLs to the server which fetches and uploads them.
|
|
341
|
+
* Sends URLs to the server which fetches and uploads them (SSE flow, unchanged).
|
|
417
342
|
*/ uploadFromUrls: builder.mutation({
|
|
418
343
|
queryFn: async ({ urls, folderId }, { dispatch, getState })=>{
|
|
419
344
|
const token = getState().admin_app?.token;
|
|
@@ -424,7 +349,6 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
424
349
|
totalFiles: urls.length,
|
|
425
350
|
fileNames
|
|
426
351
|
}));
|
|
427
|
-
dispatch(updateProgress(0));
|
|
428
352
|
// Get the uploadId from state after dispatching
|
|
429
353
|
const uploadId = getState().uploadProgress.uploadId;
|
|
430
354
|
// Create abort controller for this upload
|
|
@@ -510,7 +434,7 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
510
434
|
})
|
|
511
435
|
})
|
|
512
436
|
});
|
|
513
|
-
const {
|
|
437
|
+
const { useUploadFilesMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation } = uploadApi;
|
|
514
438
|
|
|
515
|
-
export { abortUpload, uploadApi,
|
|
439
|
+
export { abortUpload, uploadApi, useRetryCancelledFilesMutation, useUploadFilesMutation, useUploadFromUrlsMutation };
|
|
516
440
|
//# sourceMappingURL=api.mjs.map
|
|
@@ -1 +1 @@
|
|
|
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';\nimport { getFilenameFromUrl } from '../utils/files';\n\nimport type {\n CreateFilesStream,\n CreateFilesStreamEvents,\n} from '../../../../shared/contracts/files';\n\ninterface UploadFilesArgs {\n formData: FormData;\n totalFiles: number;\n}\n\ninterface UploadFromUrlsArgs {\n urls: string[];\n folderId: number | null;\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 * Makes a streaming upload-from-URLs request to the server.\n * Sends URLs as JSON body instead of FormData.\n */\nconst fetchUrlUploadStream = async ({\n token,\n urls,\n folderId,\n signal,\n}: {\n token: string | null | undefined;\n urls: string[];\n folderId: number | null;\n signal: AbortSignal;\n}): Promise<Response> => {\n const backendURL = window.strapi.backendURL;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n return fetch(`${backendURL}/upload/unstable/stream-from-urls`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ urls, folderId }),\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:fetching': {\n // URL is being fetched server-side - mark as uploading (processing)\n dispatch(\n setFileUploading({\n name: parsed.url as string,\n index: mappedIndex,\n total: parsed.total as number,\n size: 0,\n })\n );\n break;\n }\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\n/**\n * Options for performing a streaming upload.\n */\ninterface PerformStreamUploadOptions {\n token: string | null | undefined;\n formData: FormData;\n abortController: AbortController;\n uploadId: number;\n dispatch: Dispatch;\n indexMapper?: (serverIndex: number) => number;\n onUploadFailed?: (message: string) => void;\n}\n\n/**\n * Error shape returned by upload operations.\n */\ninterface UploadError {\n name: 'UnknownError';\n message: string;\n status?: number;\n}\n\n/**\n * Result of a streaming upload operation.\n * Matches RTK Query's expected return type for queryFn.\n */\ntype UploadResult =\n | { data: CreateFilesStream.Response; error?: undefined }\n | { error: UploadError; data?: undefined };\n\n/**\n * Performs the actual streaming upload to the server.\n * Shared by uploadFilesStream, retryCancelledFilesStream, and uploadFromUrls.\n *\n * @param options - Upload configuration\n * @returns The upload result or error\n */\nconst performStreamUpload = async ({\n token,\n formData,\n abortController,\n uploadId,\n dispatch,\n indexMapper = (i) => i,\n onUploadFailed = (message) => dispatch(setUploadFailed({ message })),\n}: PerformStreamUploadOptions): Promise<UploadResult> => {\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 = '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 errorMessage = `Upload failed with status ${response.status}`;\n }\n\n onUploadFailed(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 indexMapper,\n });\n\n unregisterAbortController(uploadId);\n\n if (streamResult && streamResult.data.length > 0) {\n return { data: streamResult };\n }\n\n return { data: { data: [], errors: [] } };\n } catch (err) {\n unregisterAbortController(uploadId);\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { error: { name: 'UnknownError', message: 'Upload cancelled' } };\n }\n\n const errorMessage = err instanceof Error ? err.message : 'Network error occurred';\n onUploadFailed(errorMessage);\n\n return {\n error: {\n name: 'UnknownError',\n message: errorMessage,\n },\n };\n }\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\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 return performStreamUpload({\n token,\n formData,\n abortController,\n uploadId,\n dispatch,\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 // Custom error handler: mark individual retried files as failed\n const onUploadFailed = (message: string) => {\n for (const originalIndex of indexMapping) {\n dispatch(\n setFileError({\n index: originalIndex,\n name: stateFiles[originalIndex].name,\n message,\n })\n );\n }\n };\n\n return performStreamUpload({\n token,\n formData,\n abortController,\n uploadId,\n dispatch,\n indexMapper: (serverIndex) => indexMapping[serverIndex],\n onUploadFailed,\n });\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n\n /**\n * Upload files from URLs.\n * Sends URLs to the server which fetches and uploads them.\n */\n uploadFromUrls: builder.mutation<CreateFilesStream.Response, UploadFromUrlsArgs>({\n queryFn: async ({ urls, folderId }, { dispatch, getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n\n // Extract filenames from URLs for the progress dialog\n const fileNames = urls.map((url) => getFilenameFromUrl(url));\n\n // Open progress dialog with all URLs as pending files\n dispatch(\n openUploadProgress({\n totalFiles: urls.length,\n fileNames,\n })\n );\n dispatch(updateProgress(0));\n\n // Get the uploadId from state after dispatching\n const uploadId = (getState() as RootState).uploadProgress.uploadId;\n\n // Create abort controller for this upload\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n try {\n // Send URLs to server for fetching and uploading\n const response = await fetchUrlUploadStream({\n token,\n urls,\n folderId,\n signal: abortController.signal,\n });\n\n if (!response.ok || !response.body) {\n unregisterAbortController(uploadId);\n\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 errorMessage = `Upload failed with status ${response.status}`;\n }\n\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError' as const,\n message: errorMessage,\n status: response.status,\n },\n };\n }\n\n // Process SSE stream from server\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 return { data: { data: [], errors: [] } };\n } catch (err) {\n unregisterAbortController(uploadId);\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { error: { name: 'UnknownError' as const, message: 'Upload cancelled' } };\n }\n\n const errorMessage = err instanceof Error ? err.message : 'Network error occurred';\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError' as const,\n message: errorMessage,\n },\n };\n }\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n }),\n });\n\nexport const {\n useUploadFilesStreamMutation,\n useRetryCancelledFilesStreamMutation,\n useUploadFromUrlsMutation,\n} = 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","fetchUrlUploadStream","urls","folderId","JSON","stringify","processSSEStream","response","dispatch","indexMapper","i","reader","getReader","decoder","TextDecoder","streamResult","buffer","done","value","read","decode","stream","lastDoubleNewline","lastIndexOf","completePart","parsed","parse","mappedIndex","index","setFileUploading","name","url","total","size","payload","setFileComplete","file","setFileError","message","errors","console","error","performStreamUpload","abortController","onUploadFailed","setUploadFailed","ok","errorMessage","errorData","json","status","length","err","DOMException","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","invalidatesTags","type","id","retryCancelledFilesStream","_","stateFiles","cancelledFiles","f","originalFiles","indexMapping","filesToRetry","retryCancelledFiles","FormData","fileInfoArray","caption","alternativeText","folder","forEach","append","originalIndex","serverIndex","uploadFromUrls","getFilenameFromUrl","useUploadFilesStreamMutation","useRetryCancelledFilesStreamMutation","useUploadFromUrlsMutation"],"mappings":"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"}
|
|
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 setFileProgress,\n setFileComplete,\n setFileError,\n setUploadFailed,\n retryCancelledFiles,\n} from '../store/uploadProgress';\nimport { createRafBatcher } from '../utils/createRafBatcher';\nimport { getFilenameFromUrl } from '../utils/files';\n\nimport { uploadFileViaXHR, UploadAbortedError } from './uploadFileViaXHR';\n\nimport type {\n CreateFilesStream,\n CreateFilesStreamEvents,\n File as UploadedFile,\n UploadFileInfo,\n} from '../../../../shared/contracts/files';\n\ninterface UploadFilesArgs {\n formData: FormData;\n totalFiles: number;\n}\n\ninterface UploadFromUrlsArgs {\n urls: string[];\n folderId: number | null;\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 * A single file plus the `fileInfo` it was queued with.\n * Retained in {@link uploadRegistry} so cancelled files can be retried without\n * the user re-selecting them. `File`/`AbortController` are non-serializable, so\n * they live here rather than in Redux.\n */\ninterface UploadEntry {\n file: File;\n fileInfo: UploadFileInfo;\n}\n\n/**\n * Stores the original upload entries for a batch (keyed by uploadId) to enable retry.\n */\nconst uploadRegistry = new Map<number, UploadEntry[]>();\n\nconst registerUploadEntries = (uploadId: number, entries: UploadEntry[]) => {\n uploadRegistry.set(uploadId, entries);\n};\n\nconst getUploadEntries = (uploadId: number): UploadEntry[] | undefined => {\n return uploadRegistry.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 * Redux state cannot store function references (abort controllers), RTK Query's\n * signal is only accessible within the queryFn, and the upload is triggered in\n * AssetsPage but cancelled from UploadProgressDialog.\n */\nconst abortControllers = new Map<number, AbortController>();\n\nconst registerAbortController = (uploadId: number, controller: AbortController) => {\n abortControllers.set(uploadId, controller);\n};\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 * Error shape returned by upload operations.\n * Matches RTK Query's expected return type for queryFn.\n */\ninterface UploadError {\n name: 'UnknownError';\n message: string;\n status?: number;\n}\n\ntype SequentialUploadResult =\n | { data: UploadedFile[]; error?: undefined }\n | { error: UploadError; data?: undefined };\n\n/**\n * Uploads the given entry indices one at a time through the single-file endpoint.\n *\n * For each file: dispatches \"uploading\", wires `XHR.upload.onprogress` through a\n * per-frame batcher to `setFileProgress`, awaits the XHR, then dispatches\n * \"complete\" or \"error\". Each file is wrapped in its own try/catch so one failure\n * does not stop the batch. An abort stops the loop without starting further files.\n */\nconst runSequentialUpload = async ({\n entries,\n indices,\n token,\n uploadId,\n abortController,\n dispatch,\n}: {\n entries: UploadEntry[];\n indices: number[];\n token: string | null | undefined;\n uploadId: number;\n abortController: AbortController;\n dispatch: Dispatch;\n}): Promise<SequentialUploadResult> => {\n const url = `${window.strapi.backendURL}/upload/unstable/upload-file`;\n const uploaded: UploadedFile[] = [];\n\n for (const index of indices) {\n if (abortController.signal.aborted) {\n break;\n }\n\n const entry = entries[index];\n if (!entry) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n const fileName = entry.fileInfo?.name ?? entry.file.name;\n\n dispatch(setFileUploading({ name: fileName, index, size: entry.file.size }));\n\n const formData = new FormData();\n formData.append('files', entry.file);\n formData.append('fileInfo', JSON.stringify(entry.fileInfo));\n\n // Coalesce high-frequency progress events into one dispatch per frame.\n const batcher = createRafBatcher<number>((bytes) => {\n dispatch(setFileProgress({ index, bytes }));\n });\n\n try {\n const file = await uploadFileViaXHR(url, token, formData, abortController.signal, (bytes) =>\n batcher.schedule(bytes)\n );\n batcher.cancel();\n uploaded.push(file);\n dispatch(setFileComplete({ index, file }));\n } catch (err) {\n batcher.cancel();\n\n if (err instanceof UploadAbortedError) {\n // Batch was cancelled — stop without starting further files.\n // cancelUpload (dispatched from the dialog) marks the remaining rows.\n break;\n }\n\n const message = err instanceof Error ? err.message : 'Upload failed';\n dispatch(setFileError({ index, name: fileName, message }));\n }\n }\n\n unregisterAbortController(uploadId);\n\n return { data: uploaded };\n};\n\n/* -------------------------------------------------------------------------------------------------\n * URL upload flow (SSE) — kept as-is for this iteration\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-from-URLs request to the server.\n * Sends URLs as JSON body instead of FormData.\n */\nconst fetchUrlUploadStream = async ({\n token,\n urls,\n folderId,\n signal,\n}: {\n token: string | null | undefined;\n urls: string[];\n folderId: number | null;\n signal: AbortSignal;\n}): Promise<Response> => {\n const backendURL = window.strapi.backendURL;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n\n return fetch(`${backendURL}/upload/unstable/stream-from-urls`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ urls, folderId }),\n signal,\n });\n};\n\n/**\n * Processes an SSE stream from the URL upload endpoint.\n * Dispatches Redux actions for each file event and returns the final result.\n */\nconst processSSEStream = async ({\n response,\n dispatch,\n}: {\n response: Response;\n dispatch: Dispatch;\n}): 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 index = parsed.index as number;\n\n switch (event) {\n case 'file:fetching': {\n // URL is being fetched server-side - mark as uploading (processing)\n dispatch(setFileUploading({ name: parsed.url as string, index, size: 0 }));\n break;\n }\n case 'file:uploading': {\n const payload = parsed as CreateFilesStreamEvents.FileUploadingEvent;\n dispatch(setFileUploading({ name: payload.name, index, size: payload.size }));\n break;\n }\n case 'file:complete': {\n const payload = parsed as CreateFilesStreamEvents.FileCompleteEvent;\n dispatch(setFileComplete({ index, file: payload.file }));\n break;\n }\n case 'file:error': {\n const payload = parsed as CreateFilesStreamEvents.FileErrorEvent;\n dispatch(setFileError({ index, name: payload.name, message: payload.message }));\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 * Upload files sequentially, one request per file, to `/upload/unstable/upload-file`.\n * Real per-file byte progress comes from `XHR.upload.onprogress`.\n */\n uploadFiles: builder.mutation<UploadedFile[], UploadFilesArgs>({\n queryFn: async ({ formData, totalFiles }, { dispatch, getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n\n // Extract the original files and their per-file fileInfo from the combined FormData.\n const files = formData.getAll('files') as File[];\n const fileInfoJson = formData.get('fileInfo') as string;\n const fileInfoArray = JSON.parse(fileInfoJson) as UploadFileInfo[];\n\n const entries: UploadEntry[] = files.map((file, index) => ({\n file,\n fileInfo: fileInfoArray[index] ?? {\n name: file.name,\n caption: null,\n alternativeText: null,\n folder: null,\n },\n }));\n\n const fileNames = entries.map((entry) => entry.fileInfo.name ?? entry.file.name);\n const fileSizes = entries.map((entry) => entry.file.size);\n\n // Open the progress dialog\n dispatch(openUploadProgress({ totalFiles, fileNames, fileSizes }));\n\n // Get the uploadId from state after dispatching\n const uploadId = (getState() as RootState).uploadProgress.uploadId;\n\n // Store original entries for retry functionality\n registerUploadEntries(uploadId, entries);\n\n // One AbortController per batch\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n return runSequentialUpload({\n entries,\n indices: entries.map((_, index) => index),\n token,\n uploadId,\n abortController,\n dispatch,\n });\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n\n /**\n * Retry uploading cancelled files.\n * Maps cancelled rows back to their original entries and re-runs only those\n * through the same sequential loop with a fresh AbortController.\n */\n retryCancelledFiles: builder.mutation<UploadedFile[], void>({\n queryFn: async (_, { dispatch, getState }) => {\n const { uploadId, files: stateFiles } = (getState() as RootState).uploadProgress;\n const token = (getState() as RootState).admin_app?.token;\n\n const cancelledIndices = stateFiles\n .filter((f) => f.status === 'cancelled')\n .map((f) => f.index);\n\n if (cancelledIndices.length === 0) {\n return { error: { name: 'UnknownError', message: 'No cancelled files to retry' } };\n }\n\n const entries = getUploadEntries(uploadId);\n if (!entries) {\n return { error: { name: 'UnknownError', message: 'Original files not found' } };\n }\n\n // Reset cancelled files back to pending\n dispatch(retryCancelledFiles());\n\n // Fresh AbortController for the retry run\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n return runSequentialUpload({\n entries,\n indices: cancelledIndices,\n token,\n uploadId,\n abortController,\n dispatch,\n });\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n\n /**\n * Upload files from URLs.\n * Sends URLs to the server which fetches and uploads them (SSE flow, unchanged).\n */\n uploadFromUrls: builder.mutation<CreateFilesStream.Response, UploadFromUrlsArgs>({\n queryFn: async ({ urls, folderId }, { dispatch, getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n\n // Extract filenames from URLs for the progress dialog\n const fileNames = urls.map((url) => getFilenameFromUrl(url));\n\n // Open progress dialog with all URLs as pending files\n dispatch(\n openUploadProgress({\n totalFiles: urls.length,\n fileNames,\n })\n );\n\n // Get the uploadId from state after dispatching\n const uploadId = (getState() as RootState).uploadProgress.uploadId;\n\n // Create abort controller for this upload\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n try {\n // Send URLs to server for fetching and uploading\n const response = await fetchUrlUploadStream({\n token,\n urls,\n folderId,\n signal: abortController.signal,\n });\n\n if (!response.ok || !response.body) {\n unregisterAbortController(uploadId);\n\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 errorMessage = `Upload failed with status ${response.status}`;\n }\n\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError' as const,\n message: errorMessage,\n status: response.status,\n },\n };\n }\n\n // Process SSE stream from server\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 return { data: { data: [], errors: [] } };\n } catch (err) {\n unregisterAbortController(uploadId);\n\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { error: { name: 'UnknownError' as const, message: 'Upload cancelled' } };\n }\n\n const errorMessage = err instanceof Error ? err.message : 'Network error occurred';\n dispatch(setUploadFailed({ message: errorMessage }));\n\n return {\n error: {\n name: 'UnknownError' as const,\n message: errorMessage,\n },\n };\n }\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n }),\n });\n\nexport const { useUploadFilesMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation } =\n uploadApi;\nexport { uploadApi };\n"],"names":["uploadRegistry","Map","registerUploadEntries","uploadId","entries","set","getUploadEntries","get","abortControllers","registerAbortController","controller","unregisterAbortController","delete","abortUpload","abort","runSequentialUpload","indices","token","abortController","dispatch","url","window","strapi","backendURL","uploaded","index","signal","aborted","entry","fileName","fileInfo","name","file","setFileUploading","size","formData","FormData","append","JSON","stringify","batcher","createRafBatcher","bytes","setFileProgress","uploadFileViaXHR","schedule","cancel","push","setFileComplete","err","UploadAbortedError","message","Error","setFileError","data","parseSSEEvents","chunk","events","blocks","split","filter","Boolean","block","event","line","startsWith","slice","fetchUrlUploadStream","urls","folderId","headers","Authorization","fetch","method","body","processSSEStream","response","reader","getReader","decoder","TextDecoder","streamResult","buffer","done","value","read","decode","stream","lastDoubleNewline","lastIndexOf","completePart","parsed","parse","payload","errors","console","error","uploadApi","adminApi","enhanceEndpoints","addTagTypes","injectEndpoints","endpoints","builder","uploadFiles","mutation","queryFn","totalFiles","getState","admin_app","files","getAll","fileInfoJson","fileInfoArray","map","caption","alternativeText","folder","fileNames","fileSizes","openUploadProgress","uploadProgress","AbortController","_","invalidatesTags","type","id","retryCancelledFiles","stateFiles","cancelledIndices","f","status","length","uploadFromUrls","getFilenameFromUrl","ok","errorMessage","errorData","json","setUploadFailed","DOMException","useUploadFilesMutation","useRetryCancelledFilesMutation","useUploadFromUrlsMutation"],"mappings":"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"}
|