@strapi/upload 5.35.0 → 5.36.1
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/future/App.js +8 -20
- package/dist/admin/future/App.js.map +1 -1
- package/dist/admin/future/App.mjs +8 -20
- package/dist/admin/future/App.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +494 -0
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -0
- package/dist/admin/future/components/UploadProgressDialog.mjs +473 -0
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -0
- package/dist/admin/future/enums.js +12 -0
- package/dist/admin/future/enums.js.map +1 -0
- package/dist/admin/future/enums.mjs +10 -0
- package/dist/admin/future/enums.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +311 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +290 -0
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +164 -0
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +162 -0
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +198 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +196 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.js +127 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.mjs +105 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZone.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js +107 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +104 -0
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/constants.js +54 -0
- package/dist/admin/future/pages/Assets/constants.js.map +1 -0
- package/dist/admin/future/pages/Assets/constants.mjs +50 -0
- package/dist/admin/future/pages/Assets/constants.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +77 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +74 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -0
- package/dist/admin/future/services/api.js +419 -9
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +417 -9
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +37 -0
- package/dist/admin/future/services/assets.js.map +1 -0
- package/dist/admin/future/services/assets.mjs +35 -0
- package/dist/admin/future/services/assets.mjs.map +1 -0
- package/dist/admin/future/store/hooks.js +10 -0
- package/dist/admin/future/store/hooks.js.map +1 -0
- package/dist/admin/future/store/hooks.mjs +7 -0
- package/dist/admin/future/store/hooks.mjs.map +1 -0
- package/dist/admin/future/store/uploadProgress.js +156 -0
- package/dist/admin/future/store/uploadProgress.js.map +1 -0
- package/dist/admin/future/store/uploadProgress.mjs +143 -0
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -0
- package/dist/admin/future/utils/files.js +23 -0
- package/dist/admin/future/utils/files.js.map +1 -0
- package/dist/admin/future/utils/files.mjs +19 -0
- package/dist/admin/future/utils/files.mjs.map +1 -0
- package/dist/admin/future/utils/getAssetIcon.js +28 -0
- package/dist/admin/future/utils/getAssetIcon.js.map +1 -0
- package/dist/admin/future/utils/getAssetIcon.mjs +26 -0
- package/dist/admin/future/utils/getAssetIcon.mjs.map +1 -0
- package/dist/admin/index.js +11 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +11 -0
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/package.json.js +11 -9
- package/dist/admin/package.json.js.map +1 -1
- package/dist/admin/package.json.mjs +11 -9
- package/dist/admin/package.json.mjs.map +1 -1
- package/dist/admin/src/future/components/UploadProgressDialog.d.ts +1 -0
- package/dist/admin/src/future/enums.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/AssetsPage.d.ts +1 -0
- package/dist/admin/src/future/pages/Assets/components/AssetsGrid.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZone.d.ts +9 -0
- package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZoneContext.d.ts +11 -0
- package/dist/admin/src/future/pages/Assets/constants.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +17 -0
- package/dist/admin/src/future/services/api.d.ts +21 -3
- package/dist/admin/src/future/services/assets.d.ts +13 -0
- package/dist/admin/src/future/store/hooks.d.ts +6 -0
- package/dist/admin/src/future/store/uploadProgress.d.ts +46 -0
- package/dist/admin/src/future/utils/files.d.ts +3 -0
- package/dist/admin/src/future/utils/getAssetIcon.d.ts +12 -0
- package/dist/admin/translations/en.json.js +25 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +25 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/controllers/admin-upload.js +151 -2
- package/dist/server/controllers/admin-upload.js.map +1 -1
- package/dist/server/controllers/admin-upload.mjs +151 -2
- package/dist/server/controllers/admin-upload.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +8 -2
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +9 -3
- package/dist/server/controllers/content-api.mjs.map +1 -1
- package/dist/server/routes/admin.js +10 -0
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +10 -0
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/src/controllers/admin-upload.d.ts +12 -0
- package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
- package/dist/server/src/controllers/content-api.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/index.d.ts +1 -0
- 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/utils/mime-validation.d.ts +5 -0
- package/dist/server/src/utils/mime-validation.d.ts.map +1 -1
- package/dist/server/utils/mime-validation.js +7 -4
- package/dist/server/utils/mime-validation.js.map +1 -1
- package/dist/server/utils/mime-validation.mjs +7 -4
- package/dist/server/utils/mime-validation.mjs.map +1 -1
- package/dist/shared/contracts/files.d.ts +52 -0
- package/dist/shared/contracts/files.d.ts.map +1 -0
- package/package.json +11 -9
- package/dist/admin/future/pages/AIGenerationPage.js +0 -24
- package/dist/admin/future/pages/AIGenerationPage.js.map +0 -1
- package/dist/admin/future/pages/AIGenerationPage.mjs +0 -22
- package/dist/admin/future/pages/AIGenerationPage.mjs.map +0 -1
- package/dist/admin/future/pages/MediaLibraryPage.js +0 -119
- package/dist/admin/future/pages/MediaLibraryPage.js.map +0 -1
- package/dist/admin/future/pages/MediaLibraryPage.mjs +0 -98
- package/dist/admin/future/pages/MediaLibraryPage.mjs.map +0 -1
- package/dist/admin/src/future/pages/AIGenerationPage.d.ts +0 -1
- package/dist/admin/src/future/pages/MediaLibraryPage.d.ts +0 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var name = "@strapi/upload";
|
|
6
|
-
var version = "5.
|
|
6
|
+
var version = "5.36.1";
|
|
7
7
|
var description = "Makes it easy to upload images and files to your Strapi Application.";
|
|
8
8
|
var license = "SEE LICENSE IN LICENSE";
|
|
9
9
|
var author = {
|
|
@@ -64,12 +64,14 @@ var scripts = {
|
|
|
64
64
|
};
|
|
65
65
|
var dependencies = {
|
|
66
66
|
"@mux/mux-player-react": "3.1.0",
|
|
67
|
+
"@radix-ui/react-dialog": "1.1.15",
|
|
68
|
+
"@radix-ui/react-toggle-group": "1.1.11",
|
|
67
69
|
"@reduxjs/toolkit": "1.9.7",
|
|
68
|
-
"@strapi/database": "5.
|
|
70
|
+
"@strapi/database": "5.36.1",
|
|
69
71
|
"@strapi/design-system": "2.1.2",
|
|
70
72
|
"@strapi/icons": "2.1.2",
|
|
71
|
-
"@strapi/provider-upload-local": "5.
|
|
72
|
-
"@strapi/utils": "5.
|
|
73
|
+
"@strapi/provider-upload-local": "5.36.1",
|
|
74
|
+
"@strapi/utils": "5.36.1",
|
|
73
75
|
"byte-size": "8.1.1",
|
|
74
76
|
cropperjs: "1.6.1",
|
|
75
77
|
"date-fns": "2.30.0",
|
|
@@ -82,7 +84,7 @@ var dependencies = {
|
|
|
82
84
|
lodash: "4.17.21",
|
|
83
85
|
"mime-types": "2.1.35",
|
|
84
86
|
"prop-types": "^15.8.1",
|
|
85
|
-
qs: "6.14.
|
|
87
|
+
qs: "6.14.2",
|
|
86
88
|
"react-dnd": "16.0.1",
|
|
87
89
|
"react-intl": "6.6.2",
|
|
88
90
|
"react-query": "3.39.3",
|
|
@@ -93,8 +95,8 @@ var dependencies = {
|
|
|
93
95
|
zod: "3.25.67"
|
|
94
96
|
};
|
|
95
97
|
var devDependencies = {
|
|
96
|
-
"@strapi/admin": "5.
|
|
97
|
-
"@strapi/types": "5.
|
|
98
|
+
"@strapi/admin": "5.36.1",
|
|
99
|
+
"@strapi/types": "5.36.1",
|
|
98
100
|
"@testing-library/dom": "10.4.1",
|
|
99
101
|
"@testing-library/react": "16.3.0",
|
|
100
102
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -109,14 +111,14 @@ var devDependencies = {
|
|
|
109
111
|
msw: "1.3.0",
|
|
110
112
|
react: "18.3.1",
|
|
111
113
|
"react-dom": "18.3.1",
|
|
112
|
-
"react-router-dom": "6.
|
|
114
|
+
"react-router-dom": "6.30.3",
|
|
113
115
|
"styled-components": "6.1.8"
|
|
114
116
|
};
|
|
115
117
|
var peerDependencies = {
|
|
116
118
|
"@strapi/admin": "^5.0.0",
|
|
117
119
|
react: "^17.0.0 || ^18.0.0",
|
|
118
120
|
"react-dom": "^17.0.0 || ^18.0.0",
|
|
119
|
-
"react-router-dom": "^6.
|
|
121
|
+
"react-router-dom": "^6.30.3",
|
|
120
122
|
"styled-components": "^6.0.0"
|
|
121
123
|
};
|
|
122
124
|
var engines = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"package.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var name = "@strapi/upload";
|
|
2
|
-
var version = "5.
|
|
2
|
+
var version = "5.36.1";
|
|
3
3
|
var description = "Makes it easy to upload images and files to your Strapi Application.";
|
|
4
4
|
var license = "SEE LICENSE IN LICENSE";
|
|
5
5
|
var author = {
|
|
@@ -60,12 +60,14 @@ var scripts = {
|
|
|
60
60
|
};
|
|
61
61
|
var dependencies = {
|
|
62
62
|
"@mux/mux-player-react": "3.1.0",
|
|
63
|
+
"@radix-ui/react-dialog": "1.1.15",
|
|
64
|
+
"@radix-ui/react-toggle-group": "1.1.11",
|
|
63
65
|
"@reduxjs/toolkit": "1.9.7",
|
|
64
|
-
"@strapi/database": "5.
|
|
66
|
+
"@strapi/database": "5.36.1",
|
|
65
67
|
"@strapi/design-system": "2.1.2",
|
|
66
68
|
"@strapi/icons": "2.1.2",
|
|
67
|
-
"@strapi/provider-upload-local": "5.
|
|
68
|
-
"@strapi/utils": "5.
|
|
69
|
+
"@strapi/provider-upload-local": "5.36.1",
|
|
70
|
+
"@strapi/utils": "5.36.1",
|
|
69
71
|
"byte-size": "8.1.1",
|
|
70
72
|
cropperjs: "1.6.1",
|
|
71
73
|
"date-fns": "2.30.0",
|
|
@@ -78,7 +80,7 @@ var dependencies = {
|
|
|
78
80
|
lodash: "4.17.21",
|
|
79
81
|
"mime-types": "2.1.35",
|
|
80
82
|
"prop-types": "^15.8.1",
|
|
81
|
-
qs: "6.14.
|
|
83
|
+
qs: "6.14.2",
|
|
82
84
|
"react-dnd": "16.0.1",
|
|
83
85
|
"react-intl": "6.6.2",
|
|
84
86
|
"react-query": "3.39.3",
|
|
@@ -89,8 +91,8 @@ var dependencies = {
|
|
|
89
91
|
zod: "3.25.67"
|
|
90
92
|
};
|
|
91
93
|
var devDependencies = {
|
|
92
|
-
"@strapi/admin": "5.
|
|
93
|
-
"@strapi/types": "5.
|
|
94
|
+
"@strapi/admin": "5.36.1",
|
|
95
|
+
"@strapi/types": "5.36.1",
|
|
94
96
|
"@testing-library/dom": "10.4.1",
|
|
95
97
|
"@testing-library/react": "16.3.0",
|
|
96
98
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -105,14 +107,14 @@ var devDependencies = {
|
|
|
105
107
|
msw: "1.3.0",
|
|
106
108
|
react: "18.3.1",
|
|
107
109
|
"react-dom": "18.3.1",
|
|
108
|
-
"react-router-dom": "6.
|
|
110
|
+
"react-router-dom": "6.30.3",
|
|
109
111
|
"styled-components": "6.1.8"
|
|
110
112
|
};
|
|
111
113
|
var peerDependencies = {
|
|
112
114
|
"@strapi/admin": "^5.0.0",
|
|
113
115
|
react: "^17.0.0 || ^18.0.0",
|
|
114
116
|
"react-dom": "^17.0.0 || ^18.0.0",
|
|
115
|
-
"react-router-dom": "^6.
|
|
117
|
+
"react-router-dom": "^6.30.3",
|
|
116
118
|
"styled-components": "^6.0.0"
|
|
117
119
|
};
|
|
118
120
|
var engines = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"package.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const UploadProgressDialog: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const AssetsPage: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
declare const DropZoneWithOverlay: ({ children }: {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
interface DropFilesMessageProps {
|
|
6
|
+
uploadDropZoneRef?: React.RefObject<HTMLDivElement>;
|
|
7
|
+
}
|
|
8
|
+
declare const DropFilesMessage: ({ uploadDropZoneRef }: DropFilesMessageProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
9
|
+
export { DropZoneWithOverlay, DropFilesMessage };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
type DropHandler = (files: File[]) => void | Promise<void>;
|
|
3
|
+
interface UploadDropZoneProps {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
onDrop?: DropHandler;
|
|
6
|
+
}
|
|
7
|
+
export declare const UploadDropZoneProvider: ({ children, onDrop }: UploadDropZoneProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare const useUploadDropZone: () => {
|
|
9
|
+
isDragging: boolean;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const localStorageKeys: {
|
|
2
|
+
view: string;
|
|
3
|
+
};
|
|
4
|
+
export declare const viewOptions: {
|
|
5
|
+
GRID: number;
|
|
6
|
+
TABLE: number;
|
|
7
|
+
};
|
|
8
|
+
interface TableHeader {
|
|
9
|
+
name: string;
|
|
10
|
+
label: {
|
|
11
|
+
id: string;
|
|
12
|
+
defaultMessage: string;
|
|
13
|
+
};
|
|
14
|
+
isVisuallyHidden?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const TABLE_HEADERS: TableHeader[];
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { File } from '../../../../../../shared/contracts/files';
|
|
2
|
+
declare const PAGE_SIZE = 20;
|
|
3
|
+
interface UseInfiniteAssetsOptions {
|
|
4
|
+
folder?: number | null;
|
|
5
|
+
sort?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const useInfiniteAssets: ({ folder, sort }?: UseInfiniteAssetsOptions) => {
|
|
8
|
+
assets: File[];
|
|
9
|
+
pagination: import("../../../../../../shared/contracts/files").Pagination | undefined;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
isFetchingMore: boolean;
|
|
12
|
+
hasNextPage: boolean;
|
|
13
|
+
fetchNextPage: () => void;
|
|
14
|
+
error: import("@strapi/admin/strapi-admin").BaseQueryError | import("@reduxjs/toolkit").SerializedError | undefined;
|
|
15
|
+
};
|
|
16
|
+
export { useInfiniteAssets };
|
|
17
|
+
export { PAGE_SIZE };
|
|
@@ -1,6 +1,24 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CreateFilesStream } from '../../../../shared/contracts/files';
|
|
2
|
+
interface UploadFilesArgs {
|
|
3
|
+
formData: FormData;
|
|
4
|
+
totalFiles: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Aborts an upload by its uploadId.
|
|
8
|
+
* Called from the UploadProgressDialog when the user clicks cancel or close.
|
|
9
|
+
*/
|
|
10
|
+
export declare const abortUpload: (uploadId: number) => void;
|
|
2
11
|
declare const uploadApi: import("@reduxjs/toolkit/query").Api<import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@strapi/admin/strapi-admin").QueryArguments, unknown, import("@strapi/admin/strapi-admin").BaseQueryError, {}, {}>, import("@reduxjs/toolkit/dist/query/endpointDefinitions").UpdateDefinitions<{}, "GuidedTourMeta" | "HomepageKeyStatistics" | "AIUsage" | "AIFeatureConfig" | "Asset" | "Folder", never> & {
|
|
3
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Stream upload files to the /upload/unstable/stream endpoint.
|
|
14
|
+
* Reads SSE stream for per-file progress updates.
|
|
15
|
+
*/
|
|
16
|
+
uploadFilesStream: import("@reduxjs/toolkit/query").MutationDefinition<UploadFilesArgs, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@strapi/admin/strapi-admin").QueryArguments, unknown, import("@strapi/admin/strapi-admin").BaseQueryError, {}, {}>, "GuidedTourMeta" | "HomepageKeyStatistics" | "AIUsage" | "AIFeatureConfig" | "Asset" | "Folder", CreateFilesStream.Response, "adminApi">;
|
|
17
|
+
/**
|
|
18
|
+
* Retry uploading cancelled files.
|
|
19
|
+
* Retrieves original File objects and re-uploads only the cancelled ones.
|
|
20
|
+
*/
|
|
21
|
+
retryCancelledFilesStream: import("@reduxjs/toolkit/query").MutationDefinition<void, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@strapi/admin/strapi-admin").QueryArguments, unknown, import("@strapi/admin/strapi-admin").BaseQueryError, {}, {}>, "GuidedTourMeta" | "HomepageKeyStatistics" | "AIUsage" | "AIFeatureConfig" | "Asset" | "Folder", CreateFilesStream.Response, "adminApi">;
|
|
4
22
|
}, "adminApi", "GuidedTourMeta" | "HomepageKeyStatistics" | "AIUsage" | "AIFeatureConfig" | "Asset" | "Folder", typeof import("@reduxjs/toolkit/query").coreModuleName | typeof import("@reduxjs/toolkit/dist/query/react").reactHooksModuleName>;
|
|
5
|
-
export declare const
|
|
23
|
+
export declare const useUploadFilesStreamMutation: import("@reduxjs/toolkit/dist/query/react/buildHooks").UseMutation<import("@reduxjs/toolkit/query").MutationDefinition<UploadFilesArgs, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@strapi/admin/strapi-admin").QueryArguments, unknown, import("@strapi/admin/strapi-admin").BaseQueryError, {}, {}>, "GuidedTourMeta" | "HomepageKeyStatistics" | "AIUsage" | "AIFeatureConfig" | "Asset" | "Folder", CreateFilesStream.Response, "adminApi">>, useRetryCancelledFilesStreamMutation: import("@reduxjs/toolkit/dist/query/react/buildHooks").UseMutation<import("@reduxjs/toolkit/query").MutationDefinition<void, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@strapi/admin/strapi-admin").QueryArguments, unknown, import("@strapi/admin/strapi-admin").BaseQueryError, {}, {}>, "GuidedTourMeta" | "HomepageKeyStatistics" | "AIUsage" | "AIFeatureConfig" | "Asset" | "Folder", CreateFilesStream.Response, "adminApi">>;
|
|
6
24
|
export { uploadApi };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { File, Pagination } from '../../../../shared/contracts/files';
|
|
2
|
+
interface GetAssetsParams {
|
|
3
|
+
page?: number;
|
|
4
|
+
pageSize?: number;
|
|
5
|
+
folder?: number | null;
|
|
6
|
+
sort?: string;
|
|
7
|
+
}
|
|
8
|
+
interface GetAssetsResponse {
|
|
9
|
+
results: File[];
|
|
10
|
+
pagination: Pagination;
|
|
11
|
+
}
|
|
12
|
+
export declare const useGetAssetsQuery: import("@reduxjs/toolkit/dist/query/react/buildHooks").UseQuery<import("@reduxjs/toolkit/query").QueryDefinition<void | GetAssetsParams, import("@reduxjs/toolkit/query").BaseQueryFn<string | import("@strapi/admin/strapi-admin").QueryArguments, unknown, import("@strapi/admin/strapi-admin").BaseQueryError, {}, {}>, "GuidedTourMeta" | "HomepageKeyStatistics" | "AIUsage" | "AIFeatureConfig" | "Asset" | "Folder", GetAssetsResponse, "adminApi">>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Dispatch } from '@reduxjs/toolkit';
|
|
2
|
+
import { TypedUseSelectorHook } from 'react-redux';
|
|
3
|
+
import type { RootState } from './uploadProgress';
|
|
4
|
+
declare const useTypedDispatch: () => Dispatch;
|
|
5
|
+
declare const useTypedSelector: TypedUseSelectorHook<RootState>;
|
|
6
|
+
export { useTypedSelector, useTypedDispatch };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { File } from '../../../../shared/contracts/files';
|
|
2
|
+
export interface FileUploadError {
|
|
3
|
+
name: string;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';
|
|
7
|
+
export interface FileProgress {
|
|
8
|
+
name: string;
|
|
9
|
+
index: number;
|
|
10
|
+
status: FileProgressStatus;
|
|
11
|
+
size: number;
|
|
12
|
+
file?: File;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface UploadProgressState {
|
|
16
|
+
isVisible: boolean;
|
|
17
|
+
isMinimized: boolean;
|
|
18
|
+
progress: number;
|
|
19
|
+
totalFiles: number;
|
|
20
|
+
files: FileProgress[];
|
|
21
|
+
errors: FileUploadError[];
|
|
22
|
+
uploadId: number;
|
|
23
|
+
}
|
|
24
|
+
export interface RootState {
|
|
25
|
+
uploadProgress: UploadProgressState;
|
|
26
|
+
}
|
|
27
|
+
export declare const openUploadProgress: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
28
|
+
totalFiles: number;
|
|
29
|
+
fileNames: string[];
|
|
30
|
+
fileSizes: number[];
|
|
31
|
+
}, "uploadProgress/openUploadProgress">, setFileUploading: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
32
|
+
name: string;
|
|
33
|
+
index: number;
|
|
34
|
+
total: number;
|
|
35
|
+
size: number;
|
|
36
|
+
}, "uploadProgress/setFileUploading">, setFileComplete: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
37
|
+
index: number;
|
|
38
|
+
file: File;
|
|
39
|
+
}, "uploadProgress/setFileComplete">, setFileError: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
40
|
+
index: number;
|
|
41
|
+
name: string;
|
|
42
|
+
message: string;
|
|
43
|
+
}, "uploadProgress/setFileError">, updateProgress: import("@reduxjs/toolkit").ActionCreatorWithPayload<number, "uploadProgress/updateProgress">, addUploadErrors: import("@reduxjs/toolkit").ActionCreatorWithPayload<FileUploadError[], "uploadProgress/addUploadErrors">, closeUploadProgress: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"uploadProgress/closeUploadProgress">, toggleMinimize: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"uploadProgress/toggleMinimize">, cancelUpload: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"uploadProgress/cancelUpload">, setUploadFailed: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
|
|
44
|
+
message: string;
|
|
45
|
+
}, "uploadProgress/setUploadFailed">, retryCancelledFiles: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"uploadProgress/retryCancelledFiles">;
|
|
46
|
+
export declare const uploadProgressReducer: import("redux").Reducer<UploadProgressState>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function formatBytes(receivedBytes: number | string, decimals?: number): string;
|
|
2
|
+
export declare const getFileExtension: (ext?: string | null) => string | null | undefined;
|
|
3
|
+
export declare const prefixFileUrlWithBackendUrl: (fileURL?: string) => string | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SVGProps } from 'react';
|
|
2
|
+
import { DefaultTheme } from 'styled-components';
|
|
3
|
+
interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'fill' | 'stroke'> {
|
|
4
|
+
/**
|
|
5
|
+
* @default "currentColor"
|
|
6
|
+
*/
|
|
7
|
+
fill?: keyof DefaultTheme['colors'] | string;
|
|
8
|
+
stroke?: keyof DefaultTheme['colors'] | string;
|
|
9
|
+
}
|
|
10
|
+
type IconComponent = React.FC<IconProps>;
|
|
11
|
+
export declare const getAssetIcon: (mime: string | undefined, ext: string | undefined) => IconComponent;
|
|
12
|
+
export {};
|
|
@@ -4,6 +4,28 @@ var en = {
|
|
|
4
4
|
"apiError.FileTooBig": "The uploaded file exceeds the maximum allowed asset size.",
|
|
5
5
|
"assets.uploaded": "{number, plural, one {# asset} other {# assets}} uploaded successfully",
|
|
6
6
|
"upload.generic-error": "An error occurred while uploading the file.",
|
|
7
|
+
"upload.progress": "Upload progress",
|
|
8
|
+
"upload.progress.failed": "Upload failed",
|
|
9
|
+
"upload.progress.failed.subtitle": "Please try to upload files again",
|
|
10
|
+
"upload.progress.success": "Upload successful!",
|
|
11
|
+
"upload.progress.success.subtitle": "{count, plural, one {# file uploaded successfully} other {# files uploaded successfully}}",
|
|
12
|
+
"upload.progress.uploading.withCount": "Uploading {total} items ({percentage}%)",
|
|
13
|
+
"upload.progress.uploading-files": "Uploading {count, plural, one {# file} other {# files}}",
|
|
14
|
+
"upload.progress.indicator.in-progress": "Upload in progress indicator",
|
|
15
|
+
"upload.progress.indicator.complete": "Upload complete indicator",
|
|
16
|
+
"upload.progress.label": "Upload",
|
|
17
|
+
"upload.progress.errors.title": "Failed to upload:",
|
|
18
|
+
"upload.progress.cancel": "Cancel",
|
|
19
|
+
"upload.progress.canceled": "Uploads canceled",
|
|
20
|
+
"upload.progress.canceled.subtitle": "Some files were not uploaded",
|
|
21
|
+
"upload.progress.canceled.subtitle.all": "All uploads were canceled",
|
|
22
|
+
"upload.progress.retry": "Retry",
|
|
23
|
+
"upload.progress.file.uploading": "Uploading...",
|
|
24
|
+
"upload.progress.file.canceled": "Canceled",
|
|
25
|
+
"upload.progress.file.uploaded": "Uploaded",
|
|
26
|
+
"upload.progress.maximize": "Maximize",
|
|
27
|
+
"upload.progress.minimize": "Minimize",
|
|
28
|
+
"upload.progress.close": "Close",
|
|
7
29
|
"bulk.select.label": "Select all assets",
|
|
8
30
|
"button.next": "Next",
|
|
9
31
|
"new": "New",
|
|
@@ -24,6 +46,7 @@ var en = {
|
|
|
24
46
|
"control-card.replace-media": "Replace Media",
|
|
25
47
|
"control-card.save": "Save",
|
|
26
48
|
"control-card.stop-crop": "Stop cropping",
|
|
49
|
+
"dropzone.upload.message": "Drop here to upload to",
|
|
27
50
|
"filter.add": "Add filter",
|
|
28
51
|
"form.button.replace-media": "Replace media",
|
|
29
52
|
"form.input.description.file-alt": "This text will be displayed if the asset can’t be shown.",
|
|
@@ -145,6 +168,8 @@ var en = {
|
|
|
145
168
|
"list.table.header.size": "size",
|
|
146
169
|
"list.table.header.createdAt": "created",
|
|
147
170
|
"list.table.header.updatedAt": "last update",
|
|
171
|
+
"list.table.header.creationDate": "Creation Date",
|
|
172
|
+
"list.table.header.lastModified": "Last Modified",
|
|
148
173
|
"list.table.header.sort": "Sort on {label}",
|
|
149
174
|
"list.table.content.empty-label": "This field is empty",
|
|
150
175
|
"tabs.title": "How do you want to upload your assets?",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -2,6 +2,28 @@ var en = {
|
|
|
2
2
|
"apiError.FileTooBig": "The uploaded file exceeds the maximum allowed asset size.",
|
|
3
3
|
"assets.uploaded": "{number, plural, one {# asset} other {# assets}} uploaded successfully",
|
|
4
4
|
"upload.generic-error": "An error occurred while uploading the file.",
|
|
5
|
+
"upload.progress": "Upload progress",
|
|
6
|
+
"upload.progress.failed": "Upload failed",
|
|
7
|
+
"upload.progress.failed.subtitle": "Please try to upload files again",
|
|
8
|
+
"upload.progress.success": "Upload successful!",
|
|
9
|
+
"upload.progress.success.subtitle": "{count, plural, one {# file uploaded successfully} other {# files uploaded successfully}}",
|
|
10
|
+
"upload.progress.uploading.withCount": "Uploading {total} items ({percentage}%)",
|
|
11
|
+
"upload.progress.uploading-files": "Uploading {count, plural, one {# file} other {# files}}",
|
|
12
|
+
"upload.progress.indicator.in-progress": "Upload in progress indicator",
|
|
13
|
+
"upload.progress.indicator.complete": "Upload complete indicator",
|
|
14
|
+
"upload.progress.label": "Upload",
|
|
15
|
+
"upload.progress.errors.title": "Failed to upload:",
|
|
16
|
+
"upload.progress.cancel": "Cancel",
|
|
17
|
+
"upload.progress.canceled": "Uploads canceled",
|
|
18
|
+
"upload.progress.canceled.subtitle": "Some files were not uploaded",
|
|
19
|
+
"upload.progress.canceled.subtitle.all": "All uploads were canceled",
|
|
20
|
+
"upload.progress.retry": "Retry",
|
|
21
|
+
"upload.progress.file.uploading": "Uploading...",
|
|
22
|
+
"upload.progress.file.canceled": "Canceled",
|
|
23
|
+
"upload.progress.file.uploaded": "Uploaded",
|
|
24
|
+
"upload.progress.maximize": "Maximize",
|
|
25
|
+
"upload.progress.minimize": "Minimize",
|
|
26
|
+
"upload.progress.close": "Close",
|
|
5
27
|
"bulk.select.label": "Select all assets",
|
|
6
28
|
"button.next": "Next",
|
|
7
29
|
"new": "New",
|
|
@@ -22,6 +44,7 @@ var en = {
|
|
|
22
44
|
"control-card.replace-media": "Replace Media",
|
|
23
45
|
"control-card.save": "Save",
|
|
24
46
|
"control-card.stop-crop": "Stop cropping",
|
|
47
|
+
"dropzone.upload.message": "Drop here to upload to",
|
|
25
48
|
"filter.add": "Add filter",
|
|
26
49
|
"form.button.replace-media": "Replace media",
|
|
27
50
|
"form.input.description.file-alt": "This text will be displayed if the asset can’t be shown.",
|
|
@@ -143,6 +166,8 @@ var en = {
|
|
|
143
166
|
"list.table.header.size": "size",
|
|
144
167
|
"list.table.header.createdAt": "created",
|
|
145
168
|
"list.table.header.updatedAt": "last update",
|
|
169
|
+
"list.table.header.creationDate": "Creation Date",
|
|
170
|
+
"list.table.header.lastModified": "Last Modified",
|
|
146
171
|
"list.table.header.sort": "Sort on {label}",
|
|
147
172
|
"list.table.content.empty-label": "This field is empty",
|
|
148
173
|
"tabs.title": "How do you want to upload your assets?",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -49,7 +49,10 @@ var adminUpload = {
|
|
|
49
49
|
if (Array.isArray(files)) {
|
|
50
50
|
throw new utils.errors.ApplicationError('Cannot replace a file with multiple ones');
|
|
51
51
|
}
|
|
52
|
-
const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(files, body, strapi);
|
|
52
|
+
const { validFiles, filteredBody, errors: validationErrors } = await mimeValidation.prepareUploadRequest(files, body, strapi);
|
|
53
|
+
if (validFiles.length === 0) {
|
|
54
|
+
throw new utils.errors.ValidationError(validationErrors[0].message);
|
|
55
|
+
}
|
|
53
56
|
const data = await upload.validateUploadBody(filteredBody);
|
|
54
57
|
const replacedFile = await uploadService.replace(id, {
|
|
55
58
|
data,
|
|
@@ -74,7 +77,10 @@ var adminUpload = {
|
|
|
74
77
|
if (!pm.isAllowed) {
|
|
75
78
|
return ctx.forbidden();
|
|
76
79
|
}
|
|
77
|
-
const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(files, body, strapi);
|
|
80
|
+
const { validFiles, filteredBody, errors: validationErrors } = await mimeValidation.prepareUploadRequest(files, body, strapi);
|
|
81
|
+
if (validFiles.length === 0) {
|
|
82
|
+
throw new utils.errors.ValidationError(validationErrors[0].message);
|
|
83
|
+
}
|
|
78
84
|
const isMultipleFiles = validFiles.length > 1;
|
|
79
85
|
const data = await upload.validateUploadBody(filteredBody, isMultipleFiles);
|
|
80
86
|
let filesArray = validFiles;
|
|
@@ -115,6 +121,149 @@ var adminUpload = {
|
|
|
115
121
|
});
|
|
116
122
|
ctx.status = 201;
|
|
117
123
|
},
|
|
124
|
+
/**
|
|
125
|
+
* @experimental
|
|
126
|
+
* Stream upload files with SSE streaming for per-file progress
|
|
127
|
+
*
|
|
128
|
+
* Streams Server-Sent Events as each file is validated and uploaded:
|
|
129
|
+
* - file:uploading — when processing starts for a file
|
|
130
|
+
* - file:complete — when a file is successfully uploaded
|
|
131
|
+
* - file:error — when a file fails validation or upload
|
|
132
|
+
* - stream:complete — final summary with all results
|
|
133
|
+
*
|
|
134
|
+
*/ async unstable_uploadFilesStream (ctx) {
|
|
135
|
+
const { state: { userAbility, user }, request: { body, files: { files } = {} } } = ctx;
|
|
136
|
+
const uploadService = index.getService('upload');
|
|
137
|
+
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
138
|
+
ability: userAbility,
|
|
139
|
+
action: constants.ACTIONS.create,
|
|
140
|
+
model: constants.FILE_MODEL_UID
|
|
141
|
+
});
|
|
142
|
+
if (!pm.isAllowed) {
|
|
143
|
+
return ctx.forbidden();
|
|
144
|
+
}
|
|
145
|
+
if (_.isEmpty(files) || !Array.isArray(files) && files.size === 0) {
|
|
146
|
+
throw new utils.errors.ApplicationError('Files are empty');
|
|
147
|
+
}
|
|
148
|
+
// Take manual control of the response for SSE streaming
|
|
149
|
+
ctx.respond = false;
|
|
150
|
+
const res = ctx.res;
|
|
151
|
+
res.writeHead(200, {
|
|
152
|
+
'Content-Type': 'text/event-stream',
|
|
153
|
+
'Cache-Control': 'no-cache',
|
|
154
|
+
Connection: 'keep-alive'
|
|
155
|
+
});
|
|
156
|
+
const writeSSE = (event, data)=>{
|
|
157
|
+
res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
158
|
+
};
|
|
159
|
+
// Normalize files to an array
|
|
160
|
+
const filesArray = Array.isArray(files) ? files : [
|
|
161
|
+
files
|
|
162
|
+
];
|
|
163
|
+
const total = filesArray.length;
|
|
164
|
+
// Parse fileInfo from body
|
|
165
|
+
// Multipart forms send fileInfo as either:
|
|
166
|
+
// - An array of JSON strings (one per file)
|
|
167
|
+
// - A single JSON string (one file, or a JSON-encoded array)
|
|
168
|
+
let parsedFileInfo = [];
|
|
169
|
+
if (body?.fileInfo) {
|
|
170
|
+
const raw = body.fileInfo;
|
|
171
|
+
if (Array.isArray(raw)) {
|
|
172
|
+
parsedFileInfo = raw.map((fi)=>typeof fi === 'string' ? JSON.parse(fi) : fi);
|
|
173
|
+
} else if (typeof raw === 'string') {
|
|
174
|
+
const parsed = JSON.parse(raw);
|
|
175
|
+
// Handle case where a single string contains a JSON array
|
|
176
|
+
parsedFileInfo = Array.isArray(parsed) ? parsed : [
|
|
177
|
+
parsed
|
|
178
|
+
];
|
|
179
|
+
} else {
|
|
180
|
+
parsedFileInfo = [
|
|
181
|
+
raw
|
|
182
|
+
];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const uploadErrors = [];
|
|
186
|
+
const successfulFiles = [];
|
|
187
|
+
// Process each file sequentially with inline validation
|
|
188
|
+
for(let i = 0; i < filesArray.length; i += 1){
|
|
189
|
+
const file = filesArray[i];
|
|
190
|
+
const fileName = file.originalFilename || 'unknown';
|
|
191
|
+
const fileInfo = parsedFileInfo[i] || {
|
|
192
|
+
name: fileName,
|
|
193
|
+
caption: null,
|
|
194
|
+
alternativeText: null,
|
|
195
|
+
folder: null
|
|
196
|
+
};
|
|
197
|
+
writeSSE('file:uploading', {
|
|
198
|
+
name: fileName,
|
|
199
|
+
index: i,
|
|
200
|
+
total,
|
|
201
|
+
size: file.size || 0
|
|
202
|
+
});
|
|
203
|
+
try {
|
|
204
|
+
// Validate this single file using security checks
|
|
205
|
+
const { validFiles, errors: validationErrors } = await mimeValidation.prepareUploadRequest(file, {
|
|
206
|
+
fileInfo: JSON.stringify(fileInfo)
|
|
207
|
+
}, strapi);
|
|
208
|
+
if (validFiles.length === 0) {
|
|
209
|
+
const errorMessage = validationErrors[0]?.message || 'Validation failed';
|
|
210
|
+
uploadErrors.push({
|
|
211
|
+
name: fileName,
|
|
212
|
+
message: errorMessage
|
|
213
|
+
});
|
|
214
|
+
writeSSE('file:error', {
|
|
215
|
+
name: fileName,
|
|
216
|
+
index: i,
|
|
217
|
+
message: errorMessage
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
// Validate using the already-parsed single fileInfo object directly
|
|
221
|
+
const data = await upload.validateUploadBody({
|
|
222
|
+
fileInfo
|
|
223
|
+
}, false);
|
|
224
|
+
const [uploadedFile] = await uploadService.upload({
|
|
225
|
+
data,
|
|
226
|
+
files: [
|
|
227
|
+
validFiles[0]
|
|
228
|
+
]
|
|
229
|
+
}, {
|
|
230
|
+
user
|
|
231
|
+
});
|
|
232
|
+
// Sign file url
|
|
233
|
+
const signedFile = await index.getService('file').signFileUrls(uploadedFile);
|
|
234
|
+
successfulFiles.push(signedFile);
|
|
235
|
+
writeSSE('file:complete', {
|
|
236
|
+
name: fileName,
|
|
237
|
+
index: i,
|
|
238
|
+
file: signedFile
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
243
|
+
uploadErrors.push({
|
|
244
|
+
name: fileName,
|
|
245
|
+
message: errorMessage
|
|
246
|
+
});
|
|
247
|
+
writeSSE('file:error', {
|
|
248
|
+
name: fileName,
|
|
249
|
+
index: i,
|
|
250
|
+
message: errorMessage
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Track image upload metric once if any images were uploaded
|
|
255
|
+
if (successfulFiles.some((file)=>file.mime?.startsWith('image/'))) {
|
|
256
|
+
await index.getService('metrics').trackUsage('didUploadImage');
|
|
257
|
+
}
|
|
258
|
+
// Send final stream summary
|
|
259
|
+
writeSSE('stream:complete', {
|
|
260
|
+
data: await pm.sanitizeOutput(successfulFiles, {
|
|
261
|
+
action: constants.ACTIONS.read
|
|
262
|
+
}),
|
|
263
|
+
errors: uploadErrors
|
|
264
|
+
});
|
|
265
|
+
res.end();
|
|
266
|
+
},
|
|
118
267
|
// TODO: split into multiple endpoints
|
|
119
268
|
async upload (ctx) {
|
|
120
269
|
const { query: { id }, request: { files: { files } = {} } } = ctx;
|