@strapi/upload 5.33.2 → 5.33.4
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/MediaLibraryDialog/MediaLibraryDialog.js +2 -2
- package/dist/admin/components/MediaLibraryDialog/MediaLibraryDialog.js.map +1 -1
- package/dist/admin/components/MediaLibraryDialog/MediaLibraryDialog.mjs +2 -2
- package/dist/admin/components/MediaLibraryDialog/MediaLibraryDialog.mjs.map +1 -1
- package/dist/admin/package.json.js +9 -9
- package/dist/admin/package.json.mjs +9 -9
- package/dist/admin/src/components/MediaLibraryDialog/MediaLibraryDialog.d.ts +2 -1
- package/dist/server/controllers/admin-upload.js +6 -36
- package/dist/server/controllers/admin-upload.js.map +1 -1
- package/dist/server/controllers/admin-upload.mjs +7 -37
- package/dist/server/controllers/admin-upload.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +13 -11
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +13 -11
- package/dist/server/controllers/content-api.mjs.map +1 -1
- 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/utils/mime-validation.d.ts +8 -0
- package/dist/server/src/utils/mime-validation.d.ts.map +1 -1
- package/dist/server/utils/mime-validation.js +44 -0
- package/dist/server/utils/mime-validation.js.map +1 -1
- package/dist/server/utils/mime-validation.mjs +44 -1
- package/dist/server/utils/mime-validation.mjs.map +1 -1
- package/package.json +9 -9
|
@@ -36,7 +36,7 @@ const MediaLibraryDialog = ({ onClose, onSelectAssets, allowedTypes = [
|
|
|
36
36
|
'images',
|
|
37
37
|
'videos',
|
|
38
38
|
'audios'
|
|
39
|
-
] })=>{
|
|
39
|
+
], multiple = true })=>{
|
|
40
40
|
const [step, setStep] = React__namespace.useState(STEPS.AssetSelect);
|
|
41
41
|
const [folderId, setFolderId] = React__namespace.useState(null);
|
|
42
42
|
switch(step){
|
|
@@ -50,7 +50,7 @@ const MediaLibraryDialog = ({ onClose, onSelectAssets, allowedTypes = [
|
|
|
50
50
|
onAddAsset: ()=>setStep(STEPS.AssetUpload),
|
|
51
51
|
onAddFolder: ()=>setStep(STEPS.FolderCreate),
|
|
52
52
|
onChangeFolder: (folderId)=>setFolderId(folderId),
|
|
53
|
-
multiple:
|
|
53
|
+
multiple: multiple
|
|
54
54
|
});
|
|
55
55
|
case STEPS.FolderCreate:
|
|
56
56
|
return /*#__PURE__*/ jsxRuntime.jsx(EditFolderDialog.EditFolderDialog, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaLibraryDialog.js","sources":["../../../../admin/src/components/MediaLibraryDialog/MediaLibraryDialog.tsx"],"sourcesContent":["// TODO: find a better naming convention for the file that was an index file before\nimport * as React from 'react';\n\nimport { AssetDialog } from '../AssetDialog/AssetDialog';\nimport { EditFolderDialog } from '../EditFolderDialog/EditFolderDialog';\nimport { UploadAssetDialog } from '../UploadAssetDialog/UploadAssetDialog';\n\nconst STEPS = {\n AssetSelect: 'SelectAsset',\n AssetUpload: 'UploadAsset',\n FolderCreate: 'FolderCreate',\n};\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { AllowedTypes } from '../AssetCard/AssetCard';\nexport interface MediaLibraryDialogProps {\n allowedTypes?: AllowedTypes[];\n onClose: () => void;\n onSelectAssets: (selectedAssets: File[]) => void;\n}\n\nexport const MediaLibraryDialog = ({\n onClose,\n onSelectAssets,\n allowedTypes = ['files', 'images', 'videos', 'audios'],\n}: MediaLibraryDialogProps) => {\n const [step, setStep] = React.useState(STEPS.AssetSelect);\n const [folderId, setFolderId] = React.useState<number | null>(null);\n\n switch (step) {\n case STEPS.AssetSelect:\n return (\n <AssetDialog\n allowedTypes={allowedTypes}\n folderId={folderId}\n open\n onClose={onClose}\n onValidate={onSelectAssets}\n onAddAsset={() => setStep(STEPS.AssetUpload)}\n onAddFolder={() => setStep(STEPS.FolderCreate)}\n onChangeFolder={(folderId) => setFolderId(folderId)}\n multiple\n />\n );\n\n case STEPS.FolderCreate:\n return (\n <EditFolderDialog\n open\n onClose={() => setStep(STEPS.AssetSelect)}\n parentFolderId={folderId}\n />\n );\n\n default:\n return (\n <UploadAssetDialog open onClose={() => setStep(STEPS.AssetSelect)} folderId={folderId} />\n );\n }\n};\n"],"names":["STEPS","AssetSelect","AssetUpload","FolderCreate","MediaLibraryDialog","onClose","onSelectAssets","allowedTypes","step","setStep","React","useState","folderId","setFolderId","_jsx","AssetDialog","open","onValidate","onAddAsset","onAddFolder","onChangeFolder","
|
|
1
|
+
{"version":3,"file":"MediaLibraryDialog.js","sources":["../../../../admin/src/components/MediaLibraryDialog/MediaLibraryDialog.tsx"],"sourcesContent":["// TODO: find a better naming convention for the file that was an index file before\nimport * as React from 'react';\n\nimport { AssetDialog } from '../AssetDialog/AssetDialog';\nimport { EditFolderDialog } from '../EditFolderDialog/EditFolderDialog';\nimport { UploadAssetDialog } from '../UploadAssetDialog/UploadAssetDialog';\n\nconst STEPS = {\n AssetSelect: 'SelectAsset',\n AssetUpload: 'UploadAsset',\n FolderCreate: 'FolderCreate',\n};\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { AllowedTypes } from '../AssetCard/AssetCard';\nexport interface MediaLibraryDialogProps {\n allowedTypes?: AllowedTypes[];\n multiple?: boolean;\n onClose: () => void;\n onSelectAssets: (selectedAssets: File[]) => void;\n}\n\nexport const MediaLibraryDialog = ({\n onClose,\n onSelectAssets,\n allowedTypes = ['files', 'images', 'videos', 'audios'],\n multiple = true,\n}: MediaLibraryDialogProps) => {\n const [step, setStep] = React.useState(STEPS.AssetSelect);\n const [folderId, setFolderId] = React.useState<number | null>(null);\n\n switch (step) {\n case STEPS.AssetSelect:\n return (\n <AssetDialog\n allowedTypes={allowedTypes}\n folderId={folderId}\n open\n onClose={onClose}\n onValidate={onSelectAssets}\n onAddAsset={() => setStep(STEPS.AssetUpload)}\n onAddFolder={() => setStep(STEPS.FolderCreate)}\n onChangeFolder={(folderId) => setFolderId(folderId)}\n multiple={multiple}\n />\n );\n\n case STEPS.FolderCreate:\n return (\n <EditFolderDialog\n open\n onClose={() => setStep(STEPS.AssetSelect)}\n parentFolderId={folderId}\n />\n );\n\n default:\n return (\n <UploadAssetDialog open onClose={() => setStep(STEPS.AssetSelect)} folderId={folderId} />\n );\n }\n};\n"],"names":["STEPS","AssetSelect","AssetUpload","FolderCreate","MediaLibraryDialog","onClose","onSelectAssets","allowedTypes","multiple","step","setStep","React","useState","folderId","setFolderId","_jsx","AssetDialog","open","onValidate","onAddAsset","onAddFolder","onChangeFolder","EditFolderDialog","parentFolderId","UploadAssetDialog"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAOA,MAAMA,KAAQ,GAAA;IACZC,WAAa,EAAA,aAAA;IACbC,WAAa,EAAA,aAAA;IACbC,YAAc,EAAA;AAChB,CAAA;AAWO,MAAMC,qBAAqB,CAAC,EACjCC,OAAO,EACPC,cAAc,EACdC,YAAe,GAAA;AAAC,IAAA,OAAA;AAAS,IAAA,QAAA;AAAU,IAAA,QAAA;AAAU,IAAA;CAAS,EACtDC,QAAAA,GAAW,IAAI,EACS,GAAA;IACxB,MAAM,CAACC,MAAMC,OAAQ,CAAA,GAAGC,iBAAMC,QAAQ,CAACZ,MAAMC,WAAW,CAAA;AACxD,IAAA,MAAM,CAACY,QAAUC,EAAAA,WAAAA,CAAY,GAAGH,gBAAAA,CAAMC,QAAQ,CAAgB,IAAA,CAAA;IAE9D,OAAQH,IAAAA;AACN,QAAA,KAAKT,MAAMC,WAAW;AACpB,YAAA,qBACEc,cAACC,CAAAA,uBAAAA,EAAAA;gBACCT,YAAcA,EAAAA,YAAAA;gBACdM,QAAUA,EAAAA,QAAAA;gBACVI,IAAI,EAAA,IAAA;gBACJZ,OAASA,EAAAA,OAAAA;gBACTa,UAAYZ,EAAAA,cAAAA;gBACZa,UAAY,EAAA,IAAMT,OAAQV,CAAAA,KAAAA,CAAME,WAAW,CAAA;gBAC3CkB,WAAa,EAAA,IAAMV,OAAQV,CAAAA,KAAAA,CAAMG,YAAY,CAAA;gBAC7CkB,cAAgB,EAAA,CAACR,WAAaC,WAAYD,CAAAA,QAAAA,CAAAA;gBAC1CL,QAAUA,EAAAA;;AAIhB,QAAA,KAAKR,MAAMG,YAAY;AACrB,YAAA,qBACEY,cAACO,CAAAA,iCAAAA,EAAAA;gBACCL,IAAI,EAAA,IAAA;gBACJZ,OAAS,EAAA,IAAMK,OAAQV,CAAAA,KAAAA,CAAMC,WAAW,CAAA;gBACxCsB,cAAgBV,EAAAA;;AAItB,QAAA;AACE,YAAA,qBACEE,cAACS,CAAAA,mCAAAA,EAAAA;gBAAkBP,IAAI,EAAA,IAAA;gBAACZ,OAAS,EAAA,IAAMK,OAAQV,CAAAA,KAAAA,CAAMC,WAAW,CAAA;gBAAGY,QAAUA,EAAAA;;AAEnF;AACF;;;;"}
|
|
@@ -15,7 +15,7 @@ const MediaLibraryDialog = ({ onClose, onSelectAssets, allowedTypes = [
|
|
|
15
15
|
'images',
|
|
16
16
|
'videos',
|
|
17
17
|
'audios'
|
|
18
|
-
] })=>{
|
|
18
|
+
], multiple = true })=>{
|
|
19
19
|
const [step, setStep] = React.useState(STEPS.AssetSelect);
|
|
20
20
|
const [folderId, setFolderId] = React.useState(null);
|
|
21
21
|
switch(step){
|
|
@@ -29,7 +29,7 @@ const MediaLibraryDialog = ({ onClose, onSelectAssets, allowedTypes = [
|
|
|
29
29
|
onAddAsset: ()=>setStep(STEPS.AssetUpload),
|
|
30
30
|
onAddFolder: ()=>setStep(STEPS.FolderCreate),
|
|
31
31
|
onChangeFolder: (folderId)=>setFolderId(folderId),
|
|
32
|
-
multiple:
|
|
32
|
+
multiple: multiple
|
|
33
33
|
});
|
|
34
34
|
case STEPS.FolderCreate:
|
|
35
35
|
return /*#__PURE__*/ jsx(EditFolderDialog, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaLibraryDialog.mjs","sources":["../../../../admin/src/components/MediaLibraryDialog/MediaLibraryDialog.tsx"],"sourcesContent":["// TODO: find a better naming convention for the file that was an index file before\nimport * as React from 'react';\n\nimport { AssetDialog } from '../AssetDialog/AssetDialog';\nimport { EditFolderDialog } from '../EditFolderDialog/EditFolderDialog';\nimport { UploadAssetDialog } from '../UploadAssetDialog/UploadAssetDialog';\n\nconst STEPS = {\n AssetSelect: 'SelectAsset',\n AssetUpload: 'UploadAsset',\n FolderCreate: 'FolderCreate',\n};\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { AllowedTypes } from '../AssetCard/AssetCard';\nexport interface MediaLibraryDialogProps {\n allowedTypes?: AllowedTypes[];\n onClose: () => void;\n onSelectAssets: (selectedAssets: File[]) => void;\n}\n\nexport const MediaLibraryDialog = ({\n onClose,\n onSelectAssets,\n allowedTypes = ['files', 'images', 'videos', 'audios'],\n}: MediaLibraryDialogProps) => {\n const [step, setStep] = React.useState(STEPS.AssetSelect);\n const [folderId, setFolderId] = React.useState<number | null>(null);\n\n switch (step) {\n case STEPS.AssetSelect:\n return (\n <AssetDialog\n allowedTypes={allowedTypes}\n folderId={folderId}\n open\n onClose={onClose}\n onValidate={onSelectAssets}\n onAddAsset={() => setStep(STEPS.AssetUpload)}\n onAddFolder={() => setStep(STEPS.FolderCreate)}\n onChangeFolder={(folderId) => setFolderId(folderId)}\n multiple\n />\n );\n\n case STEPS.FolderCreate:\n return (\n <EditFolderDialog\n open\n onClose={() => setStep(STEPS.AssetSelect)}\n parentFolderId={folderId}\n />\n );\n\n default:\n return (\n <UploadAssetDialog open onClose={() => setStep(STEPS.AssetSelect)} folderId={folderId} />\n );\n }\n};\n"],"names":["STEPS","AssetSelect","AssetUpload","FolderCreate","MediaLibraryDialog","onClose","onSelectAssets","allowedTypes","step","setStep","React","useState","folderId","setFolderId","_jsx","AssetDialog","open","onValidate","onAddAsset","onAddFolder","onChangeFolder","
|
|
1
|
+
{"version":3,"file":"MediaLibraryDialog.mjs","sources":["../../../../admin/src/components/MediaLibraryDialog/MediaLibraryDialog.tsx"],"sourcesContent":["// TODO: find a better naming convention for the file that was an index file before\nimport * as React from 'react';\n\nimport { AssetDialog } from '../AssetDialog/AssetDialog';\nimport { EditFolderDialog } from '../EditFolderDialog/EditFolderDialog';\nimport { UploadAssetDialog } from '../UploadAssetDialog/UploadAssetDialog';\n\nconst STEPS = {\n AssetSelect: 'SelectAsset',\n AssetUpload: 'UploadAsset',\n FolderCreate: 'FolderCreate',\n};\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { AllowedTypes } from '../AssetCard/AssetCard';\nexport interface MediaLibraryDialogProps {\n allowedTypes?: AllowedTypes[];\n multiple?: boolean;\n onClose: () => void;\n onSelectAssets: (selectedAssets: File[]) => void;\n}\n\nexport const MediaLibraryDialog = ({\n onClose,\n onSelectAssets,\n allowedTypes = ['files', 'images', 'videos', 'audios'],\n multiple = true,\n}: MediaLibraryDialogProps) => {\n const [step, setStep] = React.useState(STEPS.AssetSelect);\n const [folderId, setFolderId] = React.useState<number | null>(null);\n\n switch (step) {\n case STEPS.AssetSelect:\n return (\n <AssetDialog\n allowedTypes={allowedTypes}\n folderId={folderId}\n open\n onClose={onClose}\n onValidate={onSelectAssets}\n onAddAsset={() => setStep(STEPS.AssetUpload)}\n onAddFolder={() => setStep(STEPS.FolderCreate)}\n onChangeFolder={(folderId) => setFolderId(folderId)}\n multiple={multiple}\n />\n );\n\n case STEPS.FolderCreate:\n return (\n <EditFolderDialog\n open\n onClose={() => setStep(STEPS.AssetSelect)}\n parentFolderId={folderId}\n />\n );\n\n default:\n return (\n <UploadAssetDialog open onClose={() => setStep(STEPS.AssetSelect)} folderId={folderId} />\n );\n }\n};\n"],"names":["STEPS","AssetSelect","AssetUpload","FolderCreate","MediaLibraryDialog","onClose","onSelectAssets","allowedTypes","multiple","step","setStep","React","useState","folderId","setFolderId","_jsx","AssetDialog","open","onValidate","onAddAsset","onAddFolder","onChangeFolder","EditFolderDialog","parentFolderId","UploadAssetDialog"],"mappings":";;;;;;AAAA;AAOA,MAAMA,KAAQ,GAAA;IACZC,WAAa,EAAA,aAAA;IACbC,WAAa,EAAA,aAAA;IACbC,YAAc,EAAA;AAChB,CAAA;AAWO,MAAMC,qBAAqB,CAAC,EACjCC,OAAO,EACPC,cAAc,EACdC,YAAe,GAAA;AAAC,IAAA,OAAA;AAAS,IAAA,QAAA;AAAU,IAAA,QAAA;AAAU,IAAA;CAAS,EACtDC,QAAAA,GAAW,IAAI,EACS,GAAA;IACxB,MAAM,CAACC,MAAMC,OAAQ,CAAA,GAAGC,MAAMC,QAAQ,CAACZ,MAAMC,WAAW,CAAA;AACxD,IAAA,MAAM,CAACY,QAAUC,EAAAA,WAAAA,CAAY,GAAGH,KAAAA,CAAMC,QAAQ,CAAgB,IAAA,CAAA;IAE9D,OAAQH,IAAAA;AACN,QAAA,KAAKT,MAAMC,WAAW;AACpB,YAAA,qBACEc,GAACC,CAAAA,WAAAA,EAAAA;gBACCT,YAAcA,EAAAA,YAAAA;gBACdM,QAAUA,EAAAA,QAAAA;gBACVI,IAAI,EAAA,IAAA;gBACJZ,OAASA,EAAAA,OAAAA;gBACTa,UAAYZ,EAAAA,cAAAA;gBACZa,UAAY,EAAA,IAAMT,OAAQV,CAAAA,KAAAA,CAAME,WAAW,CAAA;gBAC3CkB,WAAa,EAAA,IAAMV,OAAQV,CAAAA,KAAAA,CAAMG,YAAY,CAAA;gBAC7CkB,cAAgB,EAAA,CAACR,WAAaC,WAAYD,CAAAA,QAAAA,CAAAA;gBAC1CL,QAAUA,EAAAA;;AAIhB,QAAA,KAAKR,MAAMG,YAAY;AACrB,YAAA,qBACEY,GAACO,CAAAA,gBAAAA,EAAAA;gBACCL,IAAI,EAAA,IAAA;gBACJZ,OAAS,EAAA,IAAMK,OAAQV,CAAAA,KAAAA,CAAMC,WAAW,CAAA;gBACxCsB,cAAgBV,EAAAA;;AAItB,QAAA;AACE,YAAA,qBACEE,GAACS,CAAAA,iBAAAA,EAAAA;gBAAkBP,IAAI,EAAA,IAAA;gBAACZ,OAAS,EAAA,IAAMK,OAAQV,CAAAA,KAAAA,CAAMC,WAAW,CAAA;gBAAGY,QAAUA,EAAAA;;AAEnF;AACF;;;;"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var name = "@strapi/upload";
|
|
6
|
-
var version = "5.33.
|
|
6
|
+
var version = "5.33.4";
|
|
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 = {
|
|
@@ -65,10 +65,10 @@ var scripts = {
|
|
|
65
65
|
var dependencies = {
|
|
66
66
|
"@mux/mux-player-react": "3.1.0",
|
|
67
67
|
"@reduxjs/toolkit": "1.9.7",
|
|
68
|
-
"@strapi/design-system": "2.
|
|
69
|
-
"@strapi/icons": "2.
|
|
70
|
-
"@strapi/provider-upload-local": "5.33.
|
|
71
|
-
"@strapi/utils": "5.33.
|
|
68
|
+
"@strapi/design-system": "2.1.2",
|
|
69
|
+
"@strapi/icons": "2.1.2",
|
|
70
|
+
"@strapi/provider-upload-local": "5.33.4",
|
|
71
|
+
"@strapi/utils": "5.33.4",
|
|
72
72
|
"byte-size": "8.1.1",
|
|
73
73
|
cropperjs: "1.6.1",
|
|
74
74
|
"date-fns": "2.30.0",
|
|
@@ -81,7 +81,7 @@ var dependencies = {
|
|
|
81
81
|
lodash: "4.17.21",
|
|
82
82
|
"mime-types": "2.1.35",
|
|
83
83
|
"prop-types": "^15.8.1",
|
|
84
|
-
qs: "6.
|
|
84
|
+
qs: "6.14.1",
|
|
85
85
|
"react-dnd": "16.0.1",
|
|
86
86
|
"react-intl": "6.6.2",
|
|
87
87
|
"react-query": "3.39.3",
|
|
@@ -92,8 +92,8 @@ var dependencies = {
|
|
|
92
92
|
zod: "3.25.67"
|
|
93
93
|
};
|
|
94
94
|
var devDependencies = {
|
|
95
|
-
"@strapi/admin": "5.33.
|
|
96
|
-
"@strapi/types": "5.33.
|
|
95
|
+
"@strapi/admin": "5.33.4",
|
|
96
|
+
"@strapi/types": "5.33.4",
|
|
97
97
|
"@testing-library/dom": "10.4.1",
|
|
98
98
|
"@testing-library/react": "16.3.0",
|
|
99
99
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -103,7 +103,7 @@ var devDependencies = {
|
|
|
103
103
|
"@types/koa-range": "0.3.5",
|
|
104
104
|
"@types/koa-static": "4.0.2",
|
|
105
105
|
formidable: "3.5.4",
|
|
106
|
-
koa: "2.16.
|
|
106
|
+
koa: "2.16.3",
|
|
107
107
|
"koa-body": "6.0.1",
|
|
108
108
|
msw: "1.3.0",
|
|
109
109
|
react: "18.3.1",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var name = "@strapi/upload";
|
|
2
|
-
var version = "5.33.
|
|
2
|
+
var version = "5.33.4";
|
|
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 = {
|
|
@@ -61,10 +61,10 @@ var scripts = {
|
|
|
61
61
|
var dependencies = {
|
|
62
62
|
"@mux/mux-player-react": "3.1.0",
|
|
63
63
|
"@reduxjs/toolkit": "1.9.7",
|
|
64
|
-
"@strapi/design-system": "2.
|
|
65
|
-
"@strapi/icons": "2.
|
|
66
|
-
"@strapi/provider-upload-local": "5.33.
|
|
67
|
-
"@strapi/utils": "5.33.
|
|
64
|
+
"@strapi/design-system": "2.1.2",
|
|
65
|
+
"@strapi/icons": "2.1.2",
|
|
66
|
+
"@strapi/provider-upload-local": "5.33.4",
|
|
67
|
+
"@strapi/utils": "5.33.4",
|
|
68
68
|
"byte-size": "8.1.1",
|
|
69
69
|
cropperjs: "1.6.1",
|
|
70
70
|
"date-fns": "2.30.0",
|
|
@@ -77,7 +77,7 @@ var dependencies = {
|
|
|
77
77
|
lodash: "4.17.21",
|
|
78
78
|
"mime-types": "2.1.35",
|
|
79
79
|
"prop-types": "^15.8.1",
|
|
80
|
-
qs: "6.
|
|
80
|
+
qs: "6.14.1",
|
|
81
81
|
"react-dnd": "16.0.1",
|
|
82
82
|
"react-intl": "6.6.2",
|
|
83
83
|
"react-query": "3.39.3",
|
|
@@ -88,8 +88,8 @@ var dependencies = {
|
|
|
88
88
|
zod: "3.25.67"
|
|
89
89
|
};
|
|
90
90
|
var devDependencies = {
|
|
91
|
-
"@strapi/admin": "5.33.
|
|
92
|
-
"@strapi/types": "5.33.
|
|
91
|
+
"@strapi/admin": "5.33.4",
|
|
92
|
+
"@strapi/types": "5.33.4",
|
|
93
93
|
"@testing-library/dom": "10.4.1",
|
|
94
94
|
"@testing-library/react": "16.3.0",
|
|
95
95
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -99,7 +99,7 @@ var devDependencies = {
|
|
|
99
99
|
"@types/koa-range": "0.3.5",
|
|
100
100
|
"@types/koa-static": "4.0.2",
|
|
101
101
|
formidable: "3.5.4",
|
|
102
|
-
koa: "2.16.
|
|
102
|
+
koa: "2.16.3",
|
|
103
103
|
"koa-body": "6.0.1",
|
|
104
104
|
msw: "1.3.0",
|
|
105
105
|
react: "18.3.1",
|
|
@@ -2,7 +2,8 @@ import type { File } from '../../../../shared/contracts/files';
|
|
|
2
2
|
import type { AllowedTypes } from '../AssetCard/AssetCard';
|
|
3
3
|
export interface MediaLibraryDialogProps {
|
|
4
4
|
allowedTypes?: AllowedTypes[];
|
|
5
|
+
multiple?: boolean;
|
|
5
6
|
onClose: () => void;
|
|
6
7
|
onSelectAssets: (selectedAssets: File[]) => void;
|
|
7
8
|
}
|
|
8
|
-
export declare const MediaLibraryDialog: ({ onClose, onSelectAssets, allowedTypes, }: MediaLibraryDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare const MediaLibraryDialog: ({ onClose, onSelectAssets, allowedTypes, multiple, }: MediaLibraryDialogProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -49,20 +49,11 @@ 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
|
|
53
|
-
|
|
54
|
-
const { error } = securityResults.errors[0];
|
|
55
|
-
switch(error.code){
|
|
56
|
-
case 'MIME_TYPE_NOT_ALLOWED':
|
|
57
|
-
throw new utils.errors.ValidationError(error.message, error.details);
|
|
58
|
-
default:
|
|
59
|
-
throw new utils.errors.ApplicationError(error.message, error.details);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const data = await upload.validateUploadBody(body);
|
|
52
|
+
const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(files, body, strapi);
|
|
53
|
+
const data = await upload.validateUploadBody(filteredBody);
|
|
63
54
|
const replacedFile = await uploadService.replace(id, {
|
|
64
55
|
data,
|
|
65
|
-
file:
|
|
56
|
+
file: validFiles[0]
|
|
66
57
|
}, {
|
|
67
58
|
user
|
|
68
59
|
});
|
|
@@ -83,31 +74,10 @@ var adminUpload = {
|
|
|
83
74
|
if (!pm.isAllowed) {
|
|
84
75
|
return ctx.forbidden();
|
|
85
76
|
}
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
throw new utils.errors.ValidationError(securityResults.errors[0].error.message, securityResults.errors[0].error.details);
|
|
89
|
-
}
|
|
90
|
-
let filteredBody = body;
|
|
91
|
-
if (body?.fileInfo && Array.isArray(body.fileInfo)) {
|
|
92
|
-
const filteredFileInfo = body.fileInfo.filter((fi)=>{
|
|
93
|
-
const info = typeof fi === 'string' ? JSON.parse(fi) : fi;
|
|
94
|
-
return securityResults.validFileNames.includes(info.name);
|
|
95
|
-
});
|
|
96
|
-
if (filteredFileInfo.length === 1) {
|
|
97
|
-
filteredBody = {
|
|
98
|
-
...body,
|
|
99
|
-
fileInfo: filteredFileInfo[0]
|
|
100
|
-
};
|
|
101
|
-
} else {
|
|
102
|
-
filteredBody = {
|
|
103
|
-
...body,
|
|
104
|
-
fileInfo: filteredFileInfo
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const isMultipleFiles = Array.isArray(filteredBody.fileInfo) && filteredBody.fileInfo.length > 1;
|
|
77
|
+
const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(files, body, strapi);
|
|
78
|
+
const isMultipleFiles = validFiles.length > 1;
|
|
109
79
|
const data = await upload.validateUploadBody(filteredBody, isMultipleFiles);
|
|
110
|
-
let filesArray =
|
|
80
|
+
let filesArray = validFiles;
|
|
111
81
|
if (data.fileInfo && Array.isArray(data.fileInfo) && filesArray.length === data.fileInfo.length) {
|
|
112
82
|
// Reorder filesArray to match data.fileInfo order
|
|
113
83
|
const alignedFilesArray = data.fileInfo.map((info)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-upload.js","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { FileInfo } from '../types';\nimport { enforceUploadSecurity } from '../utils/mime-validation';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n return pm.sanitizeOutput(updated, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(files)) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const securityResults = await enforceUploadSecurity(files, strapi);\n\n if (securityResults.errors.length > 0) {\n const { error } = securityResults.errors[0];\n switch (error.code) {\n case 'MIME_TYPE_NOT_ALLOWED':\n throw new errors.ValidationError(error.message, error.details);\n default:\n throw new errors.ApplicationError(error.message, error.details);\n }\n }\n\n const data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(\n id,\n { data, file: securityResults.validFiles[0] },\n { user }\n );\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const securityResults = await enforceUploadSecurity(files, strapi);\n\n if (securityResults.validFiles.length === 0) {\n throw new errors.ValidationError(\n securityResults.errors[0].error.message,\n securityResults.errors[0].error.details\n );\n }\n\n let filteredBody = body;\n if (body?.fileInfo && Array.isArray(body.fileInfo)) {\n const filteredFileInfo = body.fileInfo.filter((fi: string) => {\n const info = typeof fi === 'string' ? JSON.parse(fi) : fi;\n return securityResults.validFileNames.includes(info.name);\n });\n\n if (filteredFileInfo.length === 1) {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo[0],\n };\n } else {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo,\n };\n }\n }\n\n const isMultipleFiles =\n Array.isArray(filteredBody.fileInfo) && filteredBody.fileInfo.length > 1;\n\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = securityResults.validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - use thumbnail versions for AI processing\n if (await aiMetadataService.isEnabled()) {\n try {\n // Use thumbnail URLs instead of original files\n const thumbnailFiles = uploadedFiles.map(\n (file) =>\n ({\n filepath: file.formats?.thumbnail?.url || file.url, // Use thumbnail if available\n mimetype: file.mime,\n originalFilename: file.name,\n size: file.formats?.thumbnail?.size || file.size,\n provider: file.provider,\n }) as unknown as any\n );\n\n const metadataResults = await aiMetadataService.processFiles(thumbnailFiles);\n\n // Update the uploaded files with AI metadata\n await Promise.all(\n uploadedFiles.map(async (uploadedFile, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n await uploadService.updateFileInfo(\n uploadedFile.id,\n {\n alternativeText: aiMetadata.altText,\n caption: aiMetadata.caption,\n },\n { user }\n );\n\n uploadedFiles[index].alternativeText = aiMetadata.altText;\n uploadedFiles[index].caption = aiMetadata.caption;\n }\n })\n );\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","Array","isArray","ApplicationError","securityResults","enforceUploadSecurity","strapi","length","error","code","message","details","replacedFile","replace","validFiles","signedFile","signFileUrls","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","filteredBody","filteredFileInfo","filter","fi","info","JSON","parse","validFileNames","includes","name","isMultipleFiles","filesArray","alignedFilesArray","find","originalFilename","Boolean","uploadedFiles","upload","some","mime","startsWith","trackUsage","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","size","provider","metadataResults","processFiles","Promise","all","uploadedFile","index","aiMetadata","alternativeText","altText","caption","log","warn","Error","String","signedFiles","status","_","isEmpty"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin-upload.js","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { FileInfo } from '../types';\nimport { prepareUploadRequest } from '../utils/mime-validation';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n return pm.sanitizeOutput(updated, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(files)) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: validFiles[0] }, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);\n\n const isMultipleFiles = validFiles.length > 1;\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - use thumbnail versions for AI processing\n if (await aiMetadataService.isEnabled()) {\n try {\n // Use thumbnail URLs instead of original files\n const thumbnailFiles = uploadedFiles.map(\n (file) =>\n ({\n filepath: file.formats?.thumbnail?.url || file.url, // Use thumbnail if available\n mimetype: file.mime,\n originalFilename: file.name,\n size: file.formats?.thumbnail?.size || file.size,\n provider: file.provider,\n }) as unknown as any\n );\n\n const metadataResults = await aiMetadataService.processFiles(thumbnailFiles);\n\n // Update the uploaded files with AI metadata\n await Promise.all(\n uploadedFiles.map(async (uploadedFile, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n await uploadService.updateFileInfo(\n uploadedFile.id,\n {\n alternativeText: aiMetadata.altText,\n caption: aiMetadata.caption,\n },\n { user }\n );\n\n uploadedFiles[index].alternativeText = aiMetadata.altText;\n uploadedFiles[index].caption = aiMetadata.caption;\n }\n })\n );\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","Array","isArray","ApplicationError","validFiles","filteredBody","prepareUploadRequest","strapi","replacedFile","replace","signedFile","signFileUrls","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","isMultipleFiles","length","filesArray","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","mime","startsWith","trackUsage","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","size","provider","metadataResults","processFiles","Promise","all","uploadedFile","index","aiMetadata","alternativeText","altText","caption","error","log","warn","Error","message","String","signedFiles","status","_","isEmpty"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { getService } from '../utils/index.mjs';
|
|
|
4
4
|
import { ACTIONS, FILE_MODEL_UID } from '../constants.mjs';
|
|
5
5
|
import { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload.mjs';
|
|
6
6
|
import { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions.mjs';
|
|
7
|
-
import {
|
|
7
|
+
import { prepareUploadRequest } from '../utils/mime-validation.mjs';
|
|
8
8
|
|
|
9
9
|
var adminUpload = {
|
|
10
10
|
async bulkUpdateFileInfo (ctx) {
|
|
@@ -47,20 +47,11 @@ var adminUpload = {
|
|
|
47
47
|
if (Array.isArray(files)) {
|
|
48
48
|
throw new errors.ApplicationError('Cannot replace a file with multiple ones');
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const { error } = securityResults.errors[0];
|
|
53
|
-
switch(error.code){
|
|
54
|
-
case 'MIME_TYPE_NOT_ALLOWED':
|
|
55
|
-
throw new errors.ValidationError(error.message, error.details);
|
|
56
|
-
default:
|
|
57
|
-
throw new errors.ApplicationError(error.message, error.details);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const data = await validateUploadBody(body);
|
|
50
|
+
const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);
|
|
51
|
+
const data = await validateUploadBody(filteredBody);
|
|
61
52
|
const replacedFile = await uploadService.replace(id, {
|
|
62
53
|
data,
|
|
63
|
-
file:
|
|
54
|
+
file: validFiles[0]
|
|
64
55
|
}, {
|
|
65
56
|
user
|
|
66
57
|
});
|
|
@@ -81,31 +72,10 @@ var adminUpload = {
|
|
|
81
72
|
if (!pm.isAllowed) {
|
|
82
73
|
return ctx.forbidden();
|
|
83
74
|
}
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
throw new errors.ValidationError(securityResults.errors[0].error.message, securityResults.errors[0].error.details);
|
|
87
|
-
}
|
|
88
|
-
let filteredBody = body;
|
|
89
|
-
if (body?.fileInfo && Array.isArray(body.fileInfo)) {
|
|
90
|
-
const filteredFileInfo = body.fileInfo.filter((fi)=>{
|
|
91
|
-
const info = typeof fi === 'string' ? JSON.parse(fi) : fi;
|
|
92
|
-
return securityResults.validFileNames.includes(info.name);
|
|
93
|
-
});
|
|
94
|
-
if (filteredFileInfo.length === 1) {
|
|
95
|
-
filteredBody = {
|
|
96
|
-
...body,
|
|
97
|
-
fileInfo: filteredFileInfo[0]
|
|
98
|
-
};
|
|
99
|
-
} else {
|
|
100
|
-
filteredBody = {
|
|
101
|
-
...body,
|
|
102
|
-
fileInfo: filteredFileInfo
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const isMultipleFiles = Array.isArray(filteredBody.fileInfo) && filteredBody.fileInfo.length > 1;
|
|
75
|
+
const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);
|
|
76
|
+
const isMultipleFiles = validFiles.length > 1;
|
|
107
77
|
const data = await validateUploadBody(filteredBody, isMultipleFiles);
|
|
108
|
-
let filesArray =
|
|
78
|
+
let filesArray = validFiles;
|
|
109
79
|
if (data.fileInfo && Array.isArray(data.fileInfo) && filesArray.length === data.fileInfo.length) {
|
|
110
80
|
// Reorder filesArray to match data.fileInfo order
|
|
111
81
|
const alignedFilesArray = data.fileInfo.map((info)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-upload.mjs","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { FileInfo } from '../types';\nimport { enforceUploadSecurity } from '../utils/mime-validation';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n return pm.sanitizeOutput(updated, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(files)) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const securityResults = await enforceUploadSecurity(files, strapi);\n\n if (securityResults.errors.length > 0) {\n const { error } = securityResults.errors[0];\n switch (error.code) {\n case 'MIME_TYPE_NOT_ALLOWED':\n throw new errors.ValidationError(error.message, error.details);\n default:\n throw new errors.ApplicationError(error.message, error.details);\n }\n }\n\n const data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(\n id,\n { data, file: securityResults.validFiles[0] },\n { user }\n );\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const securityResults = await enforceUploadSecurity(files, strapi);\n\n if (securityResults.validFiles.length === 0) {\n throw new errors.ValidationError(\n securityResults.errors[0].error.message,\n securityResults.errors[0].error.details\n );\n }\n\n let filteredBody = body;\n if (body?.fileInfo && Array.isArray(body.fileInfo)) {\n const filteredFileInfo = body.fileInfo.filter((fi: string) => {\n const info = typeof fi === 'string' ? JSON.parse(fi) : fi;\n return securityResults.validFileNames.includes(info.name);\n });\n\n if (filteredFileInfo.length === 1) {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo[0],\n };\n } else {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo,\n };\n }\n }\n\n const isMultipleFiles =\n Array.isArray(filteredBody.fileInfo) && filteredBody.fileInfo.length > 1;\n\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = securityResults.validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - use thumbnail versions for AI processing\n if (await aiMetadataService.isEnabled()) {\n try {\n // Use thumbnail URLs instead of original files\n const thumbnailFiles = uploadedFiles.map(\n (file) =>\n ({\n filepath: file.formats?.thumbnail?.url || file.url, // Use thumbnail if available\n mimetype: file.mime,\n originalFilename: file.name,\n size: file.formats?.thumbnail?.size || file.size,\n provider: file.provider,\n }) as unknown as any\n );\n\n const metadataResults = await aiMetadataService.processFiles(thumbnailFiles);\n\n // Update the uploaded files with AI metadata\n await Promise.all(\n uploadedFiles.map(async (uploadedFile, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n await uploadService.updateFileInfo(\n uploadedFile.id,\n {\n alternativeText: aiMetadata.altText,\n caption: aiMetadata.caption,\n },\n { user }\n );\n\n uploadedFiles[index].alternativeText = aiMetadata.altText;\n uploadedFiles[index].caption = aiMetadata.caption;\n }\n })\n );\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","Array","isArray","ApplicationError","securityResults","enforceUploadSecurity","strapi","length","error","code","message","details","replacedFile","replace","validFiles","signedFile","signFileUrls","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","filteredBody","filteredFileInfo","filter","fi","info","JSON","parse","validFileNames","includes","name","isMultipleFiles","filesArray","alignedFilesArray","find","originalFilename","Boolean","uploadedFiles","upload","some","mime","startsWith","trackUsage","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","size","provider","metadataResults","processFiles","Promise","all","uploadedFile","index","aiMetadata","alternativeText","altText","caption","log","warn","Error","String","signedFiles","status","_","isEmpty"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin-upload.mjs","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { FileInfo } from '../types';\nimport { prepareUploadRequest } from '../utils/mime-validation';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n return pm.sanitizeOutput(updated, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n ctx.body = await pm.sanitizeOutput(file, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(files)) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: validFiles[0] }, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const { validFiles, filteredBody } = await prepareUploadRequest(files, body, strapi);\n\n const isMultipleFiles = validFiles.length > 1;\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - use thumbnail versions for AI processing\n if (await aiMetadataService.isEnabled()) {\n try {\n // Use thumbnail URLs instead of original files\n const thumbnailFiles = uploadedFiles.map(\n (file) =>\n ({\n filepath: file.formats?.thumbnail?.url || file.url, // Use thumbnail if available\n mimetype: file.mime,\n originalFilename: file.name,\n size: file.formats?.thumbnail?.size || file.size,\n provider: file.provider,\n }) as unknown as any\n );\n\n const metadataResults = await aiMetadataService.processFiles(thumbnailFiles);\n\n // Update the uploaded files with AI metadata\n await Promise.all(\n uploadedFiles.map(async (uploadedFile, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n await uploadService.updateFileInfo(\n uploadedFile.id,\n {\n alternativeText: aiMetadata.altText,\n caption: aiMetadata.caption,\n },\n { user }\n );\n\n uploadedFiles[index].alternativeText = aiMetadata.altText;\n uploadedFiles[index].caption = aiMetadata.caption;\n }\n })\n );\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","Array","isArray","ApplicationError","validFiles","filteredBody","prepareUploadRequest","strapi","replacedFile","replace","signedFile","signFileUrls","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","isMultipleFiles","length","filesArray","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","mime","startsWith","trackUsage","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","size","provider","metadataResults","processFiles","Promise","all","uploadedFile","index","aiMetadata","alternativeText","altText","caption","error","log","warn","Error","message","String","signedFiles","status","_","isEmpty"],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ var utils = require('@strapi/utils');
|
|
|
5
5
|
var index = require('../utils/index.js');
|
|
6
6
|
var constants = require('../constants.js');
|
|
7
7
|
var upload = require('./validation/content-api/upload.js');
|
|
8
|
+
var mimeValidation = require('../utils/mime-validation.js');
|
|
8
9
|
|
|
9
10
|
const { ValidationError } = utils.errors;
|
|
10
11
|
var contentApi = (({ strapi })=>{
|
|
@@ -65,29 +66,32 @@ var contentApi = (({ strapi })=>{
|
|
|
65
66
|
ctx.body = await sanitizeOutput(result, ctx);
|
|
66
67
|
},
|
|
67
68
|
async replaceFile (ctx) {
|
|
68
|
-
const { query: { id }, request: { body, files: { files } = {} } } = ctx;
|
|
69
|
+
const { query: { id }, request: { body, files: { files: filesInput } = {} } } = ctx;
|
|
70
|
+
const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(filesInput, body, strapi);
|
|
69
71
|
// cannot replace with more than one file
|
|
70
|
-
if (Array.isArray(
|
|
72
|
+
if (Array.isArray(filesInput)) {
|
|
71
73
|
throw new ValidationError('Cannot replace a file with multiple ones');
|
|
72
74
|
}
|
|
73
75
|
if (!id || typeof id !== 'string' && typeof id !== 'number') {
|
|
74
76
|
throw new ValidationError('File id is required and must be a single value');
|
|
75
77
|
}
|
|
76
|
-
const data = await upload.validateUploadBody(
|
|
78
|
+
const data = await upload.validateUploadBody(filteredBody);
|
|
77
79
|
const replacedFiles = await index.getService('upload').replace(id, {
|
|
78
80
|
data,
|
|
79
|
-
file:
|
|
81
|
+
file: validFiles[0]
|
|
80
82
|
});
|
|
81
83
|
ctx.body = await sanitizeOutput(replacedFiles, ctx);
|
|
82
84
|
},
|
|
83
85
|
async uploadFiles (ctx) {
|
|
84
|
-
const { request: { body, files: { files } = {} } } = ctx;
|
|
85
|
-
const
|
|
86
|
+
const { request: { body, files: { files: filesInput } = {} } } = ctx;
|
|
87
|
+
const { validFiles, filteredBody } = await mimeValidation.prepareUploadRequest(filesInput, body, strapi);
|
|
88
|
+
const isMultipleFiles = validFiles.length > 1;
|
|
89
|
+
const data = await upload.validateUploadBody(filteredBody, isMultipleFiles);
|
|
86
90
|
const apiUploadFolderService = index.getService('api-upload-folder');
|
|
87
91
|
const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder();
|
|
88
|
-
if (
|
|
92
|
+
if (isMultipleFiles) {
|
|
89
93
|
data.fileInfo = data.fileInfo || [];
|
|
90
|
-
data.fileInfo =
|
|
94
|
+
data.fileInfo = validFiles.map((_f, i)=>({
|
|
91
95
|
...data.fileInfo[i],
|
|
92
96
|
folder: apiUploadFolder.id
|
|
93
97
|
}));
|
|
@@ -99,9 +103,7 @@ var contentApi = (({ strapi })=>{
|
|
|
99
103
|
}
|
|
100
104
|
const uploadedFiles = await index.getService('upload').upload({
|
|
101
105
|
data,
|
|
102
|
-
files:
|
|
103
|
-
files
|
|
104
|
-
]
|
|
106
|
+
files: validFiles
|
|
105
107
|
});
|
|
106
108
|
ctx.body = await sanitizeOutput(uploadedFiles, ctx);
|
|
107
109
|
ctx.status = 201;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-api.js","sources":["../../../server/src/controllers/content-api.ts"],"sourcesContent":["import _ from 'lodash';\nimport utils from '@strapi/utils';\n\nimport type { Context } from 'koa';\nimport type { Core } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport { FILE_MODEL_UID } from '../constants';\nimport { validateUploadBody } from './validation/content-api/upload';\nimport { FileInfo } from '../types';\n\nconst { ValidationError } = utils.errors;\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n const sanitizeOutput = async (data: unknown | unknown[], ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.output(data, schema, { auth });\n };\n\n const validateQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.validate.query(data, schema, { auth });\n };\n\n const sanitizeQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.query(data, schema, { auth });\n };\n\n return {\n async find(ctx: Context) {\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const files = await getService('upload').findMany(sanitizedQuery);\n\n ctx.body = await sanitizeOutput(files, ctx);\n },\n\n async findOne(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const file = await getService('upload').findOne(id, sanitizedQuery.populate!);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async destroy(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n const file = await getService('upload').findOne(id);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n await getService('upload').remove(file);\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n query: { id },\n request: { body },\n } = ctx;\n const data = await validateUploadBody(body);\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const result = await getService('upload').updateFileInfo(id, data.fileInfo as any);\n\n ctx.body = await sanitizeOutput(result, ctx);\n },\n\n async replaceFile(ctx: Context) {\n const {\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n // cannot replace with more than one file\n if (Array.isArray(files)) {\n throw new ValidationError('Cannot replace a file with multiple ones');\n }\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n\n const replacedFiles = await getService('upload').replace(id, { data, file: files });\n\n ctx.body = await sanitizeOutput(replacedFiles, ctx);\n },\n\n async uploadFiles(ctx: Context) {\n const {\n request: { body, files: { files } = {} },\n } = ctx;\n\n const data: any = await validateUploadBody(body, Array.isArray(files));\n\n const apiUploadFolderService = getService('api-upload-folder');\n\n const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder();\n\n if (Array.isArray(files)) {\n data.fileInfo = data.fileInfo || [];\n data.fileInfo = files.map((_f, i) => ({ ...data.fileInfo[i], folder: apiUploadFolder.id }));\n } else {\n data.fileInfo = { ...data.fileInfo, folder: apiUploadFolder.id };\n }\n\n const uploadedFiles = await getService('upload').upload({\n data,\n files: Array.isArray(files) ? files : [files],\n });\n\n ctx.body = await sanitizeOutput(uploadedFiles as any, ctx);\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new ValidationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n };\n};\n"],"names":["ValidationError","utils","errors","strapi","sanitizeOutput","data","ctx","schema","getModel","FILE_MODEL_UID","auth","state","contentAPI","sanitize","output","validateQuery","validate","query","sanitizeQuery","find","sanitizedQuery","files","getService","findMany","body","findOne","params","id","file","populate","notFound","destroy","remove","updateFileInfo","request","validateUploadBody","result","fileInfo","replaceFile","Array","isArray","replacedFiles","replace","uploadFiles","apiUploadFolderService","apiUploadFolder","getAPIUploadFolder","map","_f","i","folder","uploadedFiles","upload","status","_","isEmpty","size"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"content-api.js","sources":["../../../server/src/controllers/content-api.ts"],"sourcesContent":["import _ from 'lodash';\nimport utils from '@strapi/utils';\n\nimport type { Context } from 'koa';\nimport type { Core } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport { FILE_MODEL_UID } from '../constants';\nimport { validateUploadBody } from './validation/content-api/upload';\nimport { FileInfo } from '../types';\nimport { prepareUploadRequest } from '../utils/mime-validation';\n\nconst { ValidationError } = utils.errors;\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n const sanitizeOutput = async (data: unknown | unknown[], ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.output(data, schema, { auth });\n };\n\n const validateQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.validate.query(data, schema, { auth });\n };\n\n const sanitizeQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.query(data, schema, { auth });\n };\n\n return {\n async find(ctx: Context) {\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const files = await getService('upload').findMany(sanitizedQuery);\n\n ctx.body = await sanitizeOutput(files, ctx);\n },\n\n async findOne(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const file = await getService('upload').findOne(id, sanitizedQuery.populate!);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async destroy(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n const file = await getService('upload').findOne(id);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n await getService('upload').remove(file);\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n query: { id },\n request: { body },\n } = ctx;\n const data = await validateUploadBody(body);\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const result = await getService('upload').updateFileInfo(id, data.fileInfo as any);\n\n ctx.body = await sanitizeOutput(result, ctx);\n },\n\n async replaceFile(ctx: Context) {\n const {\n query: { id },\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n const { validFiles, filteredBody } = await prepareUploadRequest(filesInput, body, strapi);\n\n // cannot replace with more than one file\n if (Array.isArray(filesInput)) {\n throw new ValidationError('Cannot replace a file with multiple ones');\n }\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n\n const replacedFiles = await getService('upload').replace(id, { data, file: validFiles[0] });\n\n ctx.body = await sanitizeOutput(replacedFiles, ctx);\n },\n\n async uploadFiles(ctx: Context) {\n const {\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n const { validFiles, filteredBody } = await prepareUploadRequest(filesInput, body, strapi);\n\n const isMultipleFiles = validFiles.length > 1;\n const data: any = await validateUploadBody(filteredBody, isMultipleFiles);\n\n const apiUploadFolderService = getService('api-upload-folder');\n\n const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder();\n\n if (isMultipleFiles) {\n data.fileInfo = data.fileInfo || [];\n data.fileInfo = validFiles.map((_f, i) => ({\n ...data.fileInfo[i],\n folder: apiUploadFolder.id,\n }));\n } else {\n data.fileInfo = { ...data.fileInfo, folder: apiUploadFolder.id };\n }\n\n const uploadedFiles = await getService('upload').upload({\n data,\n files: validFiles,\n });\n\n ctx.body = await sanitizeOutput(uploadedFiles as any, ctx);\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new ValidationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n };\n};\n"],"names":["ValidationError","utils","errors","strapi","sanitizeOutput","data","ctx","schema","getModel","FILE_MODEL_UID","auth","state","contentAPI","sanitize","output","validateQuery","validate","query","sanitizeQuery","find","sanitizedQuery","files","getService","findMany","body","findOne","params","id","file","populate","notFound","destroy","remove","updateFileInfo","request","validateUploadBody","result","fileInfo","replaceFile","filesInput","validFiles","filteredBody","prepareUploadRequest","Array","isArray","replacedFiles","replace","uploadFiles","isMultipleFiles","length","apiUploadFolderService","apiUploadFolder","getAPIUploadFolder","map","_f","i","folder","uploadedFiles","upload","status","_","isEmpty","size"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import utils from '@strapi/utils';
|
|
|
3
3
|
import { getService } from '../utils/index.mjs';
|
|
4
4
|
import { FILE_MODEL_UID } from '../constants.mjs';
|
|
5
5
|
import { validateUploadBody } from './validation/content-api/upload.mjs';
|
|
6
|
+
import { prepareUploadRequest } from '../utils/mime-validation.mjs';
|
|
6
7
|
|
|
7
8
|
const { ValidationError } = utils.errors;
|
|
8
9
|
var contentApi = (({ strapi })=>{
|
|
@@ -63,29 +64,32 @@ var contentApi = (({ strapi })=>{
|
|
|
63
64
|
ctx.body = await sanitizeOutput(result, ctx);
|
|
64
65
|
},
|
|
65
66
|
async replaceFile (ctx) {
|
|
66
|
-
const { query: { id }, request: { body, files: { files } = {} } } = ctx;
|
|
67
|
+
const { query: { id }, request: { body, files: { files: filesInput } = {} } } = ctx;
|
|
68
|
+
const { validFiles, filteredBody } = await prepareUploadRequest(filesInput, body, strapi);
|
|
67
69
|
// cannot replace with more than one file
|
|
68
|
-
if (Array.isArray(
|
|
70
|
+
if (Array.isArray(filesInput)) {
|
|
69
71
|
throw new ValidationError('Cannot replace a file with multiple ones');
|
|
70
72
|
}
|
|
71
73
|
if (!id || typeof id !== 'string' && typeof id !== 'number') {
|
|
72
74
|
throw new ValidationError('File id is required and must be a single value');
|
|
73
75
|
}
|
|
74
|
-
const data = await validateUploadBody(
|
|
76
|
+
const data = await validateUploadBody(filteredBody);
|
|
75
77
|
const replacedFiles = await getService('upload').replace(id, {
|
|
76
78
|
data,
|
|
77
|
-
file:
|
|
79
|
+
file: validFiles[0]
|
|
78
80
|
});
|
|
79
81
|
ctx.body = await sanitizeOutput(replacedFiles, ctx);
|
|
80
82
|
},
|
|
81
83
|
async uploadFiles (ctx) {
|
|
82
|
-
const { request: { body, files: { files } = {} } } = ctx;
|
|
83
|
-
const
|
|
84
|
+
const { request: { body, files: { files: filesInput } = {} } } = ctx;
|
|
85
|
+
const { validFiles, filteredBody } = await prepareUploadRequest(filesInput, body, strapi);
|
|
86
|
+
const isMultipleFiles = validFiles.length > 1;
|
|
87
|
+
const data = await validateUploadBody(filteredBody, isMultipleFiles);
|
|
84
88
|
const apiUploadFolderService = getService('api-upload-folder');
|
|
85
89
|
const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder();
|
|
86
|
-
if (
|
|
90
|
+
if (isMultipleFiles) {
|
|
87
91
|
data.fileInfo = data.fileInfo || [];
|
|
88
|
-
data.fileInfo =
|
|
92
|
+
data.fileInfo = validFiles.map((_f, i)=>({
|
|
89
93
|
...data.fileInfo[i],
|
|
90
94
|
folder: apiUploadFolder.id
|
|
91
95
|
}));
|
|
@@ -97,9 +101,7 @@ var contentApi = (({ strapi })=>{
|
|
|
97
101
|
}
|
|
98
102
|
const uploadedFiles = await getService('upload').upload({
|
|
99
103
|
data,
|
|
100
|
-
files:
|
|
101
|
-
files
|
|
102
|
-
]
|
|
104
|
+
files: validFiles
|
|
103
105
|
});
|
|
104
106
|
ctx.body = await sanitizeOutput(uploadedFiles, ctx);
|
|
105
107
|
ctx.status = 201;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-api.mjs","sources":["../../../server/src/controllers/content-api.ts"],"sourcesContent":["import _ from 'lodash';\nimport utils from '@strapi/utils';\n\nimport type { Context } from 'koa';\nimport type { Core } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport { FILE_MODEL_UID } from '../constants';\nimport { validateUploadBody } from './validation/content-api/upload';\nimport { FileInfo } from '../types';\n\nconst { ValidationError } = utils.errors;\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n const sanitizeOutput = async (data: unknown | unknown[], ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.output(data, schema, { auth });\n };\n\n const validateQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.validate.query(data, schema, { auth });\n };\n\n const sanitizeQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.query(data, schema, { auth });\n };\n\n return {\n async find(ctx: Context) {\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const files = await getService('upload').findMany(sanitizedQuery);\n\n ctx.body = await sanitizeOutput(files, ctx);\n },\n\n async findOne(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const file = await getService('upload').findOne(id, sanitizedQuery.populate!);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async destroy(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n const file = await getService('upload').findOne(id);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n await getService('upload').remove(file);\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n query: { id },\n request: { body },\n } = ctx;\n const data = await validateUploadBody(body);\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const result = await getService('upload').updateFileInfo(id, data.fileInfo as any);\n\n ctx.body = await sanitizeOutput(result, ctx);\n },\n\n async replaceFile(ctx: Context) {\n const {\n query: { id },\n request: { body, files: { files } = {} },\n } = ctx;\n\n // cannot replace with more than one file\n if (Array.isArray(files)) {\n throw new ValidationError('Cannot replace a file with multiple ones');\n }\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n\n const replacedFiles = await getService('upload').replace(id, { data, file: files });\n\n ctx.body = await sanitizeOutput(replacedFiles, ctx);\n },\n\n async uploadFiles(ctx: Context) {\n const {\n request: { body, files: { files } = {} },\n } = ctx;\n\n const data: any = await validateUploadBody(body, Array.isArray(files));\n\n const apiUploadFolderService = getService('api-upload-folder');\n\n const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder();\n\n if (Array.isArray(files)) {\n data.fileInfo = data.fileInfo || [];\n data.fileInfo = files.map((_f, i) => ({ ...data.fileInfo[i], folder: apiUploadFolder.id }));\n } else {\n data.fileInfo = { ...data.fileInfo, folder: apiUploadFolder.id };\n }\n\n const uploadedFiles = await getService('upload').upload({\n data,\n files: Array.isArray(files) ? files : [files],\n });\n\n ctx.body = await sanitizeOutput(uploadedFiles as any, ctx);\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new ValidationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n };\n};\n"],"names":["ValidationError","utils","errors","strapi","sanitizeOutput","data","ctx","schema","getModel","FILE_MODEL_UID","auth","state","contentAPI","sanitize","output","validateQuery","validate","query","sanitizeQuery","find","sanitizedQuery","files","getService","findMany","body","findOne","params","id","file","populate","notFound","destroy","remove","updateFileInfo","request","validateUploadBody","result","fileInfo","replaceFile","Array","isArray","replacedFiles","replace","uploadFiles","apiUploadFolderService","apiUploadFolder","getAPIUploadFolder","map","_f","i","folder","uploadedFiles","upload","status","_","isEmpty","size"],"mappings":";;;;;;AAWA,MAAM,EAAEA,eAAe,EAAE,GAAGC,MAAMC,MAAM;AAExC,iBAAe,CAAA,CAAC,EAAEC,MAAM,EAA2B,GAAA;IACjD,MAAMC,cAAAA,GAAiB,OAAOC,IAA2BC,EAAAA,GAAAA,GAAAA;QACvD,MAAMC,MAAAA,GAASJ,MAAOK,CAAAA,QAAQ,CAACC,cAAAA,CAAAA;AAC/B,QAAA,MAAM,EAAEC,IAAI,EAAE,GAAGJ,IAAIK,KAAK;QAE1B,OAAOR,MAAAA,CAAOS,UAAU,CAACC,QAAQ,CAACC,MAAM,CAACT,MAAME,MAAQ,EAAA;AAAEG,YAAAA;AAAK,SAAA,CAAA;AAChE,KAAA;IAEA,MAAMK,aAAAA,GAAgB,OAAOV,IAA+BC,EAAAA,GAAAA,GAAAA;QAC1D,MAAMC,MAAAA,GAASJ,MAAOK,CAAAA,QAAQ,CAACC,cAAAA,CAAAA;AAC/B,QAAA,MAAM,EAAEC,IAAI,EAAE,GAAGJ,IAAIK,KAAK;QAE1B,OAAOR,MAAAA,CAAOS,UAAU,CAACI,QAAQ,CAACC,KAAK,CAACZ,MAAME,MAAQ,EAAA;AAAEG,YAAAA;AAAK,SAAA,CAAA;AAC/D,KAAA;IAEA,MAAMQ,aAAAA,GAAgB,OAAOb,IAA+BC,EAAAA,GAAAA,GAAAA;QAC1D,MAAMC,MAAAA,GAASJ,MAAOK,CAAAA,QAAQ,CAACC,cAAAA,CAAAA;AAC/B,QAAA,MAAM,EAAEC,IAAI,EAAE,GAAGJ,IAAIK,KAAK;QAE1B,OAAOR,MAAAA,CAAOS,UAAU,CAACC,QAAQ,CAACI,KAAK,CAACZ,MAAME,MAAQ,EAAA;AAAEG,YAAAA;AAAK,SAAA,CAAA;AAC/D,KAAA;IAEA,OAAO;AACL,QAAA,MAAMS,MAAKb,GAAY,EAAA;YACrB,MAAMS,aAAAA,CAAcT,GAAIW,CAAAA,KAAK,EAAEX,GAAAA,CAAAA;AAC/B,YAAA,MAAMc,cAAiB,GAAA,MAAMF,aAAcZ,CAAAA,GAAAA,CAAIW,KAAK,EAAEX,GAAAA,CAAAA;AAEtD,YAAA,MAAMe,KAAQ,GAAA,MAAMC,UAAW,CAAA,QAAA,CAAA,CAAUC,QAAQ,CAACH,cAAAA,CAAAA;AAElDd,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAeiB,KAAOf,EAAAA,GAAAA,CAAAA;AACzC,SAAA;AAEA,QAAA,MAAMmB,SAAQnB,GAAY,EAAA;AACxB,YAAA,MAAM,EACJoB,MAAQ,EAAA,EAAEC,EAAE,EAAE,EACf,GAAGrB,GAAAA;YAEJ,MAAMS,aAAAA,CAAcT,GAAIW,CAAAA,KAAK,EAAEX,GAAAA,CAAAA;AAC/B,YAAA,MAAMc,cAAiB,GAAA,MAAMF,aAAcZ,CAAAA,GAAAA,CAAIW,KAAK,EAAEX,GAAAA,CAAAA;YAEtD,MAAMsB,IAAAA,GAAO,MAAMN,UAAW,CAAA,QAAA,CAAA,CAAUG,OAAO,CAACE,EAAAA,EAAIP,eAAeS,QAAQ,CAAA;AAE3E,YAAA,IAAI,CAACD,IAAM,EAAA;gBACT,OAAOtB,GAAAA,CAAIwB,QAAQ,CAAC,eAAA,CAAA;AACtB;AAEAxB,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAewB,IAAMtB,EAAAA,GAAAA,CAAAA;AACxC,SAAA;AAEA,QAAA,MAAMyB,SAAQzB,GAAY,EAAA;AACxB,YAAA,MAAM,EACJoB,MAAQ,EAAA,EAAEC,EAAE,EAAE,EACf,GAAGrB,GAAAA;AAEJ,YAAA,MAAMsB,IAAO,GAAA,MAAMN,UAAW,CAAA,QAAA,CAAA,CAAUG,OAAO,CAACE,EAAAA,CAAAA;AAEhD,YAAA,IAAI,CAACC,IAAM,EAAA;gBACT,OAAOtB,GAAAA,CAAIwB,QAAQ,CAAC,eAAA,CAAA;AACtB;YAEA,MAAMR,UAAAA,CAAW,QAAUU,CAAAA,CAAAA,MAAM,CAACJ,IAAAA,CAAAA;AAElCtB,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAewB,IAAMtB,EAAAA,GAAAA,CAAAA;AACxC,SAAA;AAEA,QAAA,MAAM2B,gBAAe3B,GAAY,EAAA;YAC/B,MAAM,EACJW,KAAO,EAAA,EAAEU,EAAE,EAAE,EACbO,OAAAA,EAAS,EAAEV,IAAI,EAAE,EAClB,GAAGlB,GAAAA;YACJ,MAAMD,IAAAA,GAAO,MAAM8B,kBAAmBX,CAAAA,IAAAA,CAAAA;AAEtC,YAAA,IAAI,CAACG,EAAO,IAAA,OAAOA,OAAO,QAAY,IAAA,OAAOA,OAAO,QAAW,EAAA;AAC7D,gBAAA,MAAM,IAAI3B,eAAgB,CAAA,gDAAA,CAAA;AAC5B;YAEA,MAAMoC,MAAAA,GAAS,MAAMd,UAAW,CAAA,QAAA,CAAA,CAAUW,cAAc,CAACN,EAAAA,EAAItB,KAAKgC,QAAQ,CAAA;AAE1E/B,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAegC,MAAQ9B,EAAAA,GAAAA,CAAAA;AAC1C,SAAA;AAEA,QAAA,MAAMgC,aAAYhC,GAAY,EAAA;AAC5B,YAAA,MAAM,EACJW,KAAO,EAAA,EAAEU,EAAE,EAAE,EACbO,SAAS,EAAEV,IAAI,EAAEH,KAAO,EAAA,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACzC,GAAGf,GAAAA;;YAGJ,IAAIiC,KAAAA,CAAMC,OAAO,CAACnB,KAAQ,CAAA,EAAA;AACxB,gBAAA,MAAM,IAAIrB,eAAgB,CAAA,0CAAA,CAAA;AAC5B;AAEA,YAAA,IAAI,CAAC2B,EAAO,IAAA,OAAOA,OAAO,QAAY,IAAA,OAAOA,OAAO,QAAW,EAAA;AAC7D,gBAAA,MAAM,IAAI3B,eAAgB,CAAA,gDAAA,CAAA;AAC5B;YAEA,MAAMK,IAAAA,GAAQ,MAAM8B,kBAAmBX,CAAAA,IAAAA,CAAAA;AAEvC,YAAA,MAAMiB,gBAAgB,MAAMnB,UAAAA,CAAW,QAAUoB,CAAAA,CAAAA,OAAO,CAACf,EAAI,EAAA;AAAEtB,gBAAAA,IAAAA;gBAAMuB,IAAMP,EAAAA;AAAM,aAAA,CAAA;AAEjFf,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAeqC,aAAenC,EAAAA,GAAAA,CAAAA;AACjD,SAAA;AAEA,QAAA,MAAMqC,aAAYrC,GAAY,EAAA;AAC5B,YAAA,MAAM,EACJ4B,OAAAA,EAAS,EAAEV,IAAI,EAAEH,KAAO,EAAA,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACzC,GAAGf,GAAAA;AAEJ,YAAA,MAAMD,OAAY,MAAM8B,kBAAAA,CAAmBX,IAAMe,EAAAA,KAAAA,CAAMC,OAAO,CAACnB,KAAAA,CAAAA,CAAAA;AAE/D,YAAA,MAAMuB,yBAAyBtB,UAAW,CAAA,mBAAA,CAAA;YAE1C,MAAMuB,eAAAA,GAAkB,MAAMD,sBAAAA,CAAuBE,kBAAkB,EAAA;YAEvE,IAAIP,KAAAA,CAAMC,OAAO,CAACnB,KAAQ,CAAA,EAAA;AACxBhB,gBAAAA,IAAAA,CAAKgC,QAAQ,GAAGhC,IAAKgC,CAAAA,QAAQ,IAAI,EAAE;gBACnChC,IAAKgC,CAAAA,QAAQ,GAAGhB,KAAM0B,CAAAA,GAAG,CAAC,CAACC,EAAAA,EAAIC,KAAO;wBAAE,GAAG5C,IAAAA,CAAKgC,QAAQ,CAACY,CAAE,CAAA;AAAEC,wBAAAA,MAAAA,EAAQL,gBAAgBlB;qBAAG,CAAA,CAAA;aACnF,MAAA;AACLtB,gBAAAA,IAAAA,CAAKgC,QAAQ,GAAG;AAAE,oBAAA,GAAGhC,KAAKgC,QAAQ;AAAEa,oBAAAA,MAAAA,EAAQL,gBAAgBlB;AAAG,iBAAA;AACjE;AAEA,YAAA,MAAMwB,aAAgB,GAAA,MAAM7B,UAAW,CAAA,QAAA,CAAA,CAAU8B,MAAM,CAAC;AACtD/C,gBAAAA,IAAAA;AACAgB,gBAAAA,KAAAA,EAAOkB,KAAMC,CAAAA,OAAO,CAACnB,KAAAA,CAAAA,GAASA,KAAQ,GAAA;AAACA,oBAAAA;AAAM;AAC/C,aAAA,CAAA;AAEAf,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAe+C,aAAsB7C,EAAAA,GAAAA,CAAAA;AACtDA,YAAAA,GAAAA,CAAI+C,MAAM,GAAG,GAAA;AACf,SAAA;;AAGA,QAAA,MAAMD,QAAO9C,GAAY,EAAA;AACvB,YAAA,MAAM,EACJW,KAAO,EAAA,EAAEU,EAAE,EAAE,EACbO,OAAS,EAAA,EAAEb,KAAO,EAAA,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACnC,GAAGf,GAAAA;AAEJ,YAAA,IAAIgD,CAAEC,CAAAA,OAAO,CAAClC,KAAAA,CAAAA,IAAW,CAACkB,KAAAA,CAAMC,OAAO,CAACnB,KAAUA,CAAAA,IAAAA,KAAAA,CAAMmC,IAAI,KAAK,CAAI,EAAA;AACnE,gBAAA,IAAI7B,EAAI,EAAA;oBACN,OAAO,IAAI,CAACM,cAAc,CAAC3B,GAAAA,CAAAA;AAC7B;AAEA,gBAAA,MAAM,IAAIN,eAAgB,CAAA,iBAAA,CAAA;AAC5B;YAEA,MAAO2B,CAAAA,EAAAA,GAAK,IAAI,CAACW,WAAW,GAAG,IAAI,CAACK,WAAU,EAAGrC,GAAAA,CAAAA;AACnD;AACF,KAAA;AACF,CAAA;;;;"}
|
|
1
|
+
{"version":3,"file":"content-api.mjs","sources":["../../../server/src/controllers/content-api.ts"],"sourcesContent":["import _ from 'lodash';\nimport utils from '@strapi/utils';\n\nimport type { Context } from 'koa';\nimport type { Core } from '@strapi/types';\n\nimport { getService } from '../utils';\nimport { FILE_MODEL_UID } from '../constants';\nimport { validateUploadBody } from './validation/content-api/upload';\nimport { FileInfo } from '../types';\nimport { prepareUploadRequest } from '../utils/mime-validation';\n\nconst { ValidationError } = utils.errors;\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => {\n const sanitizeOutput = async (data: unknown | unknown[], ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.output(data, schema, { auth });\n };\n\n const validateQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.validate.query(data, schema, { auth });\n };\n\n const sanitizeQuery = async (data: Record<string, unknown>, ctx: Context) => {\n const schema = strapi.getModel(FILE_MODEL_UID);\n const { auth } = ctx.state;\n\n return strapi.contentAPI.sanitize.query(data, schema, { auth });\n };\n\n return {\n async find(ctx: Context) {\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const files = await getService('upload').findMany(sanitizedQuery);\n\n ctx.body = await sanitizeOutput(files, ctx);\n },\n\n async findOne(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n await validateQuery(ctx.query, ctx);\n const sanitizedQuery = await sanitizeQuery(ctx.query, ctx);\n\n const file = await getService('upload').findOne(id, sanitizedQuery.populate!);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async destroy(ctx: Context) {\n const {\n params: { id },\n } = ctx;\n\n const file = await getService('upload').findOne(id);\n\n if (!file) {\n return ctx.notFound('file.notFound');\n }\n\n await getService('upload').remove(file);\n\n ctx.body = await sanitizeOutput(file, ctx);\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n query: { id },\n request: { body },\n } = ctx;\n const data = await validateUploadBody(body);\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const result = await getService('upload').updateFileInfo(id, data.fileInfo as any);\n\n ctx.body = await sanitizeOutput(result, ctx);\n },\n\n async replaceFile(ctx: Context) {\n const {\n query: { id },\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n const { validFiles, filteredBody } = await prepareUploadRequest(filesInput, body, strapi);\n\n // cannot replace with more than one file\n if (Array.isArray(filesInput)) {\n throw new ValidationError('Cannot replace a file with multiple ones');\n }\n\n if (!id || (typeof id !== 'string' && typeof id !== 'number')) {\n throw new ValidationError('File id is required and must be a single value');\n }\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n\n const replacedFiles = await getService('upload').replace(id, { data, file: validFiles[0] });\n\n ctx.body = await sanitizeOutput(replacedFiles, ctx);\n },\n\n async uploadFiles(ctx: Context) {\n const {\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n const { validFiles, filteredBody } = await prepareUploadRequest(filesInput, body, strapi);\n\n const isMultipleFiles = validFiles.length > 1;\n const data: any = await validateUploadBody(filteredBody, isMultipleFiles);\n\n const apiUploadFolderService = getService('api-upload-folder');\n\n const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder();\n\n if (isMultipleFiles) {\n data.fileInfo = data.fileInfo || [];\n data.fileInfo = validFiles.map((_f, i) => ({\n ...data.fileInfo[i],\n folder: apiUploadFolder.id,\n }));\n } else {\n data.fileInfo = { ...data.fileInfo, folder: apiUploadFolder.id };\n }\n\n const uploadedFiles = await getService('upload').upload({\n data,\n files: validFiles,\n });\n\n ctx.body = await sanitizeOutput(uploadedFiles as any, ctx);\n ctx.status = 201;\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new ValidationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n };\n};\n"],"names":["ValidationError","utils","errors","strapi","sanitizeOutput","data","ctx","schema","getModel","FILE_MODEL_UID","auth","state","contentAPI","sanitize","output","validateQuery","validate","query","sanitizeQuery","find","sanitizedQuery","files","getService","findMany","body","findOne","params","id","file","populate","notFound","destroy","remove","updateFileInfo","request","validateUploadBody","result","fileInfo","replaceFile","filesInput","validFiles","filteredBody","prepareUploadRequest","Array","isArray","replacedFiles","replace","uploadFiles","isMultipleFiles","length","apiUploadFolderService","apiUploadFolder","getAPIUploadFolder","map","_f","i","folder","uploadedFiles","upload","status","_","isEmpty","size"],"mappings":";;;;;;;AAYA,MAAM,EAAEA,eAAe,EAAE,GAAGC,MAAMC,MAAM;AAExC,iBAAe,CAAA,CAAC,EAAEC,MAAM,EAA2B,GAAA;IACjD,MAAMC,cAAAA,GAAiB,OAAOC,IAA2BC,EAAAA,GAAAA,GAAAA;QACvD,MAAMC,MAAAA,GAASJ,MAAOK,CAAAA,QAAQ,CAACC,cAAAA,CAAAA;AAC/B,QAAA,MAAM,EAAEC,IAAI,EAAE,GAAGJ,IAAIK,KAAK;QAE1B,OAAOR,MAAAA,CAAOS,UAAU,CAACC,QAAQ,CAACC,MAAM,CAACT,MAAME,MAAQ,EAAA;AAAEG,YAAAA;AAAK,SAAA,CAAA;AAChE,KAAA;IAEA,MAAMK,aAAAA,GAAgB,OAAOV,IAA+BC,EAAAA,GAAAA,GAAAA;QAC1D,MAAMC,MAAAA,GAASJ,MAAOK,CAAAA,QAAQ,CAACC,cAAAA,CAAAA;AAC/B,QAAA,MAAM,EAAEC,IAAI,EAAE,GAAGJ,IAAIK,KAAK;QAE1B,OAAOR,MAAAA,CAAOS,UAAU,CAACI,QAAQ,CAACC,KAAK,CAACZ,MAAME,MAAQ,EAAA;AAAEG,YAAAA;AAAK,SAAA,CAAA;AAC/D,KAAA;IAEA,MAAMQ,aAAAA,GAAgB,OAAOb,IAA+BC,EAAAA,GAAAA,GAAAA;QAC1D,MAAMC,MAAAA,GAASJ,MAAOK,CAAAA,QAAQ,CAACC,cAAAA,CAAAA;AAC/B,QAAA,MAAM,EAAEC,IAAI,EAAE,GAAGJ,IAAIK,KAAK;QAE1B,OAAOR,MAAAA,CAAOS,UAAU,CAACC,QAAQ,CAACI,KAAK,CAACZ,MAAME,MAAQ,EAAA;AAAEG,YAAAA;AAAK,SAAA,CAAA;AAC/D,KAAA;IAEA,OAAO;AACL,QAAA,MAAMS,MAAKb,GAAY,EAAA;YACrB,MAAMS,aAAAA,CAAcT,GAAIW,CAAAA,KAAK,EAAEX,GAAAA,CAAAA;AAC/B,YAAA,MAAMc,cAAiB,GAAA,MAAMF,aAAcZ,CAAAA,GAAAA,CAAIW,KAAK,EAAEX,GAAAA,CAAAA;AAEtD,YAAA,MAAMe,KAAQ,GAAA,MAAMC,UAAW,CAAA,QAAA,CAAA,CAAUC,QAAQ,CAACH,cAAAA,CAAAA;AAElDd,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAeiB,KAAOf,EAAAA,GAAAA,CAAAA;AACzC,SAAA;AAEA,QAAA,MAAMmB,SAAQnB,GAAY,EAAA;AACxB,YAAA,MAAM,EACJoB,MAAQ,EAAA,EAAEC,EAAE,EAAE,EACf,GAAGrB,GAAAA;YAEJ,MAAMS,aAAAA,CAAcT,GAAIW,CAAAA,KAAK,EAAEX,GAAAA,CAAAA;AAC/B,YAAA,MAAMc,cAAiB,GAAA,MAAMF,aAAcZ,CAAAA,GAAAA,CAAIW,KAAK,EAAEX,GAAAA,CAAAA;YAEtD,MAAMsB,IAAAA,GAAO,MAAMN,UAAW,CAAA,QAAA,CAAA,CAAUG,OAAO,CAACE,EAAAA,EAAIP,eAAeS,QAAQ,CAAA;AAE3E,YAAA,IAAI,CAACD,IAAM,EAAA;gBACT,OAAOtB,GAAAA,CAAIwB,QAAQ,CAAC,eAAA,CAAA;AACtB;AAEAxB,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAewB,IAAMtB,EAAAA,GAAAA,CAAAA;AACxC,SAAA;AAEA,QAAA,MAAMyB,SAAQzB,GAAY,EAAA;AACxB,YAAA,MAAM,EACJoB,MAAQ,EAAA,EAAEC,EAAE,EAAE,EACf,GAAGrB,GAAAA;AAEJ,YAAA,MAAMsB,IAAO,GAAA,MAAMN,UAAW,CAAA,QAAA,CAAA,CAAUG,OAAO,CAACE,EAAAA,CAAAA;AAEhD,YAAA,IAAI,CAACC,IAAM,EAAA;gBACT,OAAOtB,GAAAA,CAAIwB,QAAQ,CAAC,eAAA,CAAA;AACtB;YAEA,MAAMR,UAAAA,CAAW,QAAUU,CAAAA,CAAAA,MAAM,CAACJ,IAAAA,CAAAA;AAElCtB,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAewB,IAAMtB,EAAAA,GAAAA,CAAAA;AACxC,SAAA;AAEA,QAAA,MAAM2B,gBAAe3B,GAAY,EAAA;YAC/B,MAAM,EACJW,KAAO,EAAA,EAAEU,EAAE,EAAE,EACbO,OAAAA,EAAS,EAAEV,IAAI,EAAE,EAClB,GAAGlB,GAAAA;YACJ,MAAMD,IAAAA,GAAO,MAAM8B,kBAAmBX,CAAAA,IAAAA,CAAAA;AAEtC,YAAA,IAAI,CAACG,EAAO,IAAA,OAAOA,OAAO,QAAY,IAAA,OAAOA,OAAO,QAAW,EAAA;AAC7D,gBAAA,MAAM,IAAI3B,eAAgB,CAAA,gDAAA,CAAA;AAC5B;YAEA,MAAMoC,MAAAA,GAAS,MAAMd,UAAW,CAAA,QAAA,CAAA,CAAUW,cAAc,CAACN,EAAAA,EAAItB,KAAKgC,QAAQ,CAAA;AAE1E/B,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAegC,MAAQ9B,EAAAA,GAAAA,CAAAA;AAC1C,SAAA;AAEA,QAAA,MAAMgC,aAAYhC,GAAY,EAAA;YAC5B,MAAM,EACJW,OAAO,EAAEU,EAAE,EAAE,EACbO,OAAAA,EAAS,EAAEV,IAAI,EAAEH,OAAO,EAAEA,KAAAA,EAAOkB,UAAU,EAAE,GAAG,EAAE,EAAE,EACrD,GAAGjC,GAAAA;YAEJ,MAAM,EAAEkC,UAAU,EAAEC,YAAY,EAAE,GAAG,MAAMC,oBAAqBH,CAAAA,UAAAA,EAAYf,IAAMrB,EAAAA,MAAAA,CAAAA;;YAGlF,IAAIwC,KAAAA,CAAMC,OAAO,CAACL,UAAa,CAAA,EAAA;AAC7B,gBAAA,MAAM,IAAIvC,eAAgB,CAAA,0CAAA,CAAA;AAC5B;AAEA,YAAA,IAAI,CAAC2B,EAAO,IAAA,OAAOA,OAAO,QAAY,IAAA,OAAOA,OAAO,QAAW,EAAA;AAC7D,gBAAA,MAAM,IAAI3B,eAAgB,CAAA,gDAAA,CAAA;AAC5B;YAEA,MAAMK,IAAAA,GAAQ,MAAM8B,kBAAmBM,CAAAA,YAAAA,CAAAA;AAEvC,YAAA,MAAMI,gBAAgB,MAAMvB,UAAAA,CAAW,QAAUwB,CAAAA,CAAAA,OAAO,CAACnB,EAAI,EAAA;AAAEtB,gBAAAA,IAAAA;gBAAMuB,IAAMY,EAAAA,UAAU,CAAC,CAAE;AAAC,aAAA,CAAA;AAEzFlC,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAeyC,aAAevC,EAAAA,GAAAA,CAAAA;AACjD,SAAA;AAEA,QAAA,MAAMyC,aAAYzC,GAAY,EAAA;AAC5B,YAAA,MAAM,EACJ4B,OAAAA,EAAS,EAAEV,IAAI,EAAEH,KAAO,EAAA,EAAEA,KAAOkB,EAAAA,UAAU,EAAE,GAAG,EAAE,EAAE,EACrD,GAAGjC,GAAAA;YAEJ,MAAM,EAAEkC,UAAU,EAAEC,YAAY,EAAE,GAAG,MAAMC,oBAAqBH,CAAAA,UAAAA,EAAYf,IAAMrB,EAAAA,MAAAA,CAAAA;YAElF,MAAM6C,eAAAA,GAAkBR,UAAWS,CAAAA,MAAM,GAAG,CAAA;YAC5C,MAAM5C,IAAAA,GAAY,MAAM8B,kBAAAA,CAAmBM,YAAcO,EAAAA,eAAAA,CAAAA;AAEzD,YAAA,MAAME,yBAAyB5B,UAAW,CAAA,mBAAA,CAAA;YAE1C,MAAM6B,eAAAA,GAAkB,MAAMD,sBAAAA,CAAuBE,kBAAkB,EAAA;AAEvE,YAAA,IAAIJ,eAAiB,EAAA;AACnB3C,gBAAAA,IAAAA,CAAKgC,QAAQ,GAAGhC,IAAKgC,CAAAA,QAAQ,IAAI,EAAE;gBACnChC,IAAKgC,CAAAA,QAAQ,GAAGG,UAAWa,CAAAA,GAAG,CAAC,CAACC,EAAAA,EAAIC,KAAO;wBACzC,GAAGlD,IAAAA,CAAKgC,QAAQ,CAACkB,CAAE,CAAA;AACnBC,wBAAAA,MAAAA,EAAQL,gBAAgBxB;qBAC1B,CAAA,CAAA;aACK,MAAA;AACLtB,gBAAAA,IAAAA,CAAKgC,QAAQ,GAAG;AAAE,oBAAA,GAAGhC,KAAKgC,QAAQ;AAAEmB,oBAAAA,MAAAA,EAAQL,gBAAgBxB;AAAG,iBAAA;AACjE;AAEA,YAAA,MAAM8B,aAAgB,GAAA,MAAMnC,UAAW,CAAA,QAAA,CAAA,CAAUoC,MAAM,CAAC;AACtDrD,gBAAAA,IAAAA;gBACAgB,KAAOmB,EAAAA;AACT,aAAA,CAAA;AAEAlC,YAAAA,GAAAA,CAAIkB,IAAI,GAAG,MAAMpB,cAAAA,CAAeqD,aAAsBnD,EAAAA,GAAAA,CAAAA;AACtDA,YAAAA,GAAAA,CAAIqD,MAAM,GAAG,GAAA;AACf,SAAA;;AAGA,QAAA,MAAMD,QAAOpD,GAAY,EAAA;AACvB,YAAA,MAAM,EACJW,KAAO,EAAA,EAAEU,EAAE,EAAE,EACbO,OAAS,EAAA,EAAEb,KAAO,EAAA,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACnC,GAAGf,GAAAA;AAEJ,YAAA,IAAIsD,CAAEC,CAAAA,OAAO,CAACxC,KAAAA,CAAAA,IAAW,CAACsB,KAAAA,CAAMC,OAAO,CAACvB,KAAUA,CAAAA,IAAAA,KAAAA,CAAMyC,IAAI,KAAK,CAAI,EAAA;AACnE,gBAAA,IAAInC,EAAI,EAAA;oBACN,OAAO,IAAI,CAACM,cAAc,CAAC3B,GAAAA,CAAAA;AAC7B;AAEA,gBAAA,MAAM,IAAIN,eAAgB,CAAA,iBAAA,CAAA;AAC5B;YAEA,MAAO2B,CAAAA,EAAAA,GAAK,IAAI,CAACW,WAAW,GAAG,IAAI,CAACS,WAAU,EAAGzC,GAAAA,CAAAA;AACnD;AACF,KAAA;AACF,CAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-upload.d.ts","sourceRoot":"","sources":["../../../../server/src/controllers/admin-upload.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;;4BAUH,OAAO;wBA2BX,OAAO;qBA0BV,OAAO;
|
|
1
|
+
{"version":3,"file":"admin-upload.d.ts","sourceRoot":"","sources":["../../../../server/src/controllers/admin-upload.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;;4BAUH,OAAO;wBA2BX,OAAO;qBA0BV,OAAO;qBAkCP,OAAO;gBAkGZ,OAAO;;AA1L3B,wBA0ME"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-api.d.ts","sourceRoot":"","sources":["../../../../server/src/controllers/content-api.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"content-api.d.ts","sourceRoot":"","sources":["../../../../server/src/controllers/content-api.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;qCAUd;IAAE,MAAM,EAAE,KAAK,MAAM,CAAA;CAAE;cAuB/B,OAAO;iBASJ,OAAO;iBAiBP,OAAO;wBAgBA,OAAO;qBAgBV,OAAO;qBAwBP,OAAO;gBAkCZ,OAAO;;AA3I7B,wBA4JE"}
|
|
@@ -30,5 +30,13 @@ export declare function enforceUploadSecurity(files: any, strapi: Core.Strapi):
|
|
|
30
30
|
validFileNames: string[];
|
|
31
31
|
errors: Array<ErrorDetail>;
|
|
32
32
|
}>;
|
|
33
|
+
export type PrepareUploadResult = {
|
|
34
|
+
validFiles: any[];
|
|
35
|
+
filteredBody: any;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Prepare files and body for upload by enforcing security and parsing fileInfo
|
|
39
|
+
*/
|
|
40
|
+
export declare function prepareUploadRequest(filesInput: any, body: any, strapi: Core.Strapi): Promise<PrepareUploadResult>;
|
|
33
41
|
export {};
|
|
34
42
|
//# sourceMappingURL=mime-validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime-validation.d.ts","sourceRoot":"","sources":["../../../../server/src/utils/mime-validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AACF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,uBAAuB,GAAG,kBAAkB,GAAG,eAAe,CAAC;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,qBAAqB,CAAC;CAC/B,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,GAAG,CAAC;IACV,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,qBAAqB,CAAC;CAC9B,CAAC;AAOF,wBAAsB,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC3E;AAsBD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAcnF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG;;;EAMxC;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,IAAI,CAAC,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAoE3B;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA6DhG;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,IAAI,CAAC,MAAM,GAClB,OAAO,CAAC;IACT,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;CAC5B,CAAC,CAqCD"}
|
|
1
|
+
{"version":3,"file":"mime-validation.d.ts","sourceRoot":"","sources":["../../../../server/src/utils/mime-validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AACF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,uBAAuB,GAAG,kBAAkB,GAAG,eAAe,CAAC;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,qBAAqB,CAAC;CAC/B,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,GAAG,CAAC;IACV,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,qBAAqB,CAAC;CAC9B,CAAC;AAOF,wBAAsB,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC3E;AAsBD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAcnF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG;;;EAMxC;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,IAAI,CAAC,MAAM,GAClB,OAAO,CAAC,gBAAgB,CAAC,CAoE3B;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA6DhG;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,IAAI,CAAC,MAAM,GAClB,OAAO,CAAC;IACT,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;CAC5B,CAAC,CAqCD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,YAAY,EAAE,GAAG,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,GAAG,EACf,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,IAAI,CAAC,MAAM,GAClB,OAAO,CAAC,mBAAmB,CAAC,CAoD9B"}
|
|
@@ -212,11 +212,55 @@ async function enforceUploadSecurity(files, strapi) {
|
|
|
212
212
|
errors
|
|
213
213
|
};
|
|
214
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Prepare files and body for upload by enforcing security and parsing fileInfo
|
|
217
|
+
*/ async function prepareUploadRequest(filesInput, body, strapi) {
|
|
218
|
+
const securityResults = await enforceUploadSecurity(filesInput, strapi);
|
|
219
|
+
if (securityResults.validFiles.length === 0) {
|
|
220
|
+
throw new utils.errors.ValidationError(securityResults.errors[0].error.message, securityResults.errors[0].error.details);
|
|
221
|
+
}
|
|
222
|
+
let filteredBody = body;
|
|
223
|
+
if (body?.fileInfo) {
|
|
224
|
+
// Parse JSON strings in fileInfo
|
|
225
|
+
let parsedFileInfo = body.fileInfo;
|
|
226
|
+
if (Array.isArray(body.fileInfo)) {
|
|
227
|
+
parsedFileInfo = body.fileInfo.map((fi)=>typeof fi === 'string' ? JSON.parse(fi) : fi);
|
|
228
|
+
} else if (typeof body.fileInfo === 'string') {
|
|
229
|
+
parsedFileInfo = JSON.parse(body.fileInfo);
|
|
230
|
+
}
|
|
231
|
+
// Filter fileInfo by index - only keep entries for files that passed validation
|
|
232
|
+
if (Array.isArray(parsedFileInfo)) {
|
|
233
|
+
const invalidIndices = new Set(securityResults.errors.map((e)=>e.originalIndex));
|
|
234
|
+
const filteredFileInfo = parsedFileInfo.filter((_, index)=>!invalidIndices.has(index));
|
|
235
|
+
if (filteredFileInfo.length === 1) {
|
|
236
|
+
filteredBody = {
|
|
237
|
+
...body,
|
|
238
|
+
fileInfo: filteredFileInfo[0]
|
|
239
|
+
};
|
|
240
|
+
} else {
|
|
241
|
+
filteredBody = {
|
|
242
|
+
...body,
|
|
243
|
+
fileInfo: filteredFileInfo
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
filteredBody = {
|
|
248
|
+
...body,
|
|
249
|
+
fileInfo: parsedFileInfo
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
validFiles: securityResults.validFiles,
|
|
255
|
+
filteredBody
|
|
256
|
+
};
|
|
257
|
+
}
|
|
215
258
|
|
|
216
259
|
exports.detectMimeType = detectMimeType;
|
|
217
260
|
exports.enforceUploadSecurity = enforceUploadSecurity;
|
|
218
261
|
exports.extractFileInfo = extractFileInfo;
|
|
219
262
|
exports.isMimeTypeAllowed = isMimeTypeAllowed;
|
|
263
|
+
exports.prepareUploadRequest = prepareUploadRequest;
|
|
220
264
|
exports.validateFile = validateFile;
|
|
221
265
|
exports.validateFiles = validateFiles;
|
|
222
266
|
//# sourceMappingURL=mime-validation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime-validation.js","sources":["../../../server/src/utils/mime-validation.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { Core } from '@strapi/types';\nimport { errors } from '@strapi/utils';\n\nexport type SecurityConfig = {\n allowedTypes?: string[];\n deniedTypes?: string[];\n};\ntype UploadValidationError = {\n code: 'MIME_TYPE_NOT_ALLOWED' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR';\n message: string;\n details: Record<string, any>;\n};\n\ntype ValidationResult = {\n isValid: boolean;\n error?: UploadValidationError;\n};\n\ntype ErrorDetail = {\n file: any;\n originalIndex: number;\n error: UploadValidationError;\n};\n\nasync function readFileChunk(filePath: string, chunkSize: number = 4100): Promise<Buffer> {\n const buffer = await readFile(filePath);\n return buffer.length > chunkSize ? buffer.subarray(0, chunkSize) : buffer;\n}\n\nexport async function detectMimeType(file: any): Promise<string | undefined> {\n let buffer: Buffer;\n\n const filePath = file.path || file.filepath || file.tempFilePath;\n\n if (filePath) {\n try {\n buffer = await readFileChunk(filePath, 4100);\n } catch (error) {\n throw new Error(\n `Failed to read file: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n } else if (file.buffer) {\n buffer = file.buffer.length > 4100 ? file.buffer.subarray(0, 4100) : file.buffer;\n } else {\n // No file data available\n return undefined;\n }\n\n try {\n /**\n * Use dynamic import to support file-type which is ESM-only\n * Static imports fail during CommonJS build since bundler can't transform ESM-only packages\n * @see https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n */\n const { fileTypeFromBuffer } = await import('file-type');\n\n const result = await fileTypeFromBuffer(new Uint8Array(buffer));\n return result?.mime;\n } catch (error) {\n throw new Error(\n `Failed to detect MIME type: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nfunction matchesMimePattern(mimeType: string, patterns: string[]): boolean {\n if (!patterns?.length) return false;\n\n return patterns.some((pattern) => {\n const normalizedPattern = pattern.toLowerCase();\n const normalizedMimeType = mimeType.toLowerCase();\n\n if (normalizedPattern.includes('*')) {\n const regexPattern = normalizedPattern.replace(/\\*/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n const matches = regex.test(normalizedMimeType);\n return matches;\n }\n\n const exactMatch = normalizedPattern === normalizedMimeType;\n return exactMatch;\n });\n}\n\nexport function isMimeTypeAllowed(mimeType: string, config: SecurityConfig): boolean {\n const { allowedTypes, deniedTypes } = config;\n\n if (!mimeType) return false;\n\n if (deniedTypes?.length && matchesMimePattern(mimeType, deniedTypes)) {\n return false;\n }\n\n if (allowedTypes?.length) {\n return matchesMimePattern(mimeType, allowedTypes);\n }\n\n return true;\n}\n\nexport function extractFileInfo(file: any) {\n const fileName =\n file.originalFilename || file.name || file.filename || file.newFilename || 'unknown';\n const declaredMimeType = file.mimetype || file.type || file.mimeType || file.mime || '';\n\n return { fileName, declaredMimeType };\n}\n\nexport async function validateFile(\n file: any,\n config: SecurityConfig,\n strapi: Core.Strapi\n): Promise<ValidationResult> {\n const { allowedTypes, deniedTypes } = config;\n\n if (!allowedTypes && !deniedTypes) {\n return { isValid: true };\n }\n\n const { fileName, declaredMimeType } = extractFileInfo(file);\n\n let detectedMime: string | undefined;\n let mimeDetectionFailed = false;\n\n try {\n detectedMime = await detectMimeType(file);\n } catch (error) {\n mimeDetectionFailed = true;\n strapi.log.warn('Failed to detect MIME type from file', {\n fileName,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n const mimeToValidate = detectedMime || declaredMimeType;\n\n if (\n !detectedMime &&\n (declaredMimeType === 'application/octet-stream' || !declaredMimeType || mimeDetectionFailed)\n ) {\n if (allowedTypes?.length || deniedTypes?.length) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `Cannot verify file type for security reasons`,\n details: {\n fileName,\n reason: 'Unable to detect MIME type from file content',\n declaredType: declaredMimeType,\n mimeDetectionFailed,\n },\n },\n };\n }\n }\n\n if (\n mimeToValidate &&\n (allowedTypes || deniedTypes) &&\n !isMimeTypeAllowed(mimeToValidate, config)\n ) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `File type '${mimeToValidate}' is not allowed`,\n details: {\n fileName,\n detectedType: detectedMime,\n declaredType: declaredMimeType,\n finalType: mimeToValidate,\n allowedTypes,\n deniedTypes,\n },\n },\n };\n }\n\n return { isValid: true };\n}\n\nexport async function validateFiles(files: any, strapi: Core.Strapi): Promise<ValidationResult[]> {\n const filesArray = Array.isArray(files) ? files : [files];\n\n if (!filesArray.length) {\n return [];\n }\n\n const config: SecurityConfig = strapi.config.get('plugin::upload.security', {});\n if (\n config.allowedTypes &&\n (!Array.isArray(config.allowedTypes) ||\n !config.allowedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: allowedTypes must be an array of strings.'\n );\n }\n\n if (\n config.deniedTypes &&\n (!Array.isArray(config.deniedTypes) ||\n !config.deniedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: deniedTypes must be an array of strings.'\n );\n }\n\n if (!config.allowedTypes && !config.deniedTypes) {\n strapi.log.warn(\n 'No upload security configuration found. Consider configuring plugin.upload.security for enhanced file validation.'\n );\n return filesArray.map(() => ({ isValid: true }));\n }\n\n const validationPromises = filesArray.map(async (file, index) => {\n try {\n return await validateFile(file, config, strapi);\n } catch (error) {\n strapi.log.error('Unexpected error during file validation', {\n fileIndex: index,\n fileName: file?.name || file?.originalname,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return {\n isValid: false,\n error: {\n code: 'VALIDATION_ERROR' as const,\n message: `Validation failed for file at index ${index}`,\n details: {\n index,\n fileName: file?.name || file?.originalname,\n originalError: error instanceof Error ? error.message : String(error),\n },\n },\n };\n }\n });\n\n return Promise.all(validationPromises);\n}\n\nexport async function enforceUploadSecurity(\n files: any,\n strapi: Core.Strapi\n): Promise<{\n validFiles: any[];\n validFileNames: string[];\n errors: Array<ErrorDetail>;\n}> {\n const validationResults = await validateFiles(files, strapi);\n const filesArray = Array.isArray(files) ? files : [files];\n\n const validFiles: any[] = [];\n const validFileNames: string[] = [];\n const errors: Array<ErrorDetail> = [];\n\n for (const [index, result] of validationResults.entries()) {\n if (result.isValid) {\n const file = filesArray[index];\n validFiles.push(file);\n validFileNames.push(file.originalFilename || file.name);\n } else if (result.error) {\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: result.error,\n });\n } else {\n // Handle case where validation failed but no error details are provided\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: {\n code: 'UNKNOWN_ERROR' as const,\n message: 'File validation failed for unknown reason',\n details: {\n index,\n fileName: filesArray[index]?.name || filesArray[index]?.originalname,\n },\n },\n });\n }\n }\n\n return { validFiles, validFileNames, errors };\n}\n"],"names":["readFileChunk","filePath","chunkSize","buffer","readFile","length","subarray","detectMimeType","file","path","filepath","tempFilePath","error","Error","message","String","undefined","fileTypeFromBuffer","result","Uint8Array","mime","matchesMimePattern","mimeType","patterns","some","pattern","normalizedPattern","toLowerCase","normalizedMimeType","includes","regexPattern","replace","regex","RegExp","matches","test","exactMatch","isMimeTypeAllowed","config","allowedTypes","deniedTypes","extractFileInfo","fileName","originalFilename","name","filename","newFilename","declaredMimeType","mimetype","type","validateFile","strapi","isValid","detectedMime","mimeDetectionFailed","log","warn","mimeToValidate","code","details","reason","declaredType","detectedType","finalType","validateFiles","files","filesArray","Array","isArray","get","every","item","errors","ApplicationError","map","validationPromises","index","fileIndex","originalname","originalError","Promise","all","enforceUploadSecurity","validationResults","validFiles","validFileNames","entries","push","originalIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mime-validation.js","sources":["../../../server/src/utils/mime-validation.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { Core } from '@strapi/types';\nimport { errors } from '@strapi/utils';\n\nexport type SecurityConfig = {\n allowedTypes?: string[];\n deniedTypes?: string[];\n};\ntype UploadValidationError = {\n code: 'MIME_TYPE_NOT_ALLOWED' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR';\n message: string;\n details: Record<string, any>;\n};\n\ntype ValidationResult = {\n isValid: boolean;\n error?: UploadValidationError;\n};\n\ntype ErrorDetail = {\n file: any;\n originalIndex: number;\n error: UploadValidationError;\n};\n\nasync function readFileChunk(filePath: string, chunkSize: number = 4100): Promise<Buffer> {\n const buffer = await readFile(filePath);\n return buffer.length > chunkSize ? buffer.subarray(0, chunkSize) : buffer;\n}\n\nexport async function detectMimeType(file: any): Promise<string | undefined> {\n let buffer: Buffer;\n\n const filePath = file.path || file.filepath || file.tempFilePath;\n\n if (filePath) {\n try {\n buffer = await readFileChunk(filePath, 4100);\n } catch (error) {\n throw new Error(\n `Failed to read file: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n } else if (file.buffer) {\n buffer = file.buffer.length > 4100 ? file.buffer.subarray(0, 4100) : file.buffer;\n } else {\n // No file data available\n return undefined;\n }\n\n try {\n /**\n * Use dynamic import to support file-type which is ESM-only\n * Static imports fail during CommonJS build since bundler can't transform ESM-only packages\n * @see https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n */\n const { fileTypeFromBuffer } = await import('file-type');\n\n const result = await fileTypeFromBuffer(new Uint8Array(buffer));\n return result?.mime;\n } catch (error) {\n throw new Error(\n `Failed to detect MIME type: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nfunction matchesMimePattern(mimeType: string, patterns: string[]): boolean {\n if (!patterns?.length) return false;\n\n return patterns.some((pattern) => {\n const normalizedPattern = pattern.toLowerCase();\n const normalizedMimeType = mimeType.toLowerCase();\n\n if (normalizedPattern.includes('*')) {\n const regexPattern = normalizedPattern.replace(/\\*/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n const matches = regex.test(normalizedMimeType);\n return matches;\n }\n\n const exactMatch = normalizedPattern === normalizedMimeType;\n return exactMatch;\n });\n}\n\nexport function isMimeTypeAllowed(mimeType: string, config: SecurityConfig): boolean {\n const { allowedTypes, deniedTypes } = config;\n\n if (!mimeType) return false;\n\n if (deniedTypes?.length && matchesMimePattern(mimeType, deniedTypes)) {\n return false;\n }\n\n if (allowedTypes?.length) {\n return matchesMimePattern(mimeType, allowedTypes);\n }\n\n return true;\n}\n\nexport function extractFileInfo(file: any) {\n const fileName =\n file.originalFilename || file.name || file.filename || file.newFilename || 'unknown';\n const declaredMimeType = file.mimetype || file.type || file.mimeType || file.mime || '';\n\n return { fileName, declaredMimeType };\n}\n\nexport async function validateFile(\n file: any,\n config: SecurityConfig,\n strapi: Core.Strapi\n): Promise<ValidationResult> {\n const { allowedTypes, deniedTypes } = config;\n\n if (!allowedTypes && !deniedTypes) {\n return { isValid: true };\n }\n\n const { fileName, declaredMimeType } = extractFileInfo(file);\n\n let detectedMime: string | undefined;\n let mimeDetectionFailed = false;\n\n try {\n detectedMime = await detectMimeType(file);\n } catch (error) {\n mimeDetectionFailed = true;\n strapi.log.warn('Failed to detect MIME type from file', {\n fileName,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n const mimeToValidate = detectedMime || declaredMimeType;\n\n if (\n !detectedMime &&\n (declaredMimeType === 'application/octet-stream' || !declaredMimeType || mimeDetectionFailed)\n ) {\n if (allowedTypes?.length || deniedTypes?.length) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `Cannot verify file type for security reasons`,\n details: {\n fileName,\n reason: 'Unable to detect MIME type from file content',\n declaredType: declaredMimeType,\n mimeDetectionFailed,\n },\n },\n };\n }\n }\n\n if (\n mimeToValidate &&\n (allowedTypes || deniedTypes) &&\n !isMimeTypeAllowed(mimeToValidate, config)\n ) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `File type '${mimeToValidate}' is not allowed`,\n details: {\n fileName,\n detectedType: detectedMime,\n declaredType: declaredMimeType,\n finalType: mimeToValidate,\n allowedTypes,\n deniedTypes,\n },\n },\n };\n }\n\n return { isValid: true };\n}\n\nexport async function validateFiles(files: any, strapi: Core.Strapi): Promise<ValidationResult[]> {\n const filesArray = Array.isArray(files) ? files : [files];\n\n if (!filesArray.length) {\n return [];\n }\n\n const config: SecurityConfig = strapi.config.get('plugin::upload.security', {});\n if (\n config.allowedTypes &&\n (!Array.isArray(config.allowedTypes) ||\n !config.allowedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: allowedTypes must be an array of strings.'\n );\n }\n\n if (\n config.deniedTypes &&\n (!Array.isArray(config.deniedTypes) ||\n !config.deniedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: deniedTypes must be an array of strings.'\n );\n }\n\n if (!config.allowedTypes && !config.deniedTypes) {\n strapi.log.warn(\n 'No upload security configuration found. Consider configuring plugin.upload.security for enhanced file validation.'\n );\n return filesArray.map(() => ({ isValid: true }));\n }\n\n const validationPromises = filesArray.map(async (file, index) => {\n try {\n return await validateFile(file, config, strapi);\n } catch (error) {\n strapi.log.error('Unexpected error during file validation', {\n fileIndex: index,\n fileName: file?.name || file?.originalname,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return {\n isValid: false,\n error: {\n code: 'VALIDATION_ERROR' as const,\n message: `Validation failed for file at index ${index}`,\n details: {\n index,\n fileName: file?.name || file?.originalname,\n originalError: error instanceof Error ? error.message : String(error),\n },\n },\n };\n }\n });\n\n return Promise.all(validationPromises);\n}\n\nexport async function enforceUploadSecurity(\n files: any,\n strapi: Core.Strapi\n): Promise<{\n validFiles: any[];\n validFileNames: string[];\n errors: Array<ErrorDetail>;\n}> {\n const validationResults = await validateFiles(files, strapi);\n const filesArray = Array.isArray(files) ? files : [files];\n\n const validFiles: any[] = [];\n const validFileNames: string[] = [];\n const errors: Array<ErrorDetail> = [];\n\n for (const [index, result] of validationResults.entries()) {\n if (result.isValid) {\n const file = filesArray[index];\n validFiles.push(file);\n validFileNames.push(file.originalFilename || file.name);\n } else if (result.error) {\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: result.error,\n });\n } else {\n // Handle case where validation failed but no error details are provided\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: {\n code: 'UNKNOWN_ERROR' as const,\n message: 'File validation failed for unknown reason',\n details: {\n index,\n fileName: filesArray[index]?.name || filesArray[index]?.originalname,\n },\n },\n });\n }\n }\n\n return { validFiles, validFileNames, errors };\n}\n\nexport type PrepareUploadResult = {\n validFiles: any[];\n filteredBody: any;\n};\n\n/**\n * Prepare files and body for upload by enforcing security and parsing fileInfo\n */\nexport async function prepareUploadRequest(\n filesInput: any,\n body: any,\n strapi: Core.Strapi\n): Promise<PrepareUploadResult> {\n const securityResults = await enforceUploadSecurity(filesInput, strapi);\n\n if (securityResults.validFiles.length === 0) {\n throw new errors.ValidationError(\n securityResults.errors[0].error.message,\n securityResults.errors[0].error.details\n );\n }\n\n let filteredBody = body;\n if (body?.fileInfo) {\n // Parse JSON strings in fileInfo\n let parsedFileInfo = body.fileInfo;\n if (Array.isArray(body.fileInfo)) {\n parsedFileInfo = body.fileInfo.map((fi: any) =>\n typeof fi === 'string' ? JSON.parse(fi) : fi\n );\n } else if (typeof body.fileInfo === 'string') {\n parsedFileInfo = JSON.parse(body.fileInfo);\n }\n\n // Filter fileInfo by index - only keep entries for files that passed validation\n if (Array.isArray(parsedFileInfo)) {\n const invalidIndices = new Set(securityResults.errors.map((e) => e.originalIndex));\n const filteredFileInfo = parsedFileInfo.filter(\n (_: any, index: number) => !invalidIndices.has(index)\n );\n\n if (filteredFileInfo.length === 1) {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo[0],\n };\n } else {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo,\n };\n }\n } else {\n filteredBody = {\n ...body,\n fileInfo: parsedFileInfo,\n };\n }\n }\n\n return {\n validFiles: securityResults.validFiles,\n filteredBody,\n };\n}\n"],"names":["readFileChunk","filePath","chunkSize","buffer","readFile","length","subarray","detectMimeType","file","path","filepath","tempFilePath","error","Error","message","String","undefined","fileTypeFromBuffer","result","Uint8Array","mime","matchesMimePattern","mimeType","patterns","some","pattern","normalizedPattern","toLowerCase","normalizedMimeType","includes","regexPattern","replace","regex","RegExp","matches","test","exactMatch","isMimeTypeAllowed","config","allowedTypes","deniedTypes","extractFileInfo","fileName","originalFilename","name","filename","newFilename","declaredMimeType","mimetype","type","validateFile","strapi","isValid","detectedMime","mimeDetectionFailed","log","warn","mimeToValidate","code","details","reason","declaredType","detectedType","finalType","validateFiles","files","filesArray","Array","isArray","get","every","item","errors","ApplicationError","map","validationPromises","index","fileIndex","originalname","originalError","Promise","all","enforceUploadSecurity","validationResults","validFiles","validFileNames","entries","push","originalIndex","prepareUploadRequest","filesInput","body","securityResults","ValidationError","filteredBody","fileInfo","parsedFileInfo","fi","JSON","parse","invalidIndices","Set","e","filteredFileInfo","filter","_","has"],"mappings":"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"}
|
|
@@ -210,6 +210,49 @@ async function enforceUploadSecurity(files, strapi) {
|
|
|
210
210
|
errors
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Prepare files and body for upload by enforcing security and parsing fileInfo
|
|
215
|
+
*/ async function prepareUploadRequest(filesInput, body, strapi) {
|
|
216
|
+
const securityResults = await enforceUploadSecurity(filesInput, strapi);
|
|
217
|
+
if (securityResults.validFiles.length === 0) {
|
|
218
|
+
throw new errors.ValidationError(securityResults.errors[0].error.message, securityResults.errors[0].error.details);
|
|
219
|
+
}
|
|
220
|
+
let filteredBody = body;
|
|
221
|
+
if (body?.fileInfo) {
|
|
222
|
+
// Parse JSON strings in fileInfo
|
|
223
|
+
let parsedFileInfo = body.fileInfo;
|
|
224
|
+
if (Array.isArray(body.fileInfo)) {
|
|
225
|
+
parsedFileInfo = body.fileInfo.map((fi)=>typeof fi === 'string' ? JSON.parse(fi) : fi);
|
|
226
|
+
} else if (typeof body.fileInfo === 'string') {
|
|
227
|
+
parsedFileInfo = JSON.parse(body.fileInfo);
|
|
228
|
+
}
|
|
229
|
+
// Filter fileInfo by index - only keep entries for files that passed validation
|
|
230
|
+
if (Array.isArray(parsedFileInfo)) {
|
|
231
|
+
const invalidIndices = new Set(securityResults.errors.map((e)=>e.originalIndex));
|
|
232
|
+
const filteredFileInfo = parsedFileInfo.filter((_, index)=>!invalidIndices.has(index));
|
|
233
|
+
if (filteredFileInfo.length === 1) {
|
|
234
|
+
filteredBody = {
|
|
235
|
+
...body,
|
|
236
|
+
fileInfo: filteredFileInfo[0]
|
|
237
|
+
};
|
|
238
|
+
} else {
|
|
239
|
+
filteredBody = {
|
|
240
|
+
...body,
|
|
241
|
+
fileInfo: filteredFileInfo
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
filteredBody = {
|
|
246
|
+
...body,
|
|
247
|
+
fileInfo: parsedFileInfo
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
validFiles: securityResults.validFiles,
|
|
253
|
+
filteredBody
|
|
254
|
+
};
|
|
255
|
+
}
|
|
213
256
|
|
|
214
|
-
export { detectMimeType, enforceUploadSecurity, extractFileInfo, isMimeTypeAllowed, validateFile, validateFiles };
|
|
257
|
+
export { detectMimeType, enforceUploadSecurity, extractFileInfo, isMimeTypeAllowed, prepareUploadRequest, validateFile, validateFiles };
|
|
215
258
|
//# sourceMappingURL=mime-validation.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime-validation.mjs","sources":["../../../server/src/utils/mime-validation.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { Core } from '@strapi/types';\nimport { errors } from '@strapi/utils';\n\nexport type SecurityConfig = {\n allowedTypes?: string[];\n deniedTypes?: string[];\n};\ntype UploadValidationError = {\n code: 'MIME_TYPE_NOT_ALLOWED' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR';\n message: string;\n details: Record<string, any>;\n};\n\ntype ValidationResult = {\n isValid: boolean;\n error?: UploadValidationError;\n};\n\ntype ErrorDetail = {\n file: any;\n originalIndex: number;\n error: UploadValidationError;\n};\n\nasync function readFileChunk(filePath: string, chunkSize: number = 4100): Promise<Buffer> {\n const buffer = await readFile(filePath);\n return buffer.length > chunkSize ? buffer.subarray(0, chunkSize) : buffer;\n}\n\nexport async function detectMimeType(file: any): Promise<string | undefined> {\n let buffer: Buffer;\n\n const filePath = file.path || file.filepath || file.tempFilePath;\n\n if (filePath) {\n try {\n buffer = await readFileChunk(filePath, 4100);\n } catch (error) {\n throw new Error(\n `Failed to read file: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n } else if (file.buffer) {\n buffer = file.buffer.length > 4100 ? file.buffer.subarray(0, 4100) : file.buffer;\n } else {\n // No file data available\n return undefined;\n }\n\n try {\n /**\n * Use dynamic import to support file-type which is ESM-only\n * Static imports fail during CommonJS build since bundler can't transform ESM-only packages\n * @see https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n */\n const { fileTypeFromBuffer } = await import('file-type');\n\n const result = await fileTypeFromBuffer(new Uint8Array(buffer));\n return result?.mime;\n } catch (error) {\n throw new Error(\n `Failed to detect MIME type: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nfunction matchesMimePattern(mimeType: string, patterns: string[]): boolean {\n if (!patterns?.length) return false;\n\n return patterns.some((pattern) => {\n const normalizedPattern = pattern.toLowerCase();\n const normalizedMimeType = mimeType.toLowerCase();\n\n if (normalizedPattern.includes('*')) {\n const regexPattern = normalizedPattern.replace(/\\*/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n const matches = regex.test(normalizedMimeType);\n return matches;\n }\n\n const exactMatch = normalizedPattern === normalizedMimeType;\n return exactMatch;\n });\n}\n\nexport function isMimeTypeAllowed(mimeType: string, config: SecurityConfig): boolean {\n const { allowedTypes, deniedTypes } = config;\n\n if (!mimeType) return false;\n\n if (deniedTypes?.length && matchesMimePattern(mimeType, deniedTypes)) {\n return false;\n }\n\n if (allowedTypes?.length) {\n return matchesMimePattern(mimeType, allowedTypes);\n }\n\n return true;\n}\n\nexport function extractFileInfo(file: any) {\n const fileName =\n file.originalFilename || file.name || file.filename || file.newFilename || 'unknown';\n const declaredMimeType = file.mimetype || file.type || file.mimeType || file.mime || '';\n\n return { fileName, declaredMimeType };\n}\n\nexport async function validateFile(\n file: any,\n config: SecurityConfig,\n strapi: Core.Strapi\n): Promise<ValidationResult> {\n const { allowedTypes, deniedTypes } = config;\n\n if (!allowedTypes && !deniedTypes) {\n return { isValid: true };\n }\n\n const { fileName, declaredMimeType } = extractFileInfo(file);\n\n let detectedMime: string | undefined;\n let mimeDetectionFailed = false;\n\n try {\n detectedMime = await detectMimeType(file);\n } catch (error) {\n mimeDetectionFailed = true;\n strapi.log.warn('Failed to detect MIME type from file', {\n fileName,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n const mimeToValidate = detectedMime || declaredMimeType;\n\n if (\n !detectedMime &&\n (declaredMimeType === 'application/octet-stream' || !declaredMimeType || mimeDetectionFailed)\n ) {\n if (allowedTypes?.length || deniedTypes?.length) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `Cannot verify file type for security reasons`,\n details: {\n fileName,\n reason: 'Unable to detect MIME type from file content',\n declaredType: declaredMimeType,\n mimeDetectionFailed,\n },\n },\n };\n }\n }\n\n if (\n mimeToValidate &&\n (allowedTypes || deniedTypes) &&\n !isMimeTypeAllowed(mimeToValidate, config)\n ) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `File type '${mimeToValidate}' is not allowed`,\n details: {\n fileName,\n detectedType: detectedMime,\n declaredType: declaredMimeType,\n finalType: mimeToValidate,\n allowedTypes,\n deniedTypes,\n },\n },\n };\n }\n\n return { isValid: true };\n}\n\nexport async function validateFiles(files: any, strapi: Core.Strapi): Promise<ValidationResult[]> {\n const filesArray = Array.isArray(files) ? files : [files];\n\n if (!filesArray.length) {\n return [];\n }\n\n const config: SecurityConfig = strapi.config.get('plugin::upload.security', {});\n if (\n config.allowedTypes &&\n (!Array.isArray(config.allowedTypes) ||\n !config.allowedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: allowedTypes must be an array of strings.'\n );\n }\n\n if (\n config.deniedTypes &&\n (!Array.isArray(config.deniedTypes) ||\n !config.deniedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: deniedTypes must be an array of strings.'\n );\n }\n\n if (!config.allowedTypes && !config.deniedTypes) {\n strapi.log.warn(\n 'No upload security configuration found. Consider configuring plugin.upload.security for enhanced file validation.'\n );\n return filesArray.map(() => ({ isValid: true }));\n }\n\n const validationPromises = filesArray.map(async (file, index) => {\n try {\n return await validateFile(file, config, strapi);\n } catch (error) {\n strapi.log.error('Unexpected error during file validation', {\n fileIndex: index,\n fileName: file?.name || file?.originalname,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return {\n isValid: false,\n error: {\n code: 'VALIDATION_ERROR' as const,\n message: `Validation failed for file at index ${index}`,\n details: {\n index,\n fileName: file?.name || file?.originalname,\n originalError: error instanceof Error ? error.message : String(error),\n },\n },\n };\n }\n });\n\n return Promise.all(validationPromises);\n}\n\nexport async function enforceUploadSecurity(\n files: any,\n strapi: Core.Strapi\n): Promise<{\n validFiles: any[];\n validFileNames: string[];\n errors: Array<ErrorDetail>;\n}> {\n const validationResults = await validateFiles(files, strapi);\n const filesArray = Array.isArray(files) ? files : [files];\n\n const validFiles: any[] = [];\n const validFileNames: string[] = [];\n const errors: Array<ErrorDetail> = [];\n\n for (const [index, result] of validationResults.entries()) {\n if (result.isValid) {\n const file = filesArray[index];\n validFiles.push(file);\n validFileNames.push(file.originalFilename || file.name);\n } else if (result.error) {\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: result.error,\n });\n } else {\n // Handle case where validation failed but no error details are provided\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: {\n code: 'UNKNOWN_ERROR' as const,\n message: 'File validation failed for unknown reason',\n details: {\n index,\n fileName: filesArray[index]?.name || filesArray[index]?.originalname,\n },\n },\n });\n }\n }\n\n return { validFiles, validFileNames, errors };\n}\n"],"names":["readFileChunk","filePath","chunkSize","buffer","readFile","length","subarray","detectMimeType","file","path","filepath","tempFilePath","error","Error","message","String","undefined","fileTypeFromBuffer","result","Uint8Array","mime","matchesMimePattern","mimeType","patterns","some","pattern","normalizedPattern","toLowerCase","normalizedMimeType","includes","regexPattern","replace","regex","RegExp","matches","test","exactMatch","isMimeTypeAllowed","config","allowedTypes","deniedTypes","extractFileInfo","fileName","originalFilename","name","filename","newFilename","declaredMimeType","mimetype","type","validateFile","strapi","isValid","detectedMime","mimeDetectionFailed","log","warn","mimeToValidate","code","details","reason","declaredType","detectedType","finalType","validateFiles","files","filesArray","Array","isArray","get","every","item","errors","ApplicationError","map","validationPromises","index","fileIndex","originalname","originalError","Promise","all","enforceUploadSecurity","validationResults","validFiles","validFileNames","entries","push","originalIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mime-validation.mjs","sources":["../../../server/src/utils/mime-validation.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { Core } from '@strapi/types';\nimport { errors } from '@strapi/utils';\n\nexport type SecurityConfig = {\n allowedTypes?: string[];\n deniedTypes?: string[];\n};\ntype UploadValidationError = {\n code: 'MIME_TYPE_NOT_ALLOWED' | 'VALIDATION_ERROR' | 'UNKNOWN_ERROR';\n message: string;\n details: Record<string, any>;\n};\n\ntype ValidationResult = {\n isValid: boolean;\n error?: UploadValidationError;\n};\n\ntype ErrorDetail = {\n file: any;\n originalIndex: number;\n error: UploadValidationError;\n};\n\nasync function readFileChunk(filePath: string, chunkSize: number = 4100): Promise<Buffer> {\n const buffer = await readFile(filePath);\n return buffer.length > chunkSize ? buffer.subarray(0, chunkSize) : buffer;\n}\n\nexport async function detectMimeType(file: any): Promise<string | undefined> {\n let buffer: Buffer;\n\n const filePath = file.path || file.filepath || file.tempFilePath;\n\n if (filePath) {\n try {\n buffer = await readFileChunk(filePath, 4100);\n } catch (error) {\n throw new Error(\n `Failed to read file: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n } else if (file.buffer) {\n buffer = file.buffer.length > 4100 ? file.buffer.subarray(0, 4100) : file.buffer;\n } else {\n // No file data available\n return undefined;\n }\n\n try {\n /**\n * Use dynamic import to support file-type which is ESM-only\n * Static imports fail during CommonJS build since bundler can't transform ESM-only packages\n * @see https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n */\n const { fileTypeFromBuffer } = await import('file-type');\n\n const result = await fileTypeFromBuffer(new Uint8Array(buffer));\n return result?.mime;\n } catch (error) {\n throw new Error(\n `Failed to detect MIME type: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n}\n\nfunction matchesMimePattern(mimeType: string, patterns: string[]): boolean {\n if (!patterns?.length) return false;\n\n return patterns.some((pattern) => {\n const normalizedPattern = pattern.toLowerCase();\n const normalizedMimeType = mimeType.toLowerCase();\n\n if (normalizedPattern.includes('*')) {\n const regexPattern = normalizedPattern.replace(/\\*/g, '.*');\n\n const regex = new RegExp(`^${regexPattern}$`);\n const matches = regex.test(normalizedMimeType);\n return matches;\n }\n\n const exactMatch = normalizedPattern === normalizedMimeType;\n return exactMatch;\n });\n}\n\nexport function isMimeTypeAllowed(mimeType: string, config: SecurityConfig): boolean {\n const { allowedTypes, deniedTypes } = config;\n\n if (!mimeType) return false;\n\n if (deniedTypes?.length && matchesMimePattern(mimeType, deniedTypes)) {\n return false;\n }\n\n if (allowedTypes?.length) {\n return matchesMimePattern(mimeType, allowedTypes);\n }\n\n return true;\n}\n\nexport function extractFileInfo(file: any) {\n const fileName =\n file.originalFilename || file.name || file.filename || file.newFilename || 'unknown';\n const declaredMimeType = file.mimetype || file.type || file.mimeType || file.mime || '';\n\n return { fileName, declaredMimeType };\n}\n\nexport async function validateFile(\n file: any,\n config: SecurityConfig,\n strapi: Core.Strapi\n): Promise<ValidationResult> {\n const { allowedTypes, deniedTypes } = config;\n\n if (!allowedTypes && !deniedTypes) {\n return { isValid: true };\n }\n\n const { fileName, declaredMimeType } = extractFileInfo(file);\n\n let detectedMime: string | undefined;\n let mimeDetectionFailed = false;\n\n try {\n detectedMime = await detectMimeType(file);\n } catch (error) {\n mimeDetectionFailed = true;\n strapi.log.warn('Failed to detect MIME type from file', {\n fileName,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n\n const mimeToValidate = detectedMime || declaredMimeType;\n\n if (\n !detectedMime &&\n (declaredMimeType === 'application/octet-stream' || !declaredMimeType || mimeDetectionFailed)\n ) {\n if (allowedTypes?.length || deniedTypes?.length) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `Cannot verify file type for security reasons`,\n details: {\n fileName,\n reason: 'Unable to detect MIME type from file content',\n declaredType: declaredMimeType,\n mimeDetectionFailed,\n },\n },\n };\n }\n }\n\n if (\n mimeToValidate &&\n (allowedTypes || deniedTypes) &&\n !isMimeTypeAllowed(mimeToValidate, config)\n ) {\n return {\n isValid: false,\n error: {\n code: 'MIME_TYPE_NOT_ALLOWED',\n message: `File type '${mimeToValidate}' is not allowed`,\n details: {\n fileName,\n detectedType: detectedMime,\n declaredType: declaredMimeType,\n finalType: mimeToValidate,\n allowedTypes,\n deniedTypes,\n },\n },\n };\n }\n\n return { isValid: true };\n}\n\nexport async function validateFiles(files: any, strapi: Core.Strapi): Promise<ValidationResult[]> {\n const filesArray = Array.isArray(files) ? files : [files];\n\n if (!filesArray.length) {\n return [];\n }\n\n const config: SecurityConfig = strapi.config.get('plugin::upload.security', {});\n if (\n config.allowedTypes &&\n (!Array.isArray(config.allowedTypes) ||\n !config.allowedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: allowedTypes must be an array of strings.'\n );\n }\n\n if (\n config.deniedTypes &&\n (!Array.isArray(config.deniedTypes) ||\n !config.deniedTypes.every((item) => typeof item === 'string'))\n ) {\n throw new errors.ApplicationError(\n 'Invalid configuration: deniedTypes must be an array of strings.'\n );\n }\n\n if (!config.allowedTypes && !config.deniedTypes) {\n strapi.log.warn(\n 'No upload security configuration found. Consider configuring plugin.upload.security for enhanced file validation.'\n );\n return filesArray.map(() => ({ isValid: true }));\n }\n\n const validationPromises = filesArray.map(async (file, index) => {\n try {\n return await validateFile(file, config, strapi);\n } catch (error) {\n strapi.log.error('Unexpected error during file validation', {\n fileIndex: index,\n fileName: file?.name || file?.originalname,\n error: error instanceof Error ? error.message : String(error),\n });\n\n return {\n isValid: false,\n error: {\n code: 'VALIDATION_ERROR' as const,\n message: `Validation failed for file at index ${index}`,\n details: {\n index,\n fileName: file?.name || file?.originalname,\n originalError: error instanceof Error ? error.message : String(error),\n },\n },\n };\n }\n });\n\n return Promise.all(validationPromises);\n}\n\nexport async function enforceUploadSecurity(\n files: any,\n strapi: Core.Strapi\n): Promise<{\n validFiles: any[];\n validFileNames: string[];\n errors: Array<ErrorDetail>;\n}> {\n const validationResults = await validateFiles(files, strapi);\n const filesArray = Array.isArray(files) ? files : [files];\n\n const validFiles: any[] = [];\n const validFileNames: string[] = [];\n const errors: Array<ErrorDetail> = [];\n\n for (const [index, result] of validationResults.entries()) {\n if (result.isValid) {\n const file = filesArray[index];\n validFiles.push(file);\n validFileNames.push(file.originalFilename || file.name);\n } else if (result.error) {\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: result.error,\n });\n } else {\n // Handle case where validation failed but no error details are provided\n errors.push({\n file: filesArray[index],\n originalIndex: index,\n error: {\n code: 'UNKNOWN_ERROR' as const,\n message: 'File validation failed for unknown reason',\n details: {\n index,\n fileName: filesArray[index]?.name || filesArray[index]?.originalname,\n },\n },\n });\n }\n }\n\n return { validFiles, validFileNames, errors };\n}\n\nexport type PrepareUploadResult = {\n validFiles: any[];\n filteredBody: any;\n};\n\n/**\n * Prepare files and body for upload by enforcing security and parsing fileInfo\n */\nexport async function prepareUploadRequest(\n filesInput: any,\n body: any,\n strapi: Core.Strapi\n): Promise<PrepareUploadResult> {\n const securityResults = await enforceUploadSecurity(filesInput, strapi);\n\n if (securityResults.validFiles.length === 0) {\n throw new errors.ValidationError(\n securityResults.errors[0].error.message,\n securityResults.errors[0].error.details\n );\n }\n\n let filteredBody = body;\n if (body?.fileInfo) {\n // Parse JSON strings in fileInfo\n let parsedFileInfo = body.fileInfo;\n if (Array.isArray(body.fileInfo)) {\n parsedFileInfo = body.fileInfo.map((fi: any) =>\n typeof fi === 'string' ? JSON.parse(fi) : fi\n );\n } else if (typeof body.fileInfo === 'string') {\n parsedFileInfo = JSON.parse(body.fileInfo);\n }\n\n // Filter fileInfo by index - only keep entries for files that passed validation\n if (Array.isArray(parsedFileInfo)) {\n const invalidIndices = new Set(securityResults.errors.map((e) => e.originalIndex));\n const filteredFileInfo = parsedFileInfo.filter(\n (_: any, index: number) => !invalidIndices.has(index)\n );\n\n if (filteredFileInfo.length === 1) {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo[0],\n };\n } else {\n filteredBody = {\n ...body,\n fileInfo: filteredFileInfo,\n };\n }\n } else {\n filteredBody = {\n ...body,\n fileInfo: parsedFileInfo,\n };\n }\n }\n\n return {\n validFiles: securityResults.validFiles,\n filteredBody,\n };\n}\n"],"names":["readFileChunk","filePath","chunkSize","buffer","readFile","length","subarray","detectMimeType","file","path","filepath","tempFilePath","error","Error","message","String","undefined","fileTypeFromBuffer","result","Uint8Array","mime","matchesMimePattern","mimeType","patterns","some","pattern","normalizedPattern","toLowerCase","normalizedMimeType","includes","regexPattern","replace","regex","RegExp","matches","test","exactMatch","isMimeTypeAllowed","config","allowedTypes","deniedTypes","extractFileInfo","fileName","originalFilename","name","filename","newFilename","declaredMimeType","mimetype","type","validateFile","strapi","isValid","detectedMime","mimeDetectionFailed","log","warn","mimeToValidate","code","details","reason","declaredType","detectedType","finalType","validateFiles","files","filesArray","Array","isArray","get","every","item","errors","ApplicationError","map","validationPromises","index","fileIndex","originalname","originalError","Promise","all","enforceUploadSecurity","validationResults","validFiles","validFileNames","entries","push","originalIndex","prepareUploadRequest","filesInput","body","securityResults","ValidationError","filteredBody","fileInfo","parsedFileInfo","fi","JSON","parse","invalidIndices","Set","e","filteredFileInfo","filter","_","has"],"mappings":";;;AAyBA,eAAeA,aAAcC,CAAAA,QAAgB,EAAEC,SAAAA,GAAoB,IAAI,EAAA;IACrE,MAAMC,MAAAA,GAAS,MAAMC,QAASH,CAAAA,QAAAA,CAAAA;IAC9B,OAAOE,MAAAA,CAAOE,MAAM,GAAGH,SAAAA,GAAYC,OAAOG,QAAQ,CAAC,GAAGJ,SAAaC,CAAAA,GAAAA,MAAAA;AACrE;AAEO,eAAeI,eAAeC,IAAS,EAAA;IAC5C,IAAIL,MAAAA;IAEJ,MAAMF,QAAAA,GAAWO,KAAKC,IAAI,IAAID,KAAKE,QAAQ,IAAIF,KAAKG,YAAY;AAEhE,IAAA,IAAIV,QAAU,EAAA;QACZ,IAAI;YACFE,MAAS,GAAA,MAAMH,cAAcC,QAAU,EAAA,IAAA,CAAA;AACzC,SAAA,CAAE,OAAOW,KAAO,EAAA;YACd,MAAM,IAAIC,KACR,CAAA,CAAC,qBAAqB,EAAED,KAAiBC,YAAAA,KAAAA,GAAQD,KAAME,CAAAA,OAAO,GAAGC,MAAAA,CAAOH,KAAQ,CAAA,CAAA,CAAA,CAAA;AAEpF;KACK,MAAA,IAAIJ,IAAKL,CAAAA,MAAM,EAAE;AACtBA,QAAAA,MAAAA,GAASK,IAAKL,CAAAA,MAAM,CAACE,MAAM,GAAG,IAAOG,GAAAA,IAAAA,CAAKL,MAAM,CAACG,QAAQ,CAAC,CAAG,EAAA,IAAA,CAAA,GAAQE,KAAKL,MAAM;KAC3E,MAAA;;QAEL,OAAOa,SAAAA;AACT;IAEA,IAAI;AACF;;;;AAIC,QACD,MAAM,EAAEC,kBAAkB,EAAE,GAAG,MAAM,OAAO,WAAA,CAAA;AAE5C,QAAA,MAAMC,MAAS,GAAA,MAAMD,kBAAmB,CAAA,IAAIE,UAAWhB,CAAAA,MAAAA,CAAAA,CAAAA;AACvD,QAAA,OAAOe,MAAQE,EAAAA,IAAAA;AACjB,KAAA,CAAE,OAAOR,KAAO,EAAA;QACd,MAAM,IAAIC,KACR,CAAA,CAAC,4BAA4B,EAAED,KAAiBC,YAAAA,KAAAA,GAAQD,KAAME,CAAAA,OAAO,GAAGC,MAAAA,CAAOH,KAAQ,CAAA,CAAA,CAAA,CAAA;AAE3F;AACF;AAEA,SAASS,kBAAAA,CAAmBC,QAAgB,EAAEC,QAAkB,EAAA;IAC9D,IAAI,CAACA,QAAUlB,EAAAA,MAAAA,EAAQ,OAAO,KAAA;IAE9B,OAAOkB,QAAAA,CAASC,IAAI,CAAC,CAACC,OAAAA,GAAAA;QACpB,MAAMC,iBAAAA,GAAoBD,QAAQE,WAAW,EAAA;QAC7C,MAAMC,kBAAAA,GAAqBN,SAASK,WAAW,EAAA;QAE/C,IAAID,iBAAAA,CAAkBG,QAAQ,CAAC,GAAM,CAAA,EAAA;AACnC,YAAA,MAAMC,YAAeJ,GAAAA,iBAAAA,CAAkBK,OAAO,CAAC,KAAO,EAAA,IAAA,CAAA;YAEtD,MAAMC,KAAAA,GAAQ,IAAIC,MAAO,CAAA,CAAC,CAAC,EAAEH,YAAAA,CAAa,CAAC,CAAC,CAAA;YAC5C,MAAMI,OAAAA,GAAUF,KAAMG,CAAAA,IAAI,CAACP,kBAAAA,CAAAA;YAC3B,OAAOM,OAAAA;AACT;AAEA,QAAA,MAAME,aAAaV,iBAAsBE,KAAAA,kBAAAA;QACzC,OAAOQ,UAAAA;AACT,KAAA,CAAA;AACF;AAEO,SAASC,iBAAAA,CAAkBf,QAAgB,EAAEgB,MAAsB,EAAA;AACxE,IAAA,MAAM,EAAEC,YAAY,EAAEC,WAAW,EAAE,GAAGF,MAAAA;IAEtC,IAAI,CAAChB,UAAU,OAAO,KAAA;AAEtB,IAAA,IAAIkB,WAAanC,EAAAA,MAAAA,IAAUgB,kBAAmBC,CAAAA,QAAAA,EAAUkB,WAAc,CAAA,EAAA;QACpE,OAAO,KAAA;AACT;AAEA,IAAA,IAAID,cAAclC,MAAQ,EAAA;AACxB,QAAA,OAAOgB,mBAAmBC,QAAUiB,EAAAA,YAAAA,CAAAA;AACtC;IAEA,OAAO,IAAA;AACT;AAEO,SAASE,gBAAgBjC,IAAS,EAAA;AACvC,IAAA,MAAMkC,QACJlC,GAAAA,IAAAA,CAAKmC,gBAAgB,IAAInC,IAAKoC,CAAAA,IAAI,IAAIpC,IAAAA,CAAKqC,QAAQ,IAAIrC,IAAKsC,CAAAA,WAAW,IAAI,SAAA;AAC7E,IAAA,MAAMC,gBAAmBvC,GAAAA,IAAAA,CAAKwC,QAAQ,IAAIxC,IAAKyC,CAAAA,IAAI,IAAIzC,IAAAA,CAAKc,QAAQ,IAAId,IAAKY,CAAAA,IAAI,IAAI,EAAA;IAErF,OAAO;AAAEsB,QAAAA,QAAAA;AAAUK,QAAAA;AAAiB,KAAA;AACtC;AAEO,eAAeG,YACpB1C,CAAAA,IAAS,EACT8B,MAAsB,EACtBa,MAAmB,EAAA;AAEnB,IAAA,MAAM,EAAEZ,YAAY,EAAEC,WAAW,EAAE,GAAGF,MAAAA;IAEtC,IAAI,CAACC,YAAgB,IAAA,CAACC,WAAa,EAAA;QACjC,OAAO;YAAEY,OAAS,EAAA;AAAK,SAAA;AACzB;AAEA,IAAA,MAAM,EAAEV,QAAQ,EAAEK,gBAAgB,EAAE,GAAGN,eAAgBjC,CAAAA,IAAAA,CAAAA;IAEvD,IAAI6C,YAAAA;AACJ,IAAA,IAAIC,mBAAsB,GAAA,KAAA;IAE1B,IAAI;AACFD,QAAAA,YAAAA,GAAe,MAAM9C,cAAeC,CAAAA,IAAAA,CAAAA;AACtC,KAAA,CAAE,OAAOI,KAAO,EAAA;QACd0C,mBAAsB,GAAA,IAAA;AACtBH,QAAAA,MAAAA,CAAOI,GAAG,CAACC,IAAI,CAAC,sCAAwC,EAAA;AACtDd,YAAAA,QAAAA;AACA9B,YAAAA,KAAAA,EAAOA,KAAiBC,YAAAA,KAAAA,GAAQD,KAAME,CAAAA,OAAO,GAAGC,MAAOH,CAAAA,KAAAA;AACzD,SAAA,CAAA;AACF;AAEA,IAAA,MAAM6C,iBAAiBJ,YAAgBN,IAAAA,gBAAAA;IAEvC,IACE,CAACM,iBACAN,gBAAAA,KAAqB,8BAA8B,CAACA,gBAAAA,IAAoBO,mBAAkB,CAC3F,EAAA;QACA,IAAIf,YAAAA,EAAclC,MAAUmC,IAAAA,WAAAA,EAAanC,MAAQ,EAAA;YAC/C,OAAO;gBACL+C,OAAS,EAAA,KAAA;gBACTxC,KAAO,EAAA;oBACL8C,IAAM,EAAA,uBAAA;oBACN5C,OAAS,EAAA,CAAC,4CAA4C,CAAC;oBACvD6C,OAAS,EAAA;AACPjB,wBAAAA,QAAAA;wBACAkB,MAAQ,EAAA,8CAAA;wBACRC,YAAcd,EAAAA,gBAAAA;AACdO,wBAAAA;AACF;AACF;AACF,aAAA;AACF;AACF;IAEA,IACEG,cAAAA,KACClB,YAAgBC,IAAAA,WAAU,KAC3B,CAACH,iBAAAA,CAAkBoB,gBAAgBnB,MACnC,CAAA,EAAA;QACA,OAAO;YACLc,OAAS,EAAA,KAAA;YACTxC,KAAO,EAAA;gBACL8C,IAAM,EAAA,uBAAA;AACN5C,gBAAAA,OAAAA,EAAS,CAAC,WAAW,EAAE2C,cAAAA,CAAe,gBAAgB,CAAC;gBACvDE,OAAS,EAAA;AACPjB,oBAAAA,QAAAA;oBACAoB,YAAcT,EAAAA,YAAAA;oBACdQ,YAAcd,EAAAA,gBAAAA;oBACdgB,SAAWN,EAAAA,cAAAA;AACXlB,oBAAAA,YAAAA;AACAC,oBAAAA;AACF;AACF;AACF,SAAA;AACF;IAEA,OAAO;QAAEY,OAAS,EAAA;AAAK,KAAA;AACzB;AAEO,eAAeY,aAAAA,CAAcC,KAAU,EAAEd,MAAmB,EAAA;AACjE,IAAA,MAAMe,UAAaC,GAAAA,KAAAA,CAAMC,OAAO,CAACH,SAASA,KAAQ,GAAA;AAACA,QAAAA;AAAM,KAAA;IAEzD,IAAI,CAACC,UAAW7D,CAAAA,MAAM,EAAE;AACtB,QAAA,OAAO,EAAE;AACX;AAEA,IAAA,MAAMiC,SAAyBa,MAAOb,CAAAA,MAAM,CAAC+B,GAAG,CAAC,2BAA2B,EAAC,CAAA;IAC7E,IACE/B,MAAAA,CAAOC,YAAY,KAClB,CAAC4B,KAAMC,CAAAA,OAAO,CAAC9B,MAAAA,CAAOC,YAAY,CAAA,IACjC,CAACD,MAAOC,CAAAA,YAAY,CAAC+B,KAAK,CAAC,CAACC,IAAS,GAAA,OAAOA,IAAS,KAAA,QAAA,CAAQ,CAC/D,EAAA;QACA,MAAM,IAAIC,MAAOC,CAAAA,gBAAgB,CAC/B,kEAAA,CAAA;AAEJ;IAEA,IACEnC,MAAAA,CAAOE,WAAW,KACjB,CAAC2B,KAAMC,CAAAA,OAAO,CAAC9B,MAAAA,CAAOE,WAAW,CAAA,IAChC,CAACF,MAAOE,CAAAA,WAAW,CAAC8B,KAAK,CAAC,CAACC,IAAS,GAAA,OAAOA,IAAS,KAAA,QAAA,CAAQ,CAC9D,EAAA;QACA,MAAM,IAAIC,MAAOC,CAAAA,gBAAgB,CAC/B,iEAAA,CAAA;AAEJ;AAEA,IAAA,IAAI,CAACnC,MAAOC,CAAAA,YAAY,IAAI,CAACD,MAAAA,CAAOE,WAAW,EAAE;QAC/CW,MAAOI,CAAAA,GAAG,CAACC,IAAI,CACb,mHAAA,CAAA;AAEF,QAAA,OAAOU,UAAWQ,CAAAA,GAAG,CAAC,KAAO;gBAAEtB,OAAS,EAAA;aAAK,CAAA,CAAA;AAC/C;AAEA,IAAA,MAAMuB,kBAAqBT,GAAAA,UAAAA,CAAWQ,GAAG,CAAC,OAAOlE,IAAMoE,EAAAA,KAAAA,GAAAA;QACrD,IAAI;YACF,OAAO,MAAM1B,YAAa1C,CAAAA,IAAAA,EAAM8B,MAAQa,EAAAA,MAAAA,CAAAA;AAC1C,SAAA,CAAE,OAAOvC,KAAO,EAAA;AACduC,YAAAA,MAAAA,CAAOI,GAAG,CAAC3C,KAAK,CAAC,yCAA2C,EAAA;gBAC1DiE,SAAWD,EAAAA,KAAAA;gBACXlC,QAAUlC,EAAAA,IAAAA,EAAMoC,QAAQpC,IAAMsE,EAAAA,YAAAA;AAC9BlE,gBAAAA,KAAAA,EAAOA,KAAiBC,YAAAA,KAAAA,GAAQD,KAAME,CAAAA,OAAO,GAAGC,MAAOH,CAAAA,KAAAA;AACzD,aAAA,CAAA;YAEA,OAAO;gBACLwC,OAAS,EAAA,KAAA;gBACTxC,KAAO,EAAA;oBACL8C,IAAM,EAAA,kBAAA;oBACN5C,OAAS,EAAA,CAAC,oCAAoC,EAAE8D,KAAO,CAAA,CAAA;oBACvDjB,OAAS,EAAA;AACPiB,wBAAAA,KAAAA;wBACAlC,QAAUlC,EAAAA,IAAAA,EAAMoC,QAAQpC,IAAMsE,EAAAA,YAAAA;AAC9BC,wBAAAA,aAAAA,EAAenE,KAAiBC,YAAAA,KAAAA,GAAQD,KAAME,CAAAA,OAAO,GAAGC,MAAOH,CAAAA,KAAAA;AACjE;AACF;AACF,aAAA;AACF;AACF,KAAA,CAAA;IAEA,OAAOoE,OAAAA,CAAQC,GAAG,CAACN,kBAAAA,CAAAA;AACrB;AAEO,eAAeO,qBAAAA,CACpBjB,KAAU,EACVd,MAAmB,EAAA;IAMnB,MAAMgC,iBAAAA,GAAoB,MAAMnB,aAAAA,CAAcC,KAAOd,EAAAA,MAAAA,CAAAA;AACrD,IAAA,MAAMe,UAAaC,GAAAA,KAAAA,CAAMC,OAAO,CAACH,SAASA,KAAQ,GAAA;AAACA,QAAAA;AAAM,KAAA;AAEzD,IAAA,MAAMmB,aAAoB,EAAE;AAC5B,IAAA,MAAMC,iBAA2B,EAAE;AACnC,IAAA,MAAMb,SAA6B,EAAE;AAErC,IAAA,KAAK,MAAM,CAACI,KAAAA,EAAO1D,OAAO,IAAIiE,iBAAAA,CAAkBG,OAAO,EAAI,CAAA;QACzD,IAAIpE,MAAAA,CAAOkC,OAAO,EAAE;YAClB,MAAM5C,IAAAA,GAAO0D,UAAU,CAACU,KAAM,CAAA;AAC9BQ,YAAAA,UAAAA,CAAWG,IAAI,CAAC/E,IAAAA,CAAAA;AAChB6E,YAAAA,cAAAA,CAAeE,IAAI,CAAC/E,IAAAA,CAAKmC,gBAAgB,IAAInC,KAAKoC,IAAI,CAAA;SACjD,MAAA,IAAI1B,MAAON,CAAAA,KAAK,EAAE;AACvB4D,YAAAA,MAAAA,CAAOe,IAAI,CAAC;gBACV/E,IAAM0D,EAAAA,UAAU,CAACU,KAAM,CAAA;gBACvBY,aAAeZ,EAAAA,KAAAA;AACfhE,gBAAAA,KAAAA,EAAOM,OAAON;AAChB,aAAA,CAAA;SACK,MAAA;;AAEL4D,YAAAA,MAAAA,CAAOe,IAAI,CAAC;gBACV/E,IAAM0D,EAAAA,UAAU,CAACU,KAAM,CAAA;gBACvBY,aAAeZ,EAAAA,KAAAA;gBACfhE,KAAO,EAAA;oBACL8C,IAAM,EAAA,eAAA;oBACN5C,OAAS,EAAA,2CAAA;oBACT6C,OAAS,EAAA;AACPiB,wBAAAA,KAAAA;wBACAlC,QAAUwB,EAAAA,UAAU,CAACU,KAAM,CAAA,EAAEhC,QAAQsB,UAAU,CAACU,MAAM,EAAEE;AAC1D;AACF;AACF,aAAA,CAAA;AACF;AACF;IAEA,OAAO;AAAEM,QAAAA,UAAAA;AAAYC,QAAAA,cAAAA;AAAgBb,QAAAA;AAAO,KAAA;AAC9C;AAOA;;AAEC,IACM,eAAeiB,oBAAAA,CACpBC,UAAe,EACfC,IAAS,EACTxC,MAAmB,EAAA;IAEnB,MAAMyC,eAAAA,GAAkB,MAAMV,qBAAAA,CAAsBQ,UAAYvC,EAAAA,MAAAA,CAAAA;AAEhE,IAAA,IAAIyC,eAAgBR,CAAAA,UAAU,CAAC/E,MAAM,KAAK,CAAG,EAAA;QAC3C,MAAM,IAAImE,OAAOqB,eAAe,CAC9BD,gBAAgBpB,MAAM,CAAC,EAAE,CAAC5D,KAAK,CAACE,OAAO,EACvC8E,gBAAgBpB,MAAM,CAAC,EAAE,CAAC5D,KAAK,CAAC+C,OAAO,CAAA;AAE3C;AAEA,IAAA,IAAImC,YAAeH,GAAAA,IAAAA;AACnB,IAAA,IAAIA,MAAMI,QAAU,EAAA;;QAElB,IAAIC,cAAAA,GAAiBL,KAAKI,QAAQ;AAClC,QAAA,IAAI5B,KAAMC,CAAAA,OAAO,CAACuB,IAAAA,CAAKI,QAAQ,CAAG,EAAA;AAChCC,YAAAA,cAAAA,GAAiBL,IAAKI,CAAAA,QAAQ,CAACrB,GAAG,CAAC,CAACuB,EAClC,GAAA,OAAOA,EAAO,KAAA,QAAA,GAAWC,IAAKC,CAAAA,KAAK,CAACF,EAAMA,CAAAA,GAAAA,EAAAA,CAAAA;AAE9C,SAAA,MAAO,IAAI,OAAON,IAAKI,CAAAA,QAAQ,KAAK,QAAU,EAAA;AAC5CC,YAAAA,cAAAA,GAAiBE,IAAKC,CAAAA,KAAK,CAACR,IAAAA,CAAKI,QAAQ,CAAA;AAC3C;;QAGA,IAAI5B,KAAAA,CAAMC,OAAO,CAAC4B,cAAiB,CAAA,EAAA;YACjC,MAAMI,cAAAA,GAAiB,IAAIC,GAAAA,CAAIT,eAAgBpB,CAAAA,MAAM,CAACE,GAAG,CAAC,CAAC4B,CAAMA,GAAAA,CAAAA,CAAEd,aAAa,CAAA,CAAA;YAChF,MAAMe,gBAAAA,GAAmBP,cAAeQ,CAAAA,MAAM,CAC5C,CAACC,GAAQ7B,KAAkB,GAAA,CAACwB,cAAeM,CAAAA,GAAG,CAAC9B,KAAAA,CAAAA,CAAAA;YAGjD,IAAI2B,gBAAAA,CAAiBlG,MAAM,KAAK,CAAG,EAAA;gBACjCyF,YAAe,GAAA;AACb,oBAAA,GAAGH,IAAI;oBACPI,QAAUQ,EAAAA,gBAAgB,CAAC,CAAE;AAC/B,iBAAA;aACK,MAAA;gBACLT,YAAe,GAAA;AACb,oBAAA,GAAGH,IAAI;oBACPI,QAAUQ,EAAAA;AACZ,iBAAA;AACF;SACK,MAAA;YACLT,YAAe,GAAA;AACb,gBAAA,GAAGH,IAAI;gBACPI,QAAUC,EAAAA;AACZ,aAAA;AACF;AACF;IAEA,OAAO;AACLZ,QAAAA,UAAAA,EAAYQ,gBAAgBR,UAAU;AACtCU,QAAAA;AACF,KAAA;AACF;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/upload",
|
|
3
|
-
"version": "5.33.
|
|
3
|
+
"version": "5.33.4",
|
|
4
4
|
"description": "Makes it easy to upload images and files to your Strapi Application.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": {
|
|
@@ -62,10 +62,10 @@
|
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@mux/mux-player-react": "3.1.0",
|
|
64
64
|
"@reduxjs/toolkit": "1.9.7",
|
|
65
|
-
"@strapi/design-system": "2.
|
|
66
|
-
"@strapi/icons": "2.
|
|
67
|
-
"@strapi/provider-upload-local": "5.33.
|
|
68
|
-
"@strapi/utils": "5.33.
|
|
65
|
+
"@strapi/design-system": "2.1.2",
|
|
66
|
+
"@strapi/icons": "2.1.2",
|
|
67
|
+
"@strapi/provider-upload-local": "5.33.4",
|
|
68
|
+
"@strapi/utils": "5.33.4",
|
|
69
69
|
"byte-size": "8.1.1",
|
|
70
70
|
"cropperjs": "1.6.1",
|
|
71
71
|
"date-fns": "2.30.0",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"lodash": "4.17.21",
|
|
79
79
|
"mime-types": "2.1.35",
|
|
80
80
|
"prop-types": "^15.8.1",
|
|
81
|
-
"qs": "6.
|
|
81
|
+
"qs": "6.14.1",
|
|
82
82
|
"react-dnd": "16.0.1",
|
|
83
83
|
"react-intl": "6.6.2",
|
|
84
84
|
"react-query": "3.39.3",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"zod": "3.25.67"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
|
-
"@strapi/admin": "5.33.
|
|
93
|
-
"@strapi/types": "5.33.
|
|
92
|
+
"@strapi/admin": "5.33.4",
|
|
93
|
+
"@strapi/types": "5.33.4",
|
|
94
94
|
"@testing-library/dom": "10.4.1",
|
|
95
95
|
"@testing-library/react": "16.3.0",
|
|
96
96
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"@types/koa-range": "0.3.5",
|
|
101
101
|
"@types/koa-static": "4.0.2",
|
|
102
102
|
"formidable": "3.5.4",
|
|
103
|
-
"koa": "2.16.
|
|
103
|
+
"koa": "2.16.3",
|
|
104
104
|
"koa-body": "6.0.1",
|
|
105
105
|
"msw": "1.3.0",
|
|
106
106
|
"react": "18.3.1",
|