@strapi/upload 5.51.1 → 5.51.2
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/AssetCard/VideoPreview.js +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.js.map +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs.map +1 -1
- package/dist/admin/components/TableList/TableList.js +1 -1
- package/dist/admin/components/TableList/TableList.js.map +1 -1
- package/dist/admin/components/TableList/TableList.mjs +1 -1
- package/dist/admin/components/TableList/TableList.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
- package/dist/admin/future/components/Drawer.js +3 -1
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +3 -1
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +200 -27
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +203 -30
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js +35 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js.map +1 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs +33 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +203 -78
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +200 -75
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +46 -19
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +47 -20
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +5 -2
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +5 -2
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +100 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +98 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +10 -4
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +10 -4
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +140 -22
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +142 -24
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js +193 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs +191 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +18 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +18 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.js +62 -3
- package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs +62 -4
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.js +408 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +406 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +273 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +267 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +76 -19
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +78 -21
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +33 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +31 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js +21 -32
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +22 -33
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js +40 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs +38 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +21 -5
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +21 -5
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js +168 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +162 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js +201 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs +198 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js +2 -2
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -1
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +2 -2
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -1
- package/dist/admin/future/services/api.js +163 -28
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +163 -30
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +29 -17
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +29 -17
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +29 -17
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +29 -17
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/settings.js +10 -3
- package/dist/admin/future/services/settings.js.map +1 -1
- package/dist/admin/future/services/settings.mjs +10 -3
- package/dist/admin/future/services/settings.mjs.map +1 -1
- package/dist/admin/future/store/uploadProgress.js +109 -2
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +105 -3
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/future/utils/searchQueryParam.js +11 -0
- package/dist/admin/future/utils/searchQueryParam.js.map +1 -0
- package/dist/admin/future/utils/searchQueryParam.mjs +9 -0
- package/dist/admin/future/utils/searchQueryParam.mjs.map +1 -0
- package/dist/admin/hooks/useSelectionState.js +2 -4
- package/dist/admin/hooks/useSelectionState.js.map +1 -1
- package/dist/admin/hooks/useSelectionState.mjs +2 -4
- package/dist/admin/hooks/useSelectionState.mjs.map +1 -1
- package/dist/admin/shared/constants.js +43 -0
- package/dist/admin/shared/constants.js.map +1 -0
- package/dist/admin/shared/constants.mjs +38 -0
- package/dist/admin/shared/constants.mjs.map +1 -0
- package/dist/admin/src/future/hooks/useMediaLibraryPermissions.d.ts +21 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +14 -1
- package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -0
- package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZoneContext.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +18 -1
- package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +22 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
- package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSearch.d.ts +12 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +13 -3
- package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
- package/dist/admin/src/future/pages/Assets/utils/buildAssetFilters.d.ts +46 -0
- package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +3 -4
- package/dist/admin/src/future/services/api.d.ts +53 -5
- package/dist/admin/src/future/services/assets.d.ts +9 -1
- package/dist/admin/src/future/services/folders.d.ts +3 -0
- package/dist/admin/src/future/services/settings.d.ts +2 -2
- package/dist/admin/src/future/store/uploadProgress.d.ts +83 -1
- package/dist/admin/src/future/utils/searchQueryParam.d.ts +7 -0
- package/dist/admin/src/hooks/useAiAvailability.d.ts +1 -1
- package/dist/admin/src/hooks/useEditAsset.d.ts +4 -4
- package/dist/admin/src/hooks/useEditFolder.d.ts +4 -4
- package/dist/admin/src/hooks/useSettings.d.ts +1 -0
- package/dist/admin/src/hooks/useTracking.d.ts +1 -1
- package/dist/admin/src/hooks/useUpload.d.ts +1 -1
- package/dist/admin/translations/en.json.js +53 -3
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +53 -3
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/urlYupSchema.js +33 -9
- package/dist/admin/utils/urlYupSchema.js.map +1 -1
- package/dist/admin/utils/urlYupSchema.mjs +33 -9
- package/dist/admin/utils/urlYupSchema.mjs.map +1 -1
- package/dist/server/config.js +13 -5
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +13 -5
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/constants.js.map +1 -1
- package/dist/server/constants.mjs.map +1 -1
- package/dist/server/controllers/admin-file.js +30 -0
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +30 -0
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-settings.js +20 -2
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +21 -3
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +1 -2
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +2 -2
- package/dist/server/controllers/content-api.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/ai-metadata.js +12 -0
- package/dist/server/controllers/validation/admin/ai-metadata.js.map +1 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs +10 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs.map +1 -0
- package/dist/server/controllers/validation/admin/settings.js +4 -1
- package/dist/server/controllers/validation/admin/settings.js.map +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs.map +1 -1
- package/dist/server/routes/admin.js +18 -0
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +18 -0
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata.js +108 -11
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +108 -11
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/file.js +86 -13
- package/dist/server/services/file.js.map +1 -1
- package/dist/server/services/file.mjs +87 -14
- package/dist/server/services/file.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +4 -4
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +4 -4
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/shared/constants.js +43 -0
- package/dist/server/shared/constants.js.map +1 -0
- package/dist/server/shared/constants.mjs +38 -0
- package/dist/server/shared/constants.mjs.map +1 -0
- package/dist/server/src/config.d.ts +2 -0
- package/dist/server/src/config.d.ts.map +1 -1
- package/dist/server/src/constants.d.ts +2 -1
- package/dist/server/src/constants.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-file.d.ts +10 -0
- package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +1 -0
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts +2 -0
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/admin/settings.d.ts +1 -1
- package/dist/server/src/controllers/validation/admin/settings.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +7 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/routes/admin.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata.d.ts +19 -0
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/file.d.ts +12 -2
- package/dist/server/src/services/file.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +4 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/types.d.ts +3 -0
- package/dist/server/src/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +35 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/contracts/files.d.ts +25 -0
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/dist/shared/contracts/settings.d.ts +7 -0
- package/package.json +7 -7
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { adminApi } from '@strapi/admin/strapi-admin';
|
|
2
|
-
import { openUploadProgress, setUploadFailed, retryCancelledFiles, setFileError, setFileComplete, setFileUploading, setFileProgress } from '../store/uploadProgress.mjs';
|
|
2
|
+
import { openUploadProgress, setUploadFailed, retryCancelledFiles, setFileError, setFileComplete, setFileUploading, setFileMetadataGenerating, setFileMetadataResult, setFileProgress } from '../store/uploadProgress.mjs';
|
|
3
3
|
import { createRafBatcher } from '../utils/createRafBatcher.mjs';
|
|
4
4
|
import { getFilenameFromUrl } from '../utils/files.mjs';
|
|
5
5
|
import { uploadFileViaXHR, UploadAbortedError } from './uploadFileViaXHR.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Stores the original upload
|
|
8
|
+
* Stores the original upload batch (keyed by uploadId) to enable retry.
|
|
9
9
|
*/ const uploadRegistry = new Map();
|
|
10
|
-
const registerUploadEntries = (uploadId, entries)=>{
|
|
11
|
-
uploadRegistry.set(uploadId,
|
|
10
|
+
const registerUploadEntries = (uploadId, entries, generateAiMetadata, concurrency)=>{
|
|
11
|
+
uploadRegistry.set(uploadId, {
|
|
12
|
+
entries,
|
|
13
|
+
generateAiMetadata,
|
|
14
|
+
concurrency
|
|
15
|
+
});
|
|
12
16
|
};
|
|
13
17
|
const getUploadEntries = (uploadId)=>{
|
|
14
18
|
return uploadRegistry.get(uploadId);
|
|
@@ -37,6 +41,54 @@ const unregisterAbortController = (uploadId)=>{
|
|
|
37
41
|
unregisterAbortController(uploadId);
|
|
38
42
|
}
|
|
39
43
|
};
|
|
44
|
+
/** Maps a server per-file outcome onto the row's terminal metadata status. */ const METADATA_STATUS_BY_RESULT = {
|
|
45
|
+
success: 'generated',
|
|
46
|
+
skipped: 'skipped',
|
|
47
|
+
error: 'failed'
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Kicks off AI metadata generation for a freshly uploaded file.
|
|
51
|
+
*
|
|
52
|
+
* Deliberately fire-and-forget — the caller must NOT await it:
|
|
53
|
+
* - generation for file N overlaps the upload of file N+1;
|
|
54
|
+
* - a generation failure can never affect the upload result (the upload already
|
|
55
|
+
* succeeded), which is why the promise is fully swallowed here.
|
|
56
|
+
*
|
|
57
|
+
* Every uploaded file is sent, including non-images: the server already classifies
|
|
58
|
+
* them (`ai-metadata.generateForFiles` returns `skipped` for anything whose mime
|
|
59
|
+
* isn't `image/*`) and duplicating that rule here would let the two drift apart.
|
|
60
|
+
* The upshot is that a non-image now reports "Metadata generation skipped" rather
|
|
61
|
+
* than silently showing no metadata state at all.
|
|
62
|
+
*/ const maybeGenerateMetadata = ({ file, index, uploadId, enabled, dispatch })=>{
|
|
63
|
+
if (!enabled || typeof file.id !== 'number') {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
dispatch(setFileMetadataGenerating({
|
|
67
|
+
index,
|
|
68
|
+
uploadId
|
|
69
|
+
}));
|
|
70
|
+
// `uploadApi` is declared below but only read at call time, long after the module
|
|
71
|
+
// has finished evaluating.
|
|
72
|
+
const promise = dispatch(uploadApi.endpoints.generateAiMetadata.initiate({
|
|
73
|
+
fileIds: [
|
|
74
|
+
file.id
|
|
75
|
+
]
|
|
76
|
+
}));
|
|
77
|
+
promise.unwrap().then((results)=>{
|
|
78
|
+
const result = results?.find((r)=>r.id === file.id) ?? results?.[0];
|
|
79
|
+
dispatch(setFileMetadataResult({
|
|
80
|
+
index,
|
|
81
|
+
uploadId,
|
|
82
|
+
status: result ? METADATA_STATUS_BY_RESULT[result.status] : 'failed'
|
|
83
|
+
}));
|
|
84
|
+
}).catch(()=>{
|
|
85
|
+
dispatch(setFileMetadataResult({
|
|
86
|
+
index,
|
|
87
|
+
uploadId,
|
|
88
|
+
status: 'failed'
|
|
89
|
+
}));
|
|
90
|
+
});
|
|
91
|
+
};
|
|
40
92
|
/**
|
|
41
93
|
* Uploads the given entry indices one at a time through the single-file endpoint.
|
|
42
94
|
*
|
|
@@ -44,16 +96,19 @@ const unregisterAbortController = (uploadId)=>{
|
|
|
44
96
|
* per-frame batcher to `setFileProgress`, awaits the XHR, then dispatches
|
|
45
97
|
* "complete" or "error". Each file is wrapped in its own try/catch so one failure
|
|
46
98
|
* does not stop the batch. An abort stops the loop without starting further files.
|
|
47
|
-
*/ const
|
|
99
|
+
*/ const runUploadPool = async ({ entries, indices, token, uploadId, abortController, dispatch, concurrency = 1, generateAiMetadata })=>{
|
|
48
100
|
const url = `${window.strapi.backendURL}/upload/unstable/upload-file`;
|
|
49
101
|
const uploaded = [];
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
102
|
+
// Shared queue: each worker shifts the next index when it frees up, so N
|
|
103
|
+
// files are in flight at any moment (not N-sized waves). With concurrency 1
|
|
104
|
+
// this degenerates to the original strictly sequential loop.
|
|
105
|
+
const queue = [
|
|
106
|
+
...indices
|
|
107
|
+
];
|
|
108
|
+
const uploadOne = async (index)=>{
|
|
54
109
|
const entry = entries[index];
|
|
55
110
|
if (!entry) {
|
|
56
|
-
|
|
111
|
+
return;
|
|
57
112
|
}
|
|
58
113
|
const fileName = entry.fileInfo?.name ?? entry.file.name;
|
|
59
114
|
dispatch(setFileUploading({
|
|
@@ -79,10 +134,20 @@ const unregisterAbortController = (uploadId)=>{
|
|
|
79
134
|
index,
|
|
80
135
|
file
|
|
81
136
|
}));
|
|
137
|
+
// Not awaited: overlaps with the next file's upload and can't fail the batch.
|
|
138
|
+
maybeGenerateMetadata({
|
|
139
|
+
file,
|
|
140
|
+
index,
|
|
141
|
+
uploadId,
|
|
142
|
+
enabled: generateAiMetadata,
|
|
143
|
+
dispatch
|
|
144
|
+
});
|
|
82
145
|
} catch (err) {
|
|
83
146
|
batcher.cancel();
|
|
84
147
|
if (err instanceof UploadAbortedError) {
|
|
85
|
-
|
|
148
|
+
// Batch was cancelled — the worker loop checks the signal and stops.
|
|
149
|
+
// cancelUpload (dispatched from the dialog) marks the remaining rows.
|
|
150
|
+
return;
|
|
86
151
|
}
|
|
87
152
|
const message = err instanceof Error ? err.message : 'Upload failed';
|
|
88
153
|
dispatch(setFileError({
|
|
@@ -91,7 +156,28 @@ const unregisterAbortController = (uploadId)=>{
|
|
|
91
156
|
message
|
|
92
157
|
}));
|
|
93
158
|
}
|
|
94
|
-
}
|
|
159
|
+
};
|
|
160
|
+
// Guard non-finite/invalid values (NaN, Infinity, <1): `Math.floor(NaN)`
|
|
161
|
+
// would propagate through Math.max to `Array.from({ length: NaN })` = zero
|
|
162
|
+
// workers, silently uploading nothing. Anything invalid falls back to 1.
|
|
163
|
+
const requested = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;
|
|
164
|
+
const workerCount = Math.min(Math.max(1, requested), queue.length);
|
|
165
|
+
const worker = async ()=>{
|
|
166
|
+
while(queue.length > 0){
|
|
167
|
+
if (abortController.signal.aborted) {
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
const index = queue.shift();
|
|
171
|
+
if (index === undefined) {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
// eslint-disable-next-line no-await-in-loop
|
|
175
|
+
await uploadOne(index);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
await Promise.all(Array.from({
|
|
179
|
+
length: workerCount
|
|
180
|
+
}, ()=>worker()));
|
|
95
181
|
unregisterAbortController(uploadId);
|
|
96
182
|
return {
|
|
97
183
|
data: uploaded
|
|
@@ -152,7 +238,7 @@ const unregisterAbortController = (uploadId)=>{
|
|
|
152
238
|
/**
|
|
153
239
|
* Processes an SSE stream from the URL upload endpoint.
|
|
154
240
|
* Dispatches Redux actions for each file event and returns the final result.
|
|
155
|
-
*/ const processSSEStream = async ({ response, dispatch })=>{
|
|
241
|
+
*/ const processSSEStream = async ({ response, dispatch, uploadId, generateAiMetadata })=>{
|
|
156
242
|
const reader = response.body.getReader();
|
|
157
243
|
const decoder = new TextDecoder();
|
|
158
244
|
let streamResult = null;
|
|
@@ -204,6 +290,14 @@ const unregisterAbortController = (uploadId)=>{
|
|
|
204
290
|
index,
|
|
205
291
|
file: payload.file
|
|
206
292
|
}));
|
|
293
|
+
// Same fire-and-forget semantics as the file flow.
|
|
294
|
+
maybeGenerateMetadata({
|
|
295
|
+
file: payload.file,
|
|
296
|
+
index,
|
|
297
|
+
uploadId,
|
|
298
|
+
enabled: generateAiMetadata,
|
|
299
|
+
dispatch
|
|
300
|
+
});
|
|
207
301
|
break;
|
|
208
302
|
}
|
|
209
303
|
case 'file:error':
|
|
@@ -240,10 +334,12 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
240
334
|
}).injectEndpoints({
|
|
241
335
|
endpoints: (builder)=>({
|
|
242
336
|
/**
|
|
243
|
-
* Upload files
|
|
244
|
-
*
|
|
337
|
+
* Upload files to `/upload/unstable/upload-file`, one request per file,
|
|
338
|
+
* through a worker pool of `concurrency` parallel requests (default 1 =
|
|
339
|
+
* sequential). Real per-file byte progress comes from
|
|
340
|
+
* `XHR.upload.onprogress`.
|
|
245
341
|
*/ uploadFiles: builder.mutation({
|
|
246
|
-
queryFn: async ({ formData, totalFiles }, { dispatch, getState })=>{
|
|
342
|
+
queryFn: async ({ formData, totalFiles, concurrency = 1, generateAiMetadata }, { dispatch, getState })=>{
|
|
247
343
|
const token = getState().admin_app?.token;
|
|
248
344
|
// Extract the original files and their per-file fileInfo from the combined FormData.
|
|
249
345
|
const files = formData.getAll('files');
|
|
@@ -269,17 +365,19 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
269
365
|
// Get the uploadId from state after dispatching
|
|
270
366
|
const uploadId = getState().uploadProgress.uploadId;
|
|
271
367
|
// Store original entries for retry functionality
|
|
272
|
-
registerUploadEntries(uploadId, entries);
|
|
368
|
+
registerUploadEntries(uploadId, entries, generateAiMetadata, concurrency);
|
|
273
369
|
// One AbortController per batch
|
|
274
370
|
const abortController = new AbortController();
|
|
275
371
|
registerAbortController(uploadId, abortController);
|
|
276
|
-
return
|
|
372
|
+
return runUploadPool({
|
|
277
373
|
entries,
|
|
278
374
|
indices: entries.map((_, index)=>index),
|
|
279
375
|
token,
|
|
280
376
|
uploadId,
|
|
281
377
|
abortController,
|
|
282
|
-
dispatch
|
|
378
|
+
dispatch,
|
|
379
|
+
concurrency,
|
|
380
|
+
generateAiMetadata
|
|
283
381
|
});
|
|
284
382
|
},
|
|
285
383
|
invalidatesTags: [
|
|
@@ -327,9 +425,9 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
327
425
|
/**
|
|
328
426
|
* Retry uploading cancelled files.
|
|
329
427
|
* Maps cancelled rows back to their original entries and re-runs only those
|
|
330
|
-
* through the same
|
|
428
|
+
* through the same upload pool with a fresh AbortController.
|
|
331
429
|
*/ retryCancelledFiles: builder.mutation({
|
|
332
|
-
queryFn: async (
|
|
430
|
+
queryFn: async (_arg, { dispatch, getState })=>{
|
|
333
431
|
const { uploadId, files: stateFiles } = getState().uploadProgress;
|
|
334
432
|
const token = getState().admin_app?.token;
|
|
335
433
|
const cancelledIndices = stateFiles.filter((f)=>f.status === 'cancelled').map((f)=>f.index);
|
|
@@ -341,8 +439,8 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
341
439
|
}
|
|
342
440
|
};
|
|
343
441
|
}
|
|
344
|
-
const
|
|
345
|
-
if (!
|
|
442
|
+
const batch = getUploadEntries(uploadId);
|
|
443
|
+
if (!batch) {
|
|
346
444
|
return {
|
|
347
445
|
error: {
|
|
348
446
|
name: 'UnknownError',
|
|
@@ -355,13 +453,16 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
355
453
|
// Fresh AbortController for the retry run
|
|
356
454
|
const abortController = new AbortController();
|
|
357
455
|
registerAbortController(uploadId, abortController);
|
|
358
|
-
return
|
|
359
|
-
entries,
|
|
456
|
+
return runUploadPool({
|
|
457
|
+
entries: batch.entries,
|
|
360
458
|
indices: cancelledIndices,
|
|
361
459
|
token,
|
|
362
460
|
uploadId,
|
|
363
461
|
abortController,
|
|
364
|
-
dispatch
|
|
462
|
+
dispatch,
|
|
463
|
+
// Replay the retried rows with the flags the batch was started with.
|
|
464
|
+
concurrency: batch.concurrency,
|
|
465
|
+
generateAiMetadata: batch.generateAiMetadata
|
|
365
466
|
});
|
|
366
467
|
},
|
|
367
468
|
invalidatesTags: [
|
|
@@ -375,7 +476,7 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
375
476
|
* Upload files from URLs.
|
|
376
477
|
* Sends URLs to the server which fetches and uploads them (SSE flow, unchanged).
|
|
377
478
|
*/ uploadFromUrls: builder.mutation({
|
|
378
|
-
queryFn: async ({ urls, folderId }, { dispatch, getState })=>{
|
|
479
|
+
queryFn: async ({ urls, folderId, generateAiMetadata }, { dispatch, getState })=>{
|
|
379
480
|
const token = getState().admin_app?.token;
|
|
380
481
|
// Extract filenames from URLs for the progress dialog
|
|
381
482
|
const fileNames = urls.map((url)=>getFilenameFromUrl(url));
|
|
@@ -424,7 +525,9 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
424
525
|
// Process SSE stream from server
|
|
425
526
|
const streamResult = await processSSEStream({
|
|
426
527
|
response,
|
|
427
|
-
dispatch
|
|
528
|
+
dispatch,
|
|
529
|
+
uploadId,
|
|
530
|
+
generateAiMetadata
|
|
428
531
|
});
|
|
429
532
|
unregisterAbortController(uploadId);
|
|
430
533
|
if (streamResult && streamResult.data.length > 0) {
|
|
@@ -466,10 +569,40 @@ const uploadApi = adminApi.enhanceEndpoints({
|
|
|
466
569
|
id: 'LIST'
|
|
467
570
|
}
|
|
468
571
|
]
|
|
572
|
+
}),
|
|
573
|
+
/**
|
|
574
|
+
* Generate AI metadata (alt text + caption) for the given assets.
|
|
575
|
+
* Synchronous: resolves once every file has been processed and reports the
|
|
576
|
+
* outcome per file, so non-images and individual failures don't fail the
|
|
577
|
+
* whole batch. Existing alt text and captions are never overwritten.
|
|
578
|
+
*
|
|
579
|
+
* Lives here rather than in `assets.ts` because the upload flows dispatch it
|
|
580
|
+
* directly via `initiate()` after each file completes, and `assets.ts` already
|
|
581
|
+
* imports from this module — defining it there would be a circular import.
|
|
582
|
+
* Re-exported from `assets.ts` for the components that consume the hook.
|
|
583
|
+
*/ generateAiMetadata: builder.mutation({
|
|
584
|
+
query: ({ fileIds })=>({
|
|
585
|
+
url: '/upload/unstable/generate-ai-metadata',
|
|
586
|
+
method: 'POST',
|
|
587
|
+
data: {
|
|
588
|
+
fileIds
|
|
589
|
+
}
|
|
590
|
+
}),
|
|
591
|
+
transformResponse: (response)=>response.data,
|
|
592
|
+
invalidatesTags: (_result, _error, { fileIds })=>[
|
|
593
|
+
...fileIds.map((id)=>({
|
|
594
|
+
type: 'Asset',
|
|
595
|
+
id
|
|
596
|
+
})),
|
|
597
|
+
{
|
|
598
|
+
type: 'Asset',
|
|
599
|
+
id: 'LIST'
|
|
600
|
+
}
|
|
601
|
+
]
|
|
469
602
|
})
|
|
470
603
|
})
|
|
471
604
|
});
|
|
472
|
-
const { useUploadFilesMutation, useUploadFileSilentlyMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation } = uploadApi;
|
|
605
|
+
const { useUploadFilesMutation, useUploadFileSilentlyMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation, useGenerateAiMetadataMutation } = uploadApi;
|
|
473
606
|
|
|
474
|
-
export { abortUpload, uploadApi, useRetryCancelledFilesMutation, useUploadFileSilentlyMutation, useUploadFilesMutation, useUploadFromUrlsMutation };
|
|
607
|
+
export { abortUpload, maybeGenerateMetadata, uploadApi, useGenerateAiMetadataMutation, useRetryCancelledFilesMutation, useUploadFileSilentlyMutation, useUploadFilesMutation, useUploadFromUrlsMutation };
|
|
475
608
|
//# 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 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 * Upload a single, already-prepared file WITHOUT opening the global\n * upload-progress dialog. For flows that create one asset programmatically\n * (e.g. crop → \"Save as copy\"), where the bulk-upload progress UI is\n * inappropriate. Same endpoint as `uploadFiles`, minus the progress/retry\n * machinery.\n */\n uploadFileSilently: builder.mutation<UploadedFile, UploadEntry>({\n queryFn: async ({ file, fileInfo }, { getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n const url = `${window.strapi.backendURL}/upload/unstable/upload-file`;\n\n const formData = new FormData();\n formData.append('files', file);\n formData.append('fileInfo', JSON.stringify(fileInfo));\n\n try {\n const uploaded = await uploadFileViaXHR(\n url,\n token,\n formData,\n new AbortController().signal,\n () => {}\n );\n return { data: uploaded };\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Upload failed';\n return { error: { name: 'UnknownError', message } };\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 {\n useUploadFilesMutation,\n useUploadFileSilentlyMutation,\n useRetryCancelledFilesMutation,\n 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","uploadFileSilently","retryCancelledFiles","stateFiles","cancelledIndices","f","status","length","uploadFromUrls","getFilenameFromUrl","ok","errorMessage","errorData","json","setUploadFailed","DOMException","useUploadFilesMutation","useUploadFileSilentlyMutation","useRetryCancelledFilesMutation","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 setFileMetadataGenerating,\n setFileMetadataResult,\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 UnstableGenerateAIMetadata,\n UploadFileInfo,\n} from '../../../../shared/contracts/files';\nimport type { FileMetadataResultStatus } from '../store/uploadProgress';\n\ninterface UploadFilesArgs {\n formData: FormData;\n totalFiles: number;\n /**\n * How many files to upload in parallel — the `concurrentUploadRequests`\n * settings value. Defaults to 1 (sequential).\n */\n concurrency?: number;\n /** Whether AI metadata generation is enabled (EE AI available + `settings.aiMetadata`). */\n generateAiMetadata: boolean;\n}\n\ninterface UploadFromUrlsArgs {\n urls: string[];\n folderId: number | null;\n generateAiMetadata: boolean;\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 * Everything a batch needs to be replayed on retry: the original entries plus the\n * flags it was started with (AI-metadata and upload concurrency). These live here\n * rather than in Redux because `retryCancelledFiles` takes no args and the page\n * that owns `useAIAvailability` / the settings value is not involved in the retry.\n */\ninterface UploadBatch {\n entries: UploadEntry[];\n generateAiMetadata: boolean;\n concurrency: number;\n}\n\n/**\n * Stores the original upload batch (keyed by uploadId) to enable retry.\n */\nconst uploadRegistry = new Map<number, UploadBatch>();\n\nconst registerUploadEntries = (\n uploadId: number,\n entries: UploadEntry[],\n generateAiMetadata: boolean,\n concurrency: number\n) => {\n uploadRegistry.set(uploadId, { entries, generateAiMetadata, concurrency });\n};\n\nconst getUploadEntries = (uploadId: number): UploadBatch | 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\n/**\n * The store's dispatch also accepts thunks, which is what RTK Query's\n * `endpoint.initiate(...)` returns — the plain `Dispatch` action type can't express that.\n */\ntype AppDispatch = Dispatch &\n (<T>(thunk: T) => T extends (...args: never[]) => infer R ? R : never);\n\ntype UploadPoolResult =\n | { data: UploadedFile[]; error?: undefined }\n | { error: UploadError; data?: undefined };\n\n/** Maps a server per-file outcome onto the row's terminal metadata status. */\nconst METADATA_STATUS_BY_RESULT: Record<\n UnstableGenerateAIMetadata.FileStatus,\n FileMetadataResultStatus\n> = {\n success: 'generated',\n skipped: 'skipped',\n error: 'failed',\n};\n\n/**\n * Kicks off AI metadata generation for a freshly uploaded file.\n *\n * Deliberately fire-and-forget — the caller must NOT await it:\n * - generation for file N overlaps the upload of file N+1;\n * - a generation failure can never affect the upload result (the upload already\n * succeeded), which is why the promise is fully swallowed here.\n *\n * Every uploaded file is sent, including non-images: the server already classifies\n * them (`ai-metadata.generateForFiles` returns `skipped` for anything whose mime\n * isn't `image/*`) and duplicating that rule here would let the two drift apart.\n * The upshot is that a non-image now reports \"Metadata generation skipped\" rather\n * than silently showing no metadata state at all.\n */\nexport const maybeGenerateMetadata = ({\n file,\n index,\n uploadId,\n enabled,\n dispatch,\n}: {\n file: UploadedFile;\n index: number;\n uploadId: number;\n enabled: boolean;\n dispatch: Dispatch;\n}): void => {\n if (!enabled || typeof file.id !== 'number') {\n return;\n }\n\n dispatch(setFileMetadataGenerating({ index, uploadId }));\n\n // `uploadApi` is declared below but only read at call time, long after the module\n // has finished evaluating.\n const promise = (dispatch as AppDispatch)(\n uploadApi.endpoints.generateAiMetadata.initiate({ fileIds: [file.id] })\n );\n\n promise\n .unwrap()\n .then((results) => {\n const result = results?.find((r) => r.id === file.id) ?? results?.[0];\n dispatch(\n setFileMetadataResult({\n index,\n uploadId,\n status: result ? METADATA_STATUS_BY_RESULT[result.status] : 'failed',\n })\n );\n })\n .catch(() => {\n dispatch(setFileMetadataResult({ index, uploadId, status: 'failed' }));\n });\n};\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 runUploadPool = async ({\n entries,\n indices,\n token,\n uploadId,\n abortController,\n dispatch,\n concurrency = 1,\n generateAiMetadata,\n}: {\n entries: UploadEntry[];\n indices: number[];\n token: string | null | undefined;\n uploadId: number;\n abortController: AbortController;\n dispatch: Dispatch;\n /** Parallel workers pulling from the queue. 1 (the default) = sequential. */\n concurrency?: number;\n generateAiMetadata: boolean;\n}): Promise<UploadPoolResult> => {\n const url = `${window.strapi.backendURL}/upload/unstable/upload-file`;\n const uploaded: UploadedFile[] = [];\n\n // Shared queue: each worker shifts the next index when it frees up, so N\n // files are in flight at any moment (not N-sized waves). With concurrency 1\n // this degenerates to the original strictly sequential loop.\n const queue = [...indices];\n\n const uploadOne = async (index: number) => {\n const entry = entries[index];\n if (!entry) {\n return;\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\n // Not awaited: overlaps with the next file's upload and can't fail the batch.\n maybeGenerateMetadata({\n file,\n index,\n uploadId,\n enabled: generateAiMetadata,\n dispatch,\n });\n } catch (err) {\n batcher.cancel();\n\n if (err instanceof UploadAbortedError) {\n // Batch was cancelled — the worker loop checks the signal and stops.\n // cancelUpload (dispatched from the dialog) marks the remaining rows.\n return;\n }\n\n const message = err instanceof Error ? err.message : 'Upload failed';\n dispatch(setFileError({ index, name: fileName, message }));\n }\n };\n\n // Guard non-finite/invalid values (NaN, Infinity, <1): `Math.floor(NaN)`\n // would propagate through Math.max to `Array.from({ length: NaN })` = zero\n // workers, silently uploading nothing. Anything invalid falls back to 1.\n const requested = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const workerCount = Math.min(Math.max(1, requested), queue.length);\n\n const worker = async () => {\n while (queue.length > 0) {\n if (abortController.signal.aborted) {\n break;\n }\n\n const index = queue.shift();\n if (index === undefined) {\n break;\n }\n\n // eslint-disable-next-line no-await-in-loop\n await uploadOne(index);\n }\n };\n\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\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 uploadId,\n generateAiMetadata,\n}: {\n response: Response;\n dispatch: Dispatch;\n uploadId: number;\n generateAiMetadata: boolean;\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\n // Same fire-and-forget semantics as the file flow.\n maybeGenerateMetadata({\n file: payload.file,\n index,\n uploadId,\n enabled: generateAiMetadata,\n dispatch,\n });\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 to `/upload/unstable/upload-file`, one request per file,\n * through a worker pool of `concurrency` parallel requests (default 1 =\n * sequential). Real per-file byte progress comes from\n * `XHR.upload.onprogress`.\n */\n uploadFiles: builder.mutation<UploadedFile[], UploadFilesArgs>({\n queryFn: async (\n { formData, totalFiles, concurrency = 1, generateAiMetadata },\n { dispatch, getState }\n ) => {\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, generateAiMetadata, concurrency);\n\n // One AbortController per batch\n const abortController = new AbortController();\n registerAbortController(uploadId, abortController);\n\n return runUploadPool({\n entries,\n indices: entries.map((_, index) => index),\n token,\n uploadId,\n abortController,\n dispatch,\n concurrency,\n generateAiMetadata,\n });\n },\n invalidatesTags: [{ type: 'Asset', id: 'LIST' }],\n }),\n\n /**\n * Upload a single, already-prepared file WITHOUT opening the global\n * upload-progress dialog. For flows that create one asset programmatically\n * (e.g. crop → \"Save as copy\"), where the bulk-upload progress UI is\n * inappropriate. Same endpoint as `uploadFiles`, minus the progress/retry\n * machinery.\n */\n uploadFileSilently: builder.mutation<UploadedFile, UploadEntry>({\n queryFn: async ({ file, fileInfo }, { getState }) => {\n const token = (getState() as RootState).admin_app?.token;\n const url = `${window.strapi.backendURL}/upload/unstable/upload-file`;\n\n const formData = new FormData();\n formData.append('files', file);\n formData.append('fileInfo', JSON.stringify(fileInfo));\n\n try {\n const uploaded = await uploadFileViaXHR(\n url,\n token,\n formData,\n new AbortController().signal,\n () => {}\n );\n return { data: uploaded };\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Upload failed';\n return { error: { name: 'UnknownError', message } };\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 upload pool with a fresh AbortController.\n */\n retryCancelledFiles: builder.mutation<UploadedFile[], void>({\n queryFn: async (_arg, { 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 batch = getUploadEntries(uploadId);\n if (!batch) {\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 runUploadPool({\n entries: batch.entries,\n indices: cancelledIndices,\n token,\n uploadId,\n abortController,\n dispatch,\n // Replay the retried rows with the flags the batch was started with.\n concurrency: batch.concurrency,\n generateAiMetadata: batch.generateAiMetadata,\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, generateAiMetadata }, { 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 uploadId,\n generateAiMetadata,\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 * Generate AI metadata (alt text + caption) for the given assets.\n * Synchronous: resolves once every file has been processed and reports the\n * outcome per file, so non-images and individual failures don't fail the\n * whole batch. Existing alt text and captions are never overwritten.\n *\n * Lives here rather than in `assets.ts` because the upload flows dispatch it\n * directly via `initiate()` after each file completes, and `assets.ts` already\n * imports from this module — defining it there would be a circular import.\n * Re-exported from `assets.ts` for the components that consume the hook.\n */\n generateAiMetadata: builder.mutation<\n UnstableGenerateAIMetadata.Response['data'],\n { fileIds: number[] }\n >({\n query: ({ fileIds }) => ({\n url: '/upload/unstable/generate-ai-metadata',\n method: 'POST',\n data: { fileIds },\n }),\n transformResponse: (response: { data: UnstableGenerateAIMetadata.Response['data'] }) =>\n response.data,\n invalidatesTags: (_result, _error, { fileIds }) => [\n ...fileIds.map((id) => ({ type: 'Asset' as const, id })),\n { type: 'Asset' as const, id: 'LIST' },\n ],\n }),\n }),\n });\n\nexport const {\n useUploadFilesMutation,\n useUploadFileSilentlyMutation,\n useRetryCancelledFilesMutation,\n useUploadFromUrlsMutation,\n useGenerateAiMetadataMutation,\n} = uploadApi;\nexport { uploadApi };\n"],"names":["uploadRegistry","Map","registerUploadEntries","uploadId","entries","generateAiMetadata","concurrency","set","getUploadEntries","get","abortControllers","registerAbortController","controller","unregisterAbortController","delete","abortUpload","abort","METADATA_STATUS_BY_RESULT","success","skipped","error","maybeGenerateMetadata","file","index","enabled","dispatch","id","setFileMetadataGenerating","promise","uploadApi","endpoints","initiate","fileIds","unwrap","then","results","result","find","r","setFileMetadataResult","status","catch","runUploadPool","indices","token","abortController","url","window","strapi","backendURL","uploaded","queue","uploadOne","entry","fileName","fileInfo","name","setFileUploading","size","formData","FormData","append","JSON","stringify","batcher","createRafBatcher","bytes","setFileProgress","uploadFileViaXHR","signal","schedule","cancel","push","setFileComplete","err","UploadAbortedError","message","Error","setFileError","requested","Number","isFinite","Math","floor","workerCount","min","max","length","worker","aborted","shift","undefined","Promise","all","Array","from","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","adminApi","enhanceEndpoints","addTagTypes","injectEndpoints","builder","uploadFiles","mutation","queryFn","totalFiles","getState","admin_app","files","getAll","fileInfoJson","fileInfoArray","map","caption","alternativeText","folder","fileNames","fileSizes","openUploadProgress","uploadProgress","AbortController","_","invalidatesTags","type","uploadFileSilently","retryCancelledFiles","_arg","stateFiles","cancelledIndices","f","batch","uploadFromUrls","getFilenameFromUrl","ok","errorMessage","errorData","json","setUploadFailed","DOMException","query","transformResponse","_result","_error","useUploadFilesMutation","useUploadFileSilentlyMutation","useRetryCancelledFilesMutation","useUploadFromUrlsMutation","useGenerateAiMetadataMutation"],"mappings":"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"}
|
|
@@ -1,35 +1,46 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var searchQueryParam = require('../utils/searchQueryParam.js');
|
|
3
4
|
var api = require('./api.js');
|
|
4
5
|
|
|
5
6
|
const assetsApi = api.uploadApi.injectEndpoints({
|
|
6
7
|
endpoints: (builder)=>({
|
|
7
8
|
getAssets: builder.query({
|
|
8
9
|
query: (params = {})=>{
|
|
9
|
-
|
|
10
|
+
// `search` is destructured out so it never reaches the server as-is —
|
|
11
|
+
// it is re-added below as the `_q` the API actually understands.
|
|
12
|
+
const { folder, search, filters = [], ...rest } = params;
|
|
10
13
|
const queryParams = {
|
|
11
14
|
...rest
|
|
12
15
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
// List filters apply in BOTH modes: search composes with them (a
|
|
17
|
+
// filtered search), only the folder scope is dropped while searching.
|
|
18
|
+
if (search) {
|
|
19
|
+
// Search is global: folder scoping is intentionally dropped so results span the whole library.
|
|
20
|
+
queryParams['_q'] = searchQueryParam.encodeSearchQuery(search);
|
|
21
|
+
if (filters.length > 0) {
|
|
22
|
+
queryParams['filters'] = {
|
|
23
|
+
$and: [
|
|
24
|
+
...filters
|
|
25
|
+
]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
const folderScope = folder != null ? {
|
|
30
|
+
folder: {
|
|
31
|
+
id: folder
|
|
32
|
+
}
|
|
33
|
+
} : {
|
|
34
|
+
folder: {
|
|
35
|
+
id: {
|
|
36
|
+
$null: true
|
|
20
37
|
}
|
|
21
|
-
|
|
38
|
+
}
|
|
22
39
|
};
|
|
23
|
-
} else {
|
|
24
40
|
queryParams['filters'] = {
|
|
25
41
|
$and: [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
id: {
|
|
29
|
-
$null: true
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
42
|
+
folderScope,
|
|
43
|
+
...filters
|
|
33
44
|
]
|
|
34
45
|
};
|
|
35
46
|
}
|
|
@@ -189,6 +200,7 @@ const assetsApi = api.uploadApi.injectEndpoints({
|
|
|
189
200
|
});
|
|
190
201
|
const { useGetAssetsQuery, useGetAssetQuery, useUpdateAssetMutation, useReplaceAssetMutation, useDeleteAssetMutation, useBulkDeleteItemsMutation } = assetsApi;
|
|
191
202
|
|
|
203
|
+
exports.useGenerateAiMetadataMutation = api.useGenerateAiMetadataMutation;
|
|
192
204
|
exports.useBulkDeleteItemsMutation = useBulkDeleteItemsMutation;
|
|
193
205
|
exports.useDeleteAssetMutation = useDeleteAssetMutation;
|
|
194
206
|
exports.useGetAssetQuery = useGetAssetQuery;
|