@strapi/upload 5.27.0 → 5.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/ai/components/AIUploadModal.js +8 -0
- package/dist/admin/ai/components/AIUploadModal.js.map +1 -1
- package/dist/admin/ai/components/AIUploadModal.mjs +9 -1
- package/dist/admin/ai/components/AIUploadModal.mjs.map +1 -1
- package/dist/admin/components/Breadcrumbs/CrumbSimpleMenuAsync.js +3 -3
- package/dist/admin/components/Breadcrumbs/CrumbSimpleMenuAsync.js.map +1 -1
- package/dist/admin/components/Breadcrumbs/CrumbSimpleMenuAsync.mjs +4 -4
- package/dist/admin/components/Breadcrumbs/CrumbSimpleMenuAsync.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssets.js +2 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssets.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssets.mjs +2 -1
- package/dist/admin/components/MediaLibraryInput/Carousel/CarouselAssets.mjs.map +1 -1
- package/dist/admin/components/SelectTree/utils/flattenTree.js +11 -6
- package/dist/admin/components/SelectTree/utils/flattenTree.js.map +1 -1
- package/dist/admin/components/SelectTree/utils/flattenTree.mjs +11 -6
- package/dist/admin/components/SelectTree/utils/flattenTree.mjs.map +1 -1
- package/dist/admin/package.json.js +5 -5
- package/dist/admin/package.json.mjs +5 -5
- package/dist/admin/pages/App/ConfigureTheView/ConfigureTheView.js +0 -1
- package/dist/admin/pages/App/ConfigureTheView/ConfigureTheView.js.map +1 -1
- package/dist/admin/pages/App/ConfigureTheView/ConfigureTheView.mjs +0 -1
- package/dist/admin/pages/App/ConfigureTheView/ConfigureTheView.mjs.map +1 -1
- package/dist/admin/pages/App/MediaLibrary/MediaLibrary.js +8 -1
- package/dist/admin/pages/App/MediaLibrary/MediaLibrary.js.map +1 -1
- package/dist/admin/pages/App/MediaLibrary/MediaLibrary.mjs +8 -1
- package/dist/admin/pages/App/MediaLibrary/MediaLibrary.mjs.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.js +9 -4
- package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs +9 -4
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
- package/dist/admin/src/components/SelectTree/utils/flattenTree.d.ts +3 -1
- package/dist/admin/utils/getFolderParents.js +2 -1
- package/dist/admin/utils/getFolderParents.js.map +1 -1
- package/dist/admin/utils/getFolderParents.mjs +2 -1
- package/dist/admin/utils/getFolderParents.mjs.map +1 -1
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-upload.js +11 -1
- package/dist/server/controllers/admin-upload.js.map +1 -1
- package/dist/server/controllers/admin-upload.mjs +11 -1
- package/dist/server/controllers/admin-upload.mjs.map +1 -1
- package/dist/server/services/weekly-metrics.js +5 -1
- package/dist/server/services/weekly-metrics.js.map +1 -1
- package/dist/server/services/weekly-metrics.mjs +5 -1
- package/dist/server/services/weekly-metrics.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +1 -0
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +1 -0
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/weekly-metrics.d.ts +1 -0
- package/dist/server/src/services/weekly-metrics.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.js","sources":["../../server/src/bootstrap.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { getService } from './utils';\nimport { ALLOWED_SORT_STRINGS, ALLOWED_WEBHOOK_EVENTS } from './constants';\n\nexport async function bootstrap({ strapi }: { strapi: Core.Strapi }) {\n const defaultConfig = {\n settings: {\n sizeOptimization: true,\n responsiveDimensions: true,\n autoOrientation: false,\n aiMetadata: true,\n },\n view_configuration: {\n pageSize: 10,\n sort: ALLOWED_SORT_STRINGS[0],\n },\n };\n\n for (const [key, defaultValue] of Object.entries(defaultConfig)) {\n // set plugin store\n const configurator = strapi.store!({ type: 'plugin', name: 'upload', key });\n\n const config = await configurator.get({});\n if (\n config &&\n Object.keys(defaultValue).every((key) => Object.prototype.hasOwnProperty.call(config, key))\n ) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // if the config does not exist or does not have all the required keys\n // set from the defaultValue ensuring all required settings are present\n await configurator.set({\n value: Object.assign(defaultValue, config || {}),\n });\n }\n\n await registerPermissionActions();\n await registerWebhookEvents();\n\n await getService('weeklyMetrics').registerCron();\n getService('metrics').sendUploadPluginMetrics();\n\n getService('extensions').signFileUrlsOnDocumentService();\n}\n\nconst registerWebhookEvents = async () =>\n Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {\n strapi.get('webhookStore').addAllowedEvent(key, value);\n });\n\nconst registerPermissionActions = async () => {\n const actions = [\n {\n section: 'plugins',\n displayName: 'Access the Media Library',\n uid: 'read',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Create (upload)',\n uid: 'assets.create',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Update (crop, details, replace) + delete',\n uid: 'assets.update',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Download',\n uid: 'assets.download',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Copy link',\n uid: 'assets.copy-link',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Configure view',\n uid: 'configure-view',\n pluginName: 'upload',\n },\n {\n section: 'settings',\n displayName: 'Access the Media Library settings page',\n uid: 'settings.read',\n category: 'media library',\n pluginName: 'upload',\n },\n ];\n\n await strapi.service('admin::permission').actionProvider.registerMany(actions);\n};\n"],"names":["bootstrap","strapi","defaultConfig","settings","sizeOptimization","responsiveDimensions","autoOrientation","aiMetadata","view_configuration","pageSize","sort","ALLOWED_SORT_STRINGS","key","defaultValue","Object","entries","configurator","store","type","name","config","get","keys","every","prototype","hasOwnProperty","call","set","value","assign","registerPermissionActions","registerWebhookEvents","getService","registerCron","sendUploadPluginMetrics","signFileUrlsOnDocumentService","ALLOWED_WEBHOOK_EVENTS","forEach","addAllowedEvent","actions","section","displayName","uid","pluginName","subCategory","category","service","actionProvider","registerMany"],"mappings":";;;;;AAKO,eAAeA,SAAAA,CAAU,EAAEC,MAAAA,EAAAA,OAAM,EAA2B,EAAA;AACjE,IAAA,MAAMC,aAAgB,GAAA;QACpBC,QAAU,EAAA;YACRC,gBAAkB,EAAA,IAAA;YAClBC,oBAAsB,EAAA,IAAA;YACtBC,eAAiB,EAAA,KAAA;YACjBC,UAAY,EAAA;AACd,SAAA;QACAC,kBAAoB,EAAA;YAClBC,QAAU,EAAA,EAAA;YACVC,IAAMC,EAAAA,8BAAoB,CAAC,CAAE;AAC/B;AACF,KAAA;IAEA,KAAK,MAAM,CAACC,GAAKC,EAAAA,YAAAA,CAAa,IAAIC,MAAOC,CAAAA,OAAO,CAACb,aAAgB,CAAA,CAAA;;QAE/D,MAAMc,YAAAA,GAAef,OAAOgB,CAAAA,KAAK,CAAE;YAAEC,IAAM,EAAA,QAAA;YAAUC,IAAM,EAAA,QAAA;AAAUP,YAAAA;AAAI,SAAA,CAAA;AAEzE,QAAA,MAAMQ,MAAS,GAAA,MAAMJ,YAAaK,CAAAA,GAAG,CAAC,EAAC,CAAA;AACvC,QAAA,IACED,UACAN,MAAOQ,CAAAA,IAAI,CAACT,YAAAA,CAAAA,CAAcU,KAAK,CAAC,CAACX,GAAQE,GAAAA,MAAAA,CAAOU,SAAS,CAACC,cAAc,CAACC,IAAI,CAACN,QAAQR,GACtF,CAAA,CAAA,EAAA;AAEA,YAAA;AACF;;;QAIA,MAAMI,YAAAA,CAAaW,GAAG,CAAC;AACrBC,YAAAA,KAAAA,EAAOd,MAAOe,CAAAA,MAAM,CAAChB,YAAAA,EAAcO,UAAU,EAAC;AAChD,SAAA,CAAA;AACF;IAEA,MAAMU,yBAAAA,EAAAA;IACN,MAAMC,qBAAAA,EAAAA;IAEN,MAAMC,gBAAAA,CAAW,iBAAiBC,YAAY,EAAA;
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sources":["../../server/src/bootstrap.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { getService } from './utils';\nimport { ALLOWED_SORT_STRINGS, ALLOWED_WEBHOOK_EVENTS } from './constants';\n\nexport async function bootstrap({ strapi }: { strapi: Core.Strapi }) {\n const defaultConfig = {\n settings: {\n sizeOptimization: true,\n responsiveDimensions: true,\n autoOrientation: false,\n aiMetadata: true,\n },\n view_configuration: {\n pageSize: 10,\n sort: ALLOWED_SORT_STRINGS[0],\n },\n };\n\n for (const [key, defaultValue] of Object.entries(defaultConfig)) {\n // set plugin store\n const configurator = strapi.store!({ type: 'plugin', name: 'upload', key });\n\n const config = await configurator.get({});\n if (\n config &&\n Object.keys(defaultValue).every((key) => Object.prototype.hasOwnProperty.call(config, key))\n ) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // if the config does not exist or does not have all the required keys\n // set from the defaultValue ensuring all required settings are present\n await configurator.set({\n value: Object.assign(defaultValue, config || {}),\n });\n }\n\n await registerPermissionActions();\n await registerWebhookEvents();\n\n await getService('weeklyMetrics').registerCron();\n\n getService('metrics').sendUploadPluginMetrics();\n\n getService('extensions').signFileUrlsOnDocumentService();\n}\n\nconst registerWebhookEvents = async () =>\n Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {\n strapi.get('webhookStore').addAllowedEvent(key, value);\n });\n\nconst registerPermissionActions = async () => {\n const actions = [\n {\n section: 'plugins',\n displayName: 'Access the Media Library',\n uid: 'read',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Create (upload)',\n uid: 'assets.create',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Update (crop, details, replace) + delete',\n uid: 'assets.update',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Download',\n uid: 'assets.download',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Copy link',\n uid: 'assets.copy-link',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Configure view',\n uid: 'configure-view',\n pluginName: 'upload',\n },\n {\n section: 'settings',\n displayName: 'Access the Media Library settings page',\n uid: 'settings.read',\n category: 'media library',\n pluginName: 'upload',\n },\n ];\n\n await strapi.service('admin::permission').actionProvider.registerMany(actions);\n};\n"],"names":["bootstrap","strapi","defaultConfig","settings","sizeOptimization","responsiveDimensions","autoOrientation","aiMetadata","view_configuration","pageSize","sort","ALLOWED_SORT_STRINGS","key","defaultValue","Object","entries","configurator","store","type","name","config","get","keys","every","prototype","hasOwnProperty","call","set","value","assign","registerPermissionActions","registerWebhookEvents","getService","registerCron","sendUploadPluginMetrics","signFileUrlsOnDocumentService","ALLOWED_WEBHOOK_EVENTS","forEach","addAllowedEvent","actions","section","displayName","uid","pluginName","subCategory","category","service","actionProvider","registerMany"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.mjs","sources":["../../server/src/bootstrap.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { getService } from './utils';\nimport { ALLOWED_SORT_STRINGS, ALLOWED_WEBHOOK_EVENTS } from './constants';\n\nexport async function bootstrap({ strapi }: { strapi: Core.Strapi }) {\n const defaultConfig = {\n settings: {\n sizeOptimization: true,\n responsiveDimensions: true,\n autoOrientation: false,\n aiMetadata: true,\n },\n view_configuration: {\n pageSize: 10,\n sort: ALLOWED_SORT_STRINGS[0],\n },\n };\n\n for (const [key, defaultValue] of Object.entries(defaultConfig)) {\n // set plugin store\n const configurator = strapi.store!({ type: 'plugin', name: 'upload', key });\n\n const config = await configurator.get({});\n if (\n config &&\n Object.keys(defaultValue).every((key) => Object.prototype.hasOwnProperty.call(config, key))\n ) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // if the config does not exist or does not have all the required keys\n // set from the defaultValue ensuring all required settings are present\n await configurator.set({\n value: Object.assign(defaultValue, config || {}),\n });\n }\n\n await registerPermissionActions();\n await registerWebhookEvents();\n\n await getService('weeklyMetrics').registerCron();\n getService('metrics').sendUploadPluginMetrics();\n\n getService('extensions').signFileUrlsOnDocumentService();\n}\n\nconst registerWebhookEvents = async () =>\n Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {\n strapi.get('webhookStore').addAllowedEvent(key, value);\n });\n\nconst registerPermissionActions = async () => {\n const actions = [\n {\n section: 'plugins',\n displayName: 'Access the Media Library',\n uid: 'read',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Create (upload)',\n uid: 'assets.create',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Update (crop, details, replace) + delete',\n uid: 'assets.update',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Download',\n uid: 'assets.download',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Copy link',\n uid: 'assets.copy-link',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Configure view',\n uid: 'configure-view',\n pluginName: 'upload',\n },\n {\n section: 'settings',\n displayName: 'Access the Media Library settings page',\n uid: 'settings.read',\n category: 'media library',\n pluginName: 'upload',\n },\n ];\n\n await strapi.service('admin::permission').actionProvider.registerMany(actions);\n};\n"],"names":["bootstrap","strapi","defaultConfig","settings","sizeOptimization","responsiveDimensions","autoOrientation","aiMetadata","view_configuration","pageSize","sort","ALLOWED_SORT_STRINGS","key","defaultValue","Object","entries","configurator","store","type","name","config","get","keys","every","prototype","hasOwnProperty","call","set","value","assign","registerPermissionActions","registerWebhookEvents","getService","registerCron","sendUploadPluginMetrics","signFileUrlsOnDocumentService","ALLOWED_WEBHOOK_EVENTS","forEach","addAllowedEvent","actions","section","displayName","uid","pluginName","subCategory","category","service","actionProvider","registerMany"],"mappings":";;;AAKO,eAAeA,SAAAA,CAAU,EAAEC,MAAAA,EAAAA,OAAM,EAA2B,EAAA;AACjE,IAAA,MAAMC,aAAgB,GAAA;QACpBC,QAAU,EAAA;YACRC,gBAAkB,EAAA,IAAA;YAClBC,oBAAsB,EAAA,IAAA;YACtBC,eAAiB,EAAA,KAAA;YACjBC,UAAY,EAAA;AACd,SAAA;QACAC,kBAAoB,EAAA;YAClBC,QAAU,EAAA,EAAA;YACVC,IAAMC,EAAAA,oBAAoB,CAAC,CAAE;AAC/B;AACF,KAAA;IAEA,KAAK,MAAM,CAACC,GAAKC,EAAAA,YAAAA,CAAa,IAAIC,MAAOC,CAAAA,OAAO,CAACb,aAAgB,CAAA,CAAA;;QAE/D,MAAMc,YAAAA,GAAef,OAAOgB,CAAAA,KAAK,CAAE;YAAEC,IAAM,EAAA,QAAA;YAAUC,IAAM,EAAA,QAAA;AAAUP,YAAAA;AAAI,SAAA,CAAA;AAEzE,QAAA,MAAMQ,MAAS,GAAA,MAAMJ,YAAaK,CAAAA,GAAG,CAAC,EAAC,CAAA;AACvC,QAAA,IACED,UACAN,MAAOQ,CAAAA,IAAI,CAACT,YAAAA,CAAAA,CAAcU,KAAK,CAAC,CAACX,GAAQE,GAAAA,MAAAA,CAAOU,SAAS,CAACC,cAAc,CAACC,IAAI,CAACN,QAAQR,GACtF,CAAA,CAAA,EAAA;AAEA,YAAA;AACF;;;QAIA,MAAMI,YAAAA,CAAaW,GAAG,CAAC;AACrBC,YAAAA,KAAAA,EAAOd,MAAOe,CAAAA,MAAM,CAAChB,YAAAA,EAAcO,UAAU,EAAC;AAChD,SAAA,CAAA;AACF;IAEA,MAAMU,yBAAAA,EAAAA;IACN,MAAMC,qBAAAA,EAAAA;IAEN,MAAMC,UAAAA,CAAW,iBAAiBC,YAAY,EAAA;
|
|
1
|
+
{"version":3,"file":"bootstrap.mjs","sources":["../../server/src/bootstrap.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { getService } from './utils';\nimport { ALLOWED_SORT_STRINGS, ALLOWED_WEBHOOK_EVENTS } from './constants';\n\nexport async function bootstrap({ strapi }: { strapi: Core.Strapi }) {\n const defaultConfig = {\n settings: {\n sizeOptimization: true,\n responsiveDimensions: true,\n autoOrientation: false,\n aiMetadata: true,\n },\n view_configuration: {\n pageSize: 10,\n sort: ALLOWED_SORT_STRINGS[0],\n },\n };\n\n for (const [key, defaultValue] of Object.entries(defaultConfig)) {\n // set plugin store\n const configurator = strapi.store!({ type: 'plugin', name: 'upload', key });\n\n const config = await configurator.get({});\n if (\n config &&\n Object.keys(defaultValue).every((key) => Object.prototype.hasOwnProperty.call(config, key))\n ) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n // if the config does not exist or does not have all the required keys\n // set from the defaultValue ensuring all required settings are present\n await configurator.set({\n value: Object.assign(defaultValue, config || {}),\n });\n }\n\n await registerPermissionActions();\n await registerWebhookEvents();\n\n await getService('weeklyMetrics').registerCron();\n\n getService('metrics').sendUploadPluginMetrics();\n\n getService('extensions').signFileUrlsOnDocumentService();\n}\n\nconst registerWebhookEvents = async () =>\n Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {\n strapi.get('webhookStore').addAllowedEvent(key, value);\n });\n\nconst registerPermissionActions = async () => {\n const actions = [\n {\n section: 'plugins',\n displayName: 'Access the Media Library',\n uid: 'read',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Create (upload)',\n uid: 'assets.create',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Update (crop, details, replace) + delete',\n uid: 'assets.update',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Download',\n uid: 'assets.download',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Copy link',\n uid: 'assets.copy-link',\n subCategory: 'assets',\n pluginName: 'upload',\n },\n {\n section: 'plugins',\n displayName: 'Configure view',\n uid: 'configure-view',\n pluginName: 'upload',\n },\n {\n section: 'settings',\n displayName: 'Access the Media Library settings page',\n uid: 'settings.read',\n category: 'media library',\n pluginName: 'upload',\n },\n ];\n\n await strapi.service('admin::permission').actionProvider.registerMany(actions);\n};\n"],"names":["bootstrap","strapi","defaultConfig","settings","sizeOptimization","responsiveDimensions","autoOrientation","aiMetadata","view_configuration","pageSize","sort","ALLOWED_SORT_STRINGS","key","defaultValue","Object","entries","configurator","store","type","name","config","get","keys","every","prototype","hasOwnProperty","call","set","value","assign","registerPermissionActions","registerWebhookEvents","getService","registerCron","sendUploadPluginMetrics","signFileUrlsOnDocumentService","ALLOWED_WEBHOOK_EVENTS","forEach","addAllowedEvent","actions","section","displayName","uid","pluginName","subCategory","category","service","actionProvider","registerMany"],"mappings":"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"}
|
|
@@ -73,9 +73,16 @@ var adminUpload = {
|
|
|
73
73
|
return ctx.forbidden();
|
|
74
74
|
}
|
|
75
75
|
const data = await upload.validateUploadBody(body, Array.isArray(files));
|
|
76
|
-
|
|
76
|
+
let filesArray = Array.isArray(files) ? files : [
|
|
77
77
|
files
|
|
78
78
|
];
|
|
79
|
+
if (data.fileInfo && Array.isArray(data.fileInfo) && filesArray.length === data.fileInfo.length) {
|
|
80
|
+
// Reorder filesArray to match data.fileInfo order
|
|
81
|
+
const alignedFilesArray = data.fileInfo.map((info)=>{
|
|
82
|
+
return filesArray.find((file)=>file.originalFilename === info.name);
|
|
83
|
+
}).filter(Boolean);
|
|
84
|
+
filesArray = alignedFilesArray;
|
|
85
|
+
}
|
|
79
86
|
// Upload files first to get thumbnails
|
|
80
87
|
const uploadedFiles = await uploadService.upload({
|
|
81
88
|
data,
|
|
@@ -83,6 +90,9 @@ var adminUpload = {
|
|
|
83
90
|
}, {
|
|
84
91
|
user
|
|
85
92
|
});
|
|
93
|
+
if (uploadedFiles.some((file)=>file.mime?.startsWith('image/'))) {
|
|
94
|
+
strapi.telemetry.send('didUploadImage');
|
|
95
|
+
}
|
|
86
96
|
const aiMetadataService = index.getService('aiMetadata');
|
|
87
97
|
// AFTER upload - use thumbnail versions for AI processing
|
|
88
98
|
if (await aiMetadataService.isEnabled()) {
|
|
@@ -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';\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 data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: files }, { 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 data = await validateUploadBody(body, Array.isArray(files));\n const filesArray = Array.isArray(files) ? files : [files];\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\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","replacedFile","replace","signedFile","signFileUrls","uploadFiles","strapi","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","filesArray","uploadedFiles","upload","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","mime","originalFilename","name","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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"}
|
|
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';\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 data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: files }, { 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 data = await validateUploadBody(body, Array.isArray(files));\n\n let filesArray = Array.isArray(files) ? files : [files];\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 strapi.telemetry.send('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","replacedFile","replace","signedFile","signFileUrls","uploadFiles","strapi","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","filesArray","length","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","mime","startsWith","telemetry","send","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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"}
|
|
@@ -71,9 +71,16 @@ var adminUpload = {
|
|
|
71
71
|
return ctx.forbidden();
|
|
72
72
|
}
|
|
73
73
|
const data = await validateUploadBody(body, Array.isArray(files));
|
|
74
|
-
|
|
74
|
+
let filesArray = Array.isArray(files) ? files : [
|
|
75
75
|
files
|
|
76
76
|
];
|
|
77
|
+
if (data.fileInfo && Array.isArray(data.fileInfo) && filesArray.length === data.fileInfo.length) {
|
|
78
|
+
// Reorder filesArray to match data.fileInfo order
|
|
79
|
+
const alignedFilesArray = data.fileInfo.map((info)=>{
|
|
80
|
+
return filesArray.find((file)=>file.originalFilename === info.name);
|
|
81
|
+
}).filter(Boolean);
|
|
82
|
+
filesArray = alignedFilesArray;
|
|
83
|
+
}
|
|
77
84
|
// Upload files first to get thumbnails
|
|
78
85
|
const uploadedFiles = await uploadService.upload({
|
|
79
86
|
data,
|
|
@@ -81,6 +88,9 @@ var adminUpload = {
|
|
|
81
88
|
}, {
|
|
82
89
|
user
|
|
83
90
|
});
|
|
91
|
+
if (uploadedFiles.some((file)=>file.mime?.startsWith('image/'))) {
|
|
92
|
+
strapi.telemetry.send('didUploadImage');
|
|
93
|
+
}
|
|
84
94
|
const aiMetadataService = getService('aiMetadata');
|
|
85
95
|
// AFTER upload - use thumbnail versions for AI processing
|
|
86
96
|
if (await aiMetadataService.isEnabled()) {
|
|
@@ -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';\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 data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: files }, { 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 data = await validateUploadBody(body, Array.isArray(files));\n const filesArray = Array.isArray(files) ? files : [files];\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\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","replacedFile","replace","signedFile","signFileUrls","uploadFiles","strapi","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","filesArray","uploadedFiles","upload","aiMetadataService","isEnabled","thumbnailFiles","filepath","formats","thumbnail","url","mimetype","mime","originalFilename","name","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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"}
|
|
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';\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 data = (await validateUploadBody(body)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: files }, { 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 data = await validateUploadBody(body, Array.isArray(files));\n\n let filesArray = Array.isArray(files) ? files : [files];\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 strapi.telemetry.send('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","replacedFile","replace","signedFile","signFileUrls","uploadFiles","strapi","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","filesArray","length","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","mime","startsWith","telemetry","send","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":";;;;;;;AAWA,kBAAe;AACb,IAAA,MAAMA,oBAAmBC,GAAY,EAAA;AACnC,QAAA,MAAM,EACJC,KAAAA,EAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BC,OAAS,EAAA,EAAEC,IAAI,EAAE,EAClB,GAAGL,GAAAA;AAEJ,QAAA,MAAM,EAAEM,OAAO,EAAE,GAAG,MAAMC,sBAAuBF,CAAAA,IAAAA,CAAAA;AACjD,QAAA,MAAMG,gBAAgBC,UAAW,CAAA,QAAA,CAAA;QAEjC,MAAMC,OAAAA,GAAU,MAAMC,KAAAA,CAAMC,GAAG,CAC7BN,OACA,EAAA,OAAO,EAAEO,EAAE,EAAEC,QAAQ,EAAsC,GAAA;YACzD,MAAM,EAAEC,EAAE,EAAE,GAAG,MAAMC,8BACnBd,WACAe,EAAAA,OAAAA,CAAQC,MAAM,EACdC,cACAN,EAAAA,EAAAA,CAAAA;AAGF,YAAA,MAAMO,UAAU,MAAMZ,aAAAA,CAAca,cAAc,CAACR,IAAIC,QAAiB,EAAA;AAAEX,gBAAAA;AAAK,aAAA,CAAA;YAC/E,OAAOY,EAAAA,CAAGO,cAAc,CAACF,OAAS,EAAA;AAAEG,gBAAAA,MAAAA,EAAQN,QAAQO;AAAK,aAAA,CAAA;AAC3D,SAAA,CAAA;AAGFxB,QAAAA,GAAAA,CAAIK,IAAI,GAAGK,OAAAA;AACb,KAAA;AAEA,IAAA,MAAMW,gBAAerB,GAAY,EAAA;AAC/B,QAAA,MAAM,EACJC,KAAO,EAAA,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BsB,KAAAA,EAAO,EAAEZ,EAAE,EAAE,EACbT,OAAAA,EAAS,EAAEC,IAAI,EAAE,EAClB,GAAGL,GAAAA;QAEJ,IAAI,OAAOa,OAAO,QAAU,EAAA;YAC1B,MAAM,IAAIa,MAAOC,CAAAA,eAAe,CAAC,qBAAA,CAAA;AACnC;AAEA,QAAA,MAAMnB,gBAAgBC,UAAW,CAAA,QAAA,CAAA;QACjC,MAAM,EAAEM,EAAE,EAAE,GAAG,MAAMC,8BACnBd,WACAe,EAAAA,OAAAA,CAAQC,MAAM,EACdC,cACAN,EAAAA,EAAAA,CAAAA;QAGF,MAAMe,IAAAA,GAAO,MAAMC,kBAAmBxB,CAAAA,IAAAA,CAAAA;QAEtC,MAAMyB,IAAAA,GAAO,MAAMtB,aAAca,CAAAA,cAAc,CAACR,EAAIe,EAAAA,IAAAA,CAAKd,QAAQ,EAAS;AAAEX,YAAAA;AAAK,SAAA,CAAA;AAEjFH,QAAAA,GAAAA,CAAIK,IAAI,GAAG,MAAMU,EAAGO,CAAAA,cAAc,CAACQ,IAAM,EAAA;AAAEP,YAAAA,MAAAA,EAAQN,QAAQO;AAAK,SAAA,CAAA;AAClE,KAAA;AAEA,IAAA,MAAMO,aAAY/B,GAAY,EAAA;QAC5B,MAAM,EACJC,KAAO,EAAA,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BsB,KAAAA,EAAO,EAAEZ,EAAE,EAAE,EACbT,SAAS,EAAEC,IAAI,EAAE2B,KAAAA,EAAO,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACzC,GAAGhC,GAAAA;QAEJ,IAAI,OAAOa,OAAO,QAAU,EAAA;YAC1B,MAAM,IAAIa,MAAOC,CAAAA,eAAe,CAAC,qBAAA,CAAA;AACnC;AAEA,QAAA,MAAMnB,gBAAgBC,UAAW,CAAA,QAAA,CAAA;QACjC,MAAM,EAAEM,EAAE,EAAE,GAAG,MAAMC,8BACnBd,WACAe,EAAAA,OAAAA,CAAQC,MAAM,EACdC,cACAN,EAAAA,EAAAA,CAAAA;QAGF,IAAIoB,KAAAA,CAAMC,OAAO,CAACF,KAAQ,CAAA,EAAA;YACxB,MAAM,IAAIN,MAAOS,CAAAA,gBAAgB,CAAC,0CAAA,CAAA;AACpC;QAEA,MAAMP,IAAAA,GAAQ,MAAMC,kBAAmBxB,CAAAA,IAAAA,CAAAA;AACvC,QAAA,MAAM+B,YAAe,GAAA,MAAM5B,aAAc6B,CAAAA,OAAO,CAACxB,EAAI,EAAA;AAAEe,YAAAA,IAAAA;YAAME,IAAME,EAAAA;SAAS,EAAA;AAAE7B,YAAAA;AAAK,SAAA,CAAA;;AAGnF,QAAA,MAAMmC,UAAa,GAAA,MAAM7B,UAAW,CAAA,MAAA,CAAA,CAAQ8B,YAAY,CAACH,YAAAA,CAAAA;AAEzDpC,QAAAA,GAAAA,CAAIK,IAAI,GAAG,MAAMU,EAAGO,CAAAA,cAAc,CAACgB,UAAY,EAAA;AAAEf,YAAAA,MAAAA,EAAQN,QAAQO;AAAK,SAAA,CAAA;AACxE,KAAA;AAEA,IAAA,MAAMgB,aAAYxC,GAAY,EAAA;QAC5B,MAAM,EACJC,OAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BC,OAAS,EAAA,EAAEC,IAAI,EAAE2B,KAAAA,EAAO,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACzC,GAAGhC,GAAAA;AAEJ,QAAA,MAAMQ,gBAAgBC,UAAW,CAAA,QAAA,CAAA;AACjC,QAAA,MAAMM,KAAK0B,MAAOC,CAAAA,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAS1C,EAAAA,WAAAA;AACTqB,YAAAA,MAAAA,EAAQN,QAAQ4B,MAAM;YACtBC,KAAO3B,EAAAA;AACT,SAAA,CAAA;QAEA,IAAI,CAACJ,EAAGgC,CAAAA,SAAS,EAAE;AACjB,YAAA,OAAO/C,IAAIgD,SAAS,EAAA;AACtB;AAEA,QAAA,MAAMpB,OAAO,MAAMC,kBAAAA,CAAmBxB,IAAM4B,EAAAA,KAAAA,CAAMC,OAAO,CAACF,KAAAA,CAAAA,CAAAA;AAE1D,QAAA,IAAIiB,UAAahB,GAAAA,KAAAA,CAAMC,OAAO,CAACF,SAASA,KAAQ,GAAA;AAACA,YAAAA;AAAM,SAAA;AAEvD,QAAA,IACEJ,KAAKd,QAAQ,IACbmB,KAAMC,CAAAA,OAAO,CAACN,IAAKd,CAAAA,QAAQ,CAC3BmC,IAAAA,UAAAA,CAAWC,MAAM,KAAKtB,IAAAA,CAAKd,QAAQ,CAACoC,MAAM,EAC1C;;AAEA,YAAA,MAAMC,oBAAoBvB,IAAKd,CAAAA,QAAQ,CACpCF,GAAG,CAAC,CAACwC,IAAAA,GAAAA;gBACJ,OAAOH,UAAAA,CAAWI,IAAI,CAAC,CAACvB,OAASA,IAAKwB,CAAAA,gBAAgB,KAAKF,IAAAA,CAAKG,IAAI,CAAA;AACtE,aAAA,CAAA,CACCC,MAAM,CAACC,OAAAA,CAAAA;YAEVR,UAAaE,GAAAA,iBAAAA;AACf;;AAGA,QAAA,MAAMO,aAAgB,GAAA,MAAMlD,aAAcmD,CAAAA,MAAM,CAAC;AAAE/B,YAAAA,IAAAA;YAAMI,KAAOiB,EAAAA;SAAc,EAAA;AAAE9C,YAAAA;AAAK,SAAA,CAAA;QACrF,IAAIuD,aAAAA,CAAcE,IAAI,CAAC,CAAC9B,OAASA,IAAK+B,CAAAA,IAAI,EAAEC,UAAAA,CAAW,QAAY,CAAA,CAAA,EAAA;YACjErB,MAAOsB,CAAAA,SAAS,CAACC,IAAI,CAAC,gBAAA,CAAA;AACxB;AAEA,QAAA,MAAMC,oBAAoBxD,UAAW,CAAA,YAAA,CAAA;;QAGrC,IAAI,MAAMwD,iBAAkBC,CAAAA,SAAS,EAAI,EAAA;YACvC,IAAI;;AAEF,gBAAA,MAAMC,iBAAiBT,aAAc9C,CAAAA,GAAG,CACtC,CAACkB,QACE;AACCsC,wBAAAA,QAAAA,EAAUtC,KAAKuC,OAAO,EAAEC,SAAWC,EAAAA,GAAAA,IAAOzC,KAAKyC,GAAG;AAClDC,wBAAAA,QAAAA,EAAU1C,KAAK+B,IAAI;AACnBP,wBAAAA,gBAAAA,EAAkBxB,KAAKyB,IAAI;AAC3BkB,wBAAAA,IAAAA,EAAM3C,KAAKuC,OAAO,EAAEC,SAAWG,EAAAA,IAAAA,IAAQ3C,KAAK2C,IAAI;AAChDC,wBAAAA,QAAAA,EAAU5C,KAAK4C;qBACjB,CAAA,CAAA;AAGJ,gBAAA,MAAMC,eAAkB,GAAA,MAAMV,iBAAkBW,CAAAA,YAAY,CAACT,cAAAA,CAAAA;;AAG7D,gBAAA,MAAMU,QAAQC,GAAG,CACfpB,cAAc9C,GAAG,CAAC,OAAOmE,YAAcC,EAAAA,KAAAA,GAAAA;oBACrC,MAAMC,UAAAA,GAAaN,eAAe,CAACK,KAAM,CAAA;AACzC,oBAAA,IAAIC,UAAY,EAAA;AACd,wBAAA,MAAMzE,aAAca,CAAAA,cAAc,CAChC0D,YAAAA,CAAalE,EAAE,EACf;AACEqE,4BAAAA,eAAAA,EAAiBD,WAAWE,OAAO;AACnCC,4BAAAA,OAAAA,EAASH,WAAWG;yBAEtB,EAAA;AAAEjF,4BAAAA;AAAK,yBAAA,CAAA;AAGTuD,wBAAAA,aAAa,CAACsB,KAAM,CAAA,CAACE,eAAe,GAAGD,WAAWE,OAAO;AACzDzB,wBAAAA,aAAa,CAACsB,KAAM,CAAA,CAACI,OAAO,GAAGH,WAAWG,OAAO;AACnD;AACF,iBAAA,CAAA,CAAA;AAEJ,aAAA,CAAE,OAAOC,KAAO,EAAA;AACd5C,gBAAAA,MAAAA,CAAO6C,GAAG,CAACC,IAAI,CAAC,mEAAqE,EAAA;AACnFF,oBAAAA,KAAAA,EAAOA,KAAiBG,YAAAA,KAAAA,GAAQH,KAAMI,CAAAA,OAAO,GAAGC,MAAOL,CAAAA,KAAAA;AACzD,iBAAA,CAAA;AACF;AACF;;QAGA,MAAMM,WAAAA,GAAc,MAAMhF,KAAMC,CAAAA,GAAG,CAAC8C,aAAejD,EAAAA,UAAAA,CAAW,QAAQ8B,YAAY,CAAA;AAElFvC,QAAAA,GAAAA,CAAIK,IAAI,GAAG,MAAMU,EAAGO,CAAAA,cAAc,CAACqE,WAAa,EAAA;AAAEpE,YAAAA,MAAAA,EAAQN,QAAQO;AAAK,SAAA,CAAA;AACvExB,QAAAA,GAAAA,CAAI4F,MAAM,GAAG,GAAA;AACf,KAAA;;AAGA,IAAA,MAAMjC,QAAO3D,GAAY,EAAA;AACvB,QAAA,MAAM,EACJyB,KAAO,EAAA,EAAEZ,EAAE,EAAE,EACbT,OAAS,EAAA,EAAE4B,KAAO,EAAA,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACnC,GAAGhC,GAAAA;AAEJ,QAAA,IAAI6F,CAAEC,CAAAA,OAAO,CAAC9D,KAAAA,CAAAA,IAAW,CAACC,KAAAA,CAAMC,OAAO,CAACF,KAAUA,CAAAA,IAAAA,KAAAA,CAAMyC,IAAI,KAAK,CAAI,EAAA;AACnE,YAAA,IAAI5D,EAAI,EAAA;gBACN,OAAO,IAAI,CAACQ,cAAc,CAACrB,GAAAA,CAAAA;AAC7B;YAEA,MAAM,IAAI0B,MAAOS,CAAAA,gBAAgB,CAAC,iBAAA,CAAA;AACpC;QAEA,MAAOtB,CAAAA,EAAAA,GAAK,IAAI,CAACkB,WAAW,GAAG,IAAI,CAACS,WAAU,EAAGxC,GAAAA,CAAAA;AACnD;AACF,CAAE;;;;"}
|
|
@@ -74,12 +74,16 @@ var weeklyMetrics = (({ strapi: strapi1 })=>({
|
|
|
74
74
|
const averageDeviationDepth = folderNumber !== 0 ? sumOfDeviation / folderNumber : 0;
|
|
75
75
|
// File metrics
|
|
76
76
|
const assetNumber = await strapi1.db.query(constants.FILE_MODEL_UID).count();
|
|
77
|
+
// AI metadata generation metrics
|
|
78
|
+
const settings = await strapi1.plugin('upload').service('upload').getSettings();
|
|
79
|
+
const isAIMediaLibraryConfigured = settings?.aiMetadata;
|
|
77
80
|
return {
|
|
78
81
|
assetNumber,
|
|
79
82
|
folderNumber,
|
|
80
83
|
averageDepth,
|
|
81
84
|
maxDepth,
|
|
82
|
-
averageDeviationDepth
|
|
85
|
+
averageDeviationDepth,
|
|
86
|
+
isAIMediaLibraryConfigured
|
|
83
87
|
};
|
|
84
88
|
},
|
|
85
89
|
async sendMetrics () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weekly-metrics.js","sources":["../../../server/src/services/weekly-metrics.ts"],"sourcesContent":["import { defaultTo } from 'lodash/fp';\nimport { add } from 'date-fns';\n\nimport type { Core } from '@strapi/types';\n\nimport { getWeeklyCronScheduleAt } from '../utils/cron';\nimport { FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\n\ntype MetricStoreValue = {\n lastWeeklyUpdate?: number;\n weeklySchedule?: string;\n};\n\nconst ONE_WEEK = 7 * 24 * 60 * 60 * 1000;\n\nconst getMetricsStoreValue = async (): Promise<MetricStoreValue> => {\n const value = await strapi.store.get({ type: 'plugin', name: 'upload', key: 'metrics' });\n return defaultTo({}, value) as MetricStoreValue;\n};\nconst setMetricsStoreValue = (value: MetricStoreValue) =>\n strapi.store.set({ type: 'plugin', name: 'upload', key: 'metrics', value });\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async computeMetrics() {\n // Folder metrics\n // @ts-expect-error - no dynamic types for the metadata\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n\n let keepOnlySlashesSQLString = '??';\n const queryParams = [pathColName];\n for (let i = 0; i < 10; i += 1) {\n keepOnlySlashesSQLString = `REPLACE(${keepOnlySlashesSQLString}, ?, ?)`;\n queryParams.push(String(i), '');\n }\n\n /*\n The following query goal is to count the number of folders with depth 1, depth 2 etc.\n The query returns :\n [\n { depth: 1, occurence: 4 },\n { depth: 2, occurence: 2 },\n { depth: 3, occurence: 5 },\n ]\n\n The query is built as follow:\n 1. In order to get the depth level of a folder:\n - we take their path\n - remove all numbers (by replacing 0123456789 by '', thus the 10 REPLACE in the query)\n - count the remaining `/`, which correspond to their depth (by using LENGTH)\n We now have, for each folder, its depth.\n 2. In order to get the number of folders for each depth:\n - we group them by their depth and use COUNT(*)\n */\n\n const res = (await strapi.db\n .getConnection(folderTable)\n .select(\n strapi.db.connection.raw(\n `LENGTH(${keepOnlySlashesSQLString}) AS depth, COUNT(*) AS occurence`,\n queryParams\n )\n )\n .groupBy('depth')) as Array<{ depth: string; occurence: string }>;\n\n const folderLevelsArray = res.map((map) => ({\n depth: Number(map.depth),\n occurence: Number(map.occurence),\n })); // values can be strings depending on the database\n\n let product = 0;\n let folderNumber = 0;\n let maxDepth = 0;\n for (const folderLevel of folderLevelsArray) {\n product += folderLevel.depth * folderLevel.occurence;\n folderNumber += folderLevel.occurence;\n if (folderLevel.depth > maxDepth) {\n maxDepth = folderLevel.depth;\n }\n }\n const averageDepth = folderNumber !== 0 ? product / folderNumber : 0;\n\n let sumOfDeviation = 0;\n for (const folderLevel of folderLevelsArray) {\n sumOfDeviation += Math.abs(folderLevel.depth - averageDepth) * folderLevel.occurence;\n }\n\n const averageDeviationDepth = folderNumber !== 0 ? sumOfDeviation / folderNumber : 0;\n\n // File metrics\n const assetNumber = await strapi.db.query(FILE_MODEL_UID).count();\n\n return {\n assetNumber,\n folderNumber,\n averageDepth,\n maxDepth,\n averageDeviationDepth,\n };\n },\n\n async sendMetrics() {\n const metrics = await this.computeMetrics();\n strapi.telemetry.send('didSendUploadPropertiesOnceAWeek', {\n groupProperties: { metrics },\n });\n\n const metricsInfoStored = await getMetricsStoreValue();\n await setMetricsStoreValue({ ...metricsInfoStored, lastWeeklyUpdate: new Date().getTime() });\n },\n\n async ensureWeeklyStoredCronSchedule(): Promise<string> {\n const metricsInfoStored = await getMetricsStoreValue();\n const { weeklySchedule: currentSchedule, lastWeeklyUpdate } = metricsInfoStored;\n\n const now = new Date();\n let weeklySchedule = currentSchedule;\n\n if (!weeklySchedule || !lastWeeklyUpdate || lastWeeklyUpdate + ONE_WEEK < now.getTime()) {\n weeklySchedule = getWeeklyCronScheduleAt(add(now, { seconds: 15 }));\n await setMetricsStoreValue({ ...metricsInfoStored, weeklySchedule });\n\n return weeklySchedule;\n }\n\n return weeklySchedule;\n },\n\n async registerCron() {\n const weeklySchedule = await this.ensureWeeklyStoredCronSchedule();\n\n strapi.cron.add({\n uploadWeekly: {\n task: this.sendMetrics.bind(this),\n options: weeklySchedule,\n },\n });\n },\n});\n"],"names":["ONE_WEEK","getMetricsStoreValue","value","strapi","store","get","type","name","key","defaultTo","setMetricsStoreValue","set","computeMetrics","pathColName","db","metadata","FOLDER_MODEL_UID","attributes","path","columnName","folderTable","getModel","collectionName","keepOnlySlashesSQLString","queryParams","i","push","String","res","getConnection","select","connection","raw","groupBy","folderLevelsArray","map","depth","Number","occurence","product","folderNumber","maxDepth","folderLevel","averageDepth","sumOfDeviation","Math","abs","averageDeviationDepth","assetNumber","query","FILE_MODEL_UID","count","sendMetrics","metrics","telemetry","send","groupProperties","metricsInfoStored","lastWeeklyUpdate","Date","getTime","ensureWeeklyStoredCronSchedule","weeklySchedule","currentSchedule","now","getWeeklyCronScheduleAt","add","seconds","registerCron","cron","uploadWeekly","task","bind","options"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"weekly-metrics.js","sources":["../../../server/src/services/weekly-metrics.ts"],"sourcesContent":["import { defaultTo } from 'lodash/fp';\nimport { add } from 'date-fns';\n\nimport type { Core } from '@strapi/types';\n\nimport { getWeeklyCronScheduleAt } from '../utils/cron';\nimport { FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\nimport { Settings } from '../controllers/validation/admin/settings';\n\ntype MetricStoreValue = {\n lastWeeklyUpdate?: number;\n weeklySchedule?: string;\n};\n\nconst ONE_WEEK = 7 * 24 * 60 * 60 * 1000;\n\nconst getMetricsStoreValue = async (): Promise<MetricStoreValue> => {\n const value = await strapi.store.get({ type: 'plugin', name: 'upload', key: 'metrics' });\n return defaultTo({}, value) as MetricStoreValue;\n};\nconst setMetricsStoreValue = (value: MetricStoreValue) =>\n strapi.store.set({ type: 'plugin', name: 'upload', key: 'metrics', value });\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async computeMetrics() {\n // Folder metrics\n // @ts-expect-error - no dynamic types for the metadata\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n\n let keepOnlySlashesSQLString = '??';\n const queryParams = [pathColName];\n for (let i = 0; i < 10; i += 1) {\n keepOnlySlashesSQLString = `REPLACE(${keepOnlySlashesSQLString}, ?, ?)`;\n queryParams.push(String(i), '');\n }\n\n /*\n The following query goal is to count the number of folders with depth 1, depth 2 etc.\n The query returns :\n [\n { depth: 1, occurence: 4 },\n { depth: 2, occurence: 2 },\n { depth: 3, occurence: 5 },\n ]\n\n The query is built as follow:\n 1. In order to get the depth level of a folder:\n - we take their path\n - remove all numbers (by replacing 0123456789 by '', thus the 10 REPLACE in the query)\n - count the remaining `/`, which correspond to their depth (by using LENGTH)\n We now have, for each folder, its depth.\n 2. In order to get the number of folders for each depth:\n - we group them by their depth and use COUNT(*)\n */\n\n const res = (await strapi.db\n .getConnection(folderTable)\n .select(\n strapi.db.connection.raw(\n `LENGTH(${keepOnlySlashesSQLString}) AS depth, COUNT(*) AS occurence`,\n queryParams\n )\n )\n .groupBy('depth')) as Array<{ depth: string; occurence: string }>;\n\n const folderLevelsArray = res.map((map) => ({\n depth: Number(map.depth),\n occurence: Number(map.occurence),\n })); // values can be strings depending on the database\n\n let product = 0;\n let folderNumber = 0;\n let maxDepth = 0;\n for (const folderLevel of folderLevelsArray) {\n product += folderLevel.depth * folderLevel.occurence;\n folderNumber += folderLevel.occurence;\n if (folderLevel.depth > maxDepth) {\n maxDepth = folderLevel.depth;\n }\n }\n const averageDepth = folderNumber !== 0 ? product / folderNumber : 0;\n\n let sumOfDeviation = 0;\n for (const folderLevel of folderLevelsArray) {\n sumOfDeviation += Math.abs(folderLevel.depth - averageDepth) * folderLevel.occurence;\n }\n\n const averageDeviationDepth = folderNumber !== 0 ? sumOfDeviation / folderNumber : 0;\n\n // File metrics\n const assetNumber = await strapi.db.query(FILE_MODEL_UID).count();\n\n // AI metadata generation metrics\n const settings: Settings = await strapi.plugin('upload').service('upload').getSettings();\n const isAIMediaLibraryConfigured = settings?.aiMetadata;\n\n return {\n assetNumber,\n folderNumber,\n averageDepth,\n maxDepth,\n averageDeviationDepth,\n isAIMediaLibraryConfigured,\n };\n },\n\n async sendMetrics() {\n const metrics = await this.computeMetrics();\n strapi.telemetry.send('didSendUploadPropertiesOnceAWeek', {\n groupProperties: { metrics },\n });\n\n const metricsInfoStored = await getMetricsStoreValue();\n await setMetricsStoreValue({ ...metricsInfoStored, lastWeeklyUpdate: new Date().getTime() });\n },\n\n async ensureWeeklyStoredCronSchedule(): Promise<string> {\n const metricsInfoStored = await getMetricsStoreValue();\n const { weeklySchedule: currentSchedule, lastWeeklyUpdate } = metricsInfoStored;\n\n const now = new Date();\n let weeklySchedule = currentSchedule;\n\n if (!weeklySchedule || !lastWeeklyUpdate || lastWeeklyUpdate + ONE_WEEK < now.getTime()) {\n weeklySchedule = getWeeklyCronScheduleAt(add(now, { seconds: 15 }));\n await setMetricsStoreValue({ ...metricsInfoStored, weeklySchedule });\n\n return weeklySchedule;\n }\n\n return weeklySchedule;\n },\n\n async registerCron() {\n const weeklySchedule = await this.ensureWeeklyStoredCronSchedule();\n\n strapi.cron.add({\n uploadWeekly: {\n task: this.sendMetrics.bind(this),\n options: weeklySchedule,\n },\n });\n },\n});\n"],"names":["ONE_WEEK","getMetricsStoreValue","value","strapi","store","get","type","name","key","defaultTo","setMetricsStoreValue","set","computeMetrics","pathColName","db","metadata","FOLDER_MODEL_UID","attributes","path","columnName","folderTable","getModel","collectionName","keepOnlySlashesSQLString","queryParams","i","push","String","res","getConnection","select","connection","raw","groupBy","folderLevelsArray","map","depth","Number","occurence","product","folderNumber","maxDepth","folderLevel","averageDepth","sumOfDeviation","Math","abs","averageDeviationDepth","assetNumber","query","FILE_MODEL_UID","count","settings","plugin","service","getSettings","isAIMediaLibraryConfigured","aiMetadata","sendMetrics","metrics","telemetry","send","groupProperties","metricsInfoStored","lastWeeklyUpdate","Date","getTime","ensureWeeklyStoredCronSchedule","weeklySchedule","currentSchedule","now","getWeeklyCronScheduleAt","add","seconds","registerCron","cron","uploadWeekly","task","bind","options"],"mappings":"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"}
|
|
@@ -72,12 +72,16 @@ var weeklyMetrics = (({ strapi: strapi1 })=>({
|
|
|
72
72
|
const averageDeviationDepth = folderNumber !== 0 ? sumOfDeviation / folderNumber : 0;
|
|
73
73
|
// File metrics
|
|
74
74
|
const assetNumber = await strapi1.db.query(FILE_MODEL_UID).count();
|
|
75
|
+
// AI metadata generation metrics
|
|
76
|
+
const settings = await strapi1.plugin('upload').service('upload').getSettings();
|
|
77
|
+
const isAIMediaLibraryConfigured = settings?.aiMetadata;
|
|
75
78
|
return {
|
|
76
79
|
assetNumber,
|
|
77
80
|
folderNumber,
|
|
78
81
|
averageDepth,
|
|
79
82
|
maxDepth,
|
|
80
|
-
averageDeviationDepth
|
|
83
|
+
averageDeviationDepth,
|
|
84
|
+
isAIMediaLibraryConfigured
|
|
81
85
|
};
|
|
82
86
|
},
|
|
83
87
|
async sendMetrics () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weekly-metrics.mjs","sources":["../../../server/src/services/weekly-metrics.ts"],"sourcesContent":["import { defaultTo } from 'lodash/fp';\nimport { add } from 'date-fns';\n\nimport type { Core } from '@strapi/types';\n\nimport { getWeeklyCronScheduleAt } from '../utils/cron';\nimport { FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\n\ntype MetricStoreValue = {\n lastWeeklyUpdate?: number;\n weeklySchedule?: string;\n};\n\nconst ONE_WEEK = 7 * 24 * 60 * 60 * 1000;\n\nconst getMetricsStoreValue = async (): Promise<MetricStoreValue> => {\n const value = await strapi.store.get({ type: 'plugin', name: 'upload', key: 'metrics' });\n return defaultTo({}, value) as MetricStoreValue;\n};\nconst setMetricsStoreValue = (value: MetricStoreValue) =>\n strapi.store.set({ type: 'plugin', name: 'upload', key: 'metrics', value });\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async computeMetrics() {\n // Folder metrics\n // @ts-expect-error - no dynamic types for the metadata\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n\n let keepOnlySlashesSQLString = '??';\n const queryParams = [pathColName];\n for (let i = 0; i < 10; i += 1) {\n keepOnlySlashesSQLString = `REPLACE(${keepOnlySlashesSQLString}, ?, ?)`;\n queryParams.push(String(i), '');\n }\n\n /*\n The following query goal is to count the number of folders with depth 1, depth 2 etc.\n The query returns :\n [\n { depth: 1, occurence: 4 },\n { depth: 2, occurence: 2 },\n { depth: 3, occurence: 5 },\n ]\n\n The query is built as follow:\n 1. In order to get the depth level of a folder:\n - we take their path\n - remove all numbers (by replacing 0123456789 by '', thus the 10 REPLACE in the query)\n - count the remaining `/`, which correspond to their depth (by using LENGTH)\n We now have, for each folder, its depth.\n 2. In order to get the number of folders for each depth:\n - we group them by their depth and use COUNT(*)\n */\n\n const res = (await strapi.db\n .getConnection(folderTable)\n .select(\n strapi.db.connection.raw(\n `LENGTH(${keepOnlySlashesSQLString}) AS depth, COUNT(*) AS occurence`,\n queryParams\n )\n )\n .groupBy('depth')) as Array<{ depth: string; occurence: string }>;\n\n const folderLevelsArray = res.map((map) => ({\n depth: Number(map.depth),\n occurence: Number(map.occurence),\n })); // values can be strings depending on the database\n\n let product = 0;\n let folderNumber = 0;\n let maxDepth = 0;\n for (const folderLevel of folderLevelsArray) {\n product += folderLevel.depth * folderLevel.occurence;\n folderNumber += folderLevel.occurence;\n if (folderLevel.depth > maxDepth) {\n maxDepth = folderLevel.depth;\n }\n }\n const averageDepth = folderNumber !== 0 ? product / folderNumber : 0;\n\n let sumOfDeviation = 0;\n for (const folderLevel of folderLevelsArray) {\n sumOfDeviation += Math.abs(folderLevel.depth - averageDepth) * folderLevel.occurence;\n }\n\n const averageDeviationDepth = folderNumber !== 0 ? sumOfDeviation / folderNumber : 0;\n\n // File metrics\n const assetNumber = await strapi.db.query(FILE_MODEL_UID).count();\n\n return {\n assetNumber,\n folderNumber,\n averageDepth,\n maxDepth,\n averageDeviationDepth,\n };\n },\n\n async sendMetrics() {\n const metrics = await this.computeMetrics();\n strapi.telemetry.send('didSendUploadPropertiesOnceAWeek', {\n groupProperties: { metrics },\n });\n\n const metricsInfoStored = await getMetricsStoreValue();\n await setMetricsStoreValue({ ...metricsInfoStored, lastWeeklyUpdate: new Date().getTime() });\n },\n\n async ensureWeeklyStoredCronSchedule(): Promise<string> {\n const metricsInfoStored = await getMetricsStoreValue();\n const { weeklySchedule: currentSchedule, lastWeeklyUpdate } = metricsInfoStored;\n\n const now = new Date();\n let weeklySchedule = currentSchedule;\n\n if (!weeklySchedule || !lastWeeklyUpdate || lastWeeklyUpdate + ONE_WEEK < now.getTime()) {\n weeklySchedule = getWeeklyCronScheduleAt(add(now, { seconds: 15 }));\n await setMetricsStoreValue({ ...metricsInfoStored, weeklySchedule });\n\n return weeklySchedule;\n }\n\n return weeklySchedule;\n },\n\n async registerCron() {\n const weeklySchedule = await this.ensureWeeklyStoredCronSchedule();\n\n strapi.cron.add({\n uploadWeekly: {\n task: this.sendMetrics.bind(this),\n options: weeklySchedule,\n },\n });\n },\n});\n"],"names":["ONE_WEEK","getMetricsStoreValue","value","strapi","store","get","type","name","key","defaultTo","setMetricsStoreValue","set","computeMetrics","pathColName","db","metadata","FOLDER_MODEL_UID","attributes","path","columnName","folderTable","getModel","collectionName","keepOnlySlashesSQLString","queryParams","i","push","String","res","getConnection","select","connection","raw","groupBy","folderLevelsArray","map","depth","Number","occurence","product","folderNumber","maxDepth","folderLevel","averageDepth","sumOfDeviation","Math","abs","averageDeviationDepth","assetNumber","query","FILE_MODEL_UID","count","sendMetrics","metrics","telemetry","send","groupProperties","metricsInfoStored","lastWeeklyUpdate","Date","getTime","ensureWeeklyStoredCronSchedule","weeklySchedule","currentSchedule","now","getWeeklyCronScheduleAt","add","seconds","registerCron","cron","uploadWeekly","task","bind","options"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"weekly-metrics.mjs","sources":["../../../server/src/services/weekly-metrics.ts"],"sourcesContent":["import { defaultTo } from 'lodash/fp';\nimport { add } from 'date-fns';\n\nimport type { Core } from '@strapi/types';\n\nimport { getWeeklyCronScheduleAt } from '../utils/cron';\nimport { FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\nimport { Settings } from '../controllers/validation/admin/settings';\n\ntype MetricStoreValue = {\n lastWeeklyUpdate?: number;\n weeklySchedule?: string;\n};\n\nconst ONE_WEEK = 7 * 24 * 60 * 60 * 1000;\n\nconst getMetricsStoreValue = async (): Promise<MetricStoreValue> => {\n const value = await strapi.store.get({ type: 'plugin', name: 'upload', key: 'metrics' });\n return defaultTo({}, value) as MetricStoreValue;\n};\nconst setMetricsStoreValue = (value: MetricStoreValue) =>\n strapi.store.set({ type: 'plugin', name: 'upload', key: 'metrics', value });\n\nexport default ({ strapi }: { strapi: Core.Strapi }) => ({\n async computeMetrics() {\n // Folder metrics\n // @ts-expect-error - no dynamic types for the metadata\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n\n let keepOnlySlashesSQLString = '??';\n const queryParams = [pathColName];\n for (let i = 0; i < 10; i += 1) {\n keepOnlySlashesSQLString = `REPLACE(${keepOnlySlashesSQLString}, ?, ?)`;\n queryParams.push(String(i), '');\n }\n\n /*\n The following query goal is to count the number of folders with depth 1, depth 2 etc.\n The query returns :\n [\n { depth: 1, occurence: 4 },\n { depth: 2, occurence: 2 },\n { depth: 3, occurence: 5 },\n ]\n\n The query is built as follow:\n 1. In order to get the depth level of a folder:\n - we take their path\n - remove all numbers (by replacing 0123456789 by '', thus the 10 REPLACE in the query)\n - count the remaining `/`, which correspond to their depth (by using LENGTH)\n We now have, for each folder, its depth.\n 2. In order to get the number of folders for each depth:\n - we group them by their depth and use COUNT(*)\n */\n\n const res = (await strapi.db\n .getConnection(folderTable)\n .select(\n strapi.db.connection.raw(\n `LENGTH(${keepOnlySlashesSQLString}) AS depth, COUNT(*) AS occurence`,\n queryParams\n )\n )\n .groupBy('depth')) as Array<{ depth: string; occurence: string }>;\n\n const folderLevelsArray = res.map((map) => ({\n depth: Number(map.depth),\n occurence: Number(map.occurence),\n })); // values can be strings depending on the database\n\n let product = 0;\n let folderNumber = 0;\n let maxDepth = 0;\n for (const folderLevel of folderLevelsArray) {\n product += folderLevel.depth * folderLevel.occurence;\n folderNumber += folderLevel.occurence;\n if (folderLevel.depth > maxDepth) {\n maxDepth = folderLevel.depth;\n }\n }\n const averageDepth = folderNumber !== 0 ? product / folderNumber : 0;\n\n let sumOfDeviation = 0;\n for (const folderLevel of folderLevelsArray) {\n sumOfDeviation += Math.abs(folderLevel.depth - averageDepth) * folderLevel.occurence;\n }\n\n const averageDeviationDepth = folderNumber !== 0 ? sumOfDeviation / folderNumber : 0;\n\n // File metrics\n const assetNumber = await strapi.db.query(FILE_MODEL_UID).count();\n\n // AI metadata generation metrics\n const settings: Settings = await strapi.plugin('upload').service('upload').getSettings();\n const isAIMediaLibraryConfigured = settings?.aiMetadata;\n\n return {\n assetNumber,\n folderNumber,\n averageDepth,\n maxDepth,\n averageDeviationDepth,\n isAIMediaLibraryConfigured,\n };\n },\n\n async sendMetrics() {\n const metrics = await this.computeMetrics();\n strapi.telemetry.send('didSendUploadPropertiesOnceAWeek', {\n groupProperties: { metrics },\n });\n\n const metricsInfoStored = await getMetricsStoreValue();\n await setMetricsStoreValue({ ...metricsInfoStored, lastWeeklyUpdate: new Date().getTime() });\n },\n\n async ensureWeeklyStoredCronSchedule(): Promise<string> {\n const metricsInfoStored = await getMetricsStoreValue();\n const { weeklySchedule: currentSchedule, lastWeeklyUpdate } = metricsInfoStored;\n\n const now = new Date();\n let weeklySchedule = currentSchedule;\n\n if (!weeklySchedule || !lastWeeklyUpdate || lastWeeklyUpdate + ONE_WEEK < now.getTime()) {\n weeklySchedule = getWeeklyCronScheduleAt(add(now, { seconds: 15 }));\n await setMetricsStoreValue({ ...metricsInfoStored, weeklySchedule });\n\n return weeklySchedule;\n }\n\n return weeklySchedule;\n },\n\n async registerCron() {\n const weeklySchedule = await this.ensureWeeklyStoredCronSchedule();\n\n strapi.cron.add({\n uploadWeekly: {\n task: this.sendMetrics.bind(this),\n options: weeklySchedule,\n },\n });\n },\n});\n"],"names":["ONE_WEEK","getMetricsStoreValue","value","strapi","store","get","type","name","key","defaultTo","setMetricsStoreValue","set","computeMetrics","pathColName","db","metadata","FOLDER_MODEL_UID","attributes","path","columnName","folderTable","getModel","collectionName","keepOnlySlashesSQLString","queryParams","i","push","String","res","getConnection","select","connection","raw","groupBy","folderLevelsArray","map","depth","Number","occurence","product","folderNumber","maxDepth","folderLevel","averageDepth","sumOfDeviation","Math","abs","averageDeviationDepth","assetNumber","query","FILE_MODEL_UID","count","settings","plugin","service","getSettings","isAIMediaLibraryConfigured","aiMetadata","sendMetrics","metrics","telemetry","send","groupProperties","metricsInfoStored","lastWeeklyUpdate","Date","getTime","ensureWeeklyStoredCronSchedule","weeklySchedule","currentSchedule","now","getWeeklyCronScheduleAt","add","seconds","registerCron","cron","uploadWeekly","task","bind","options"],"mappings":";;;;;AAcA,MAAMA,QAAW,GAAA,CAAA,GAAI,EAAK,GAAA,EAAA,GAAK,EAAK,GAAA,IAAA;AAEpC,MAAMC,oBAAuB,GAAA,UAAA;AAC3B,IAAA,MAAMC,QAAQ,MAAMC,MAAAA,CAAOC,KAAK,CAACC,GAAG,CAAC;QAAEC,IAAM,EAAA,QAAA;QAAUC,IAAM,EAAA,QAAA;QAAUC,GAAK,EAAA;AAAU,KAAA,CAAA;IACtF,OAAOC,SAAAA,CAAU,EAAIP,EAAAA,KAAAA,CAAAA;AACvB,CAAA;AACA,MAAMQ,uBAAuB,CAACR,KAAAA,GAC5BC,OAAOC,KAAK,CAACO,GAAG,CAAC;QAAEL,IAAM,EAAA,QAAA;QAAUC,IAAM,EAAA,QAAA;QAAUC,GAAK,EAAA,SAAA;AAAWN,QAAAA;AAAM,KAAA,CAAA;AAE3E,oBAAe,CAAA,CAAC,EAAEC,QAAAA,OAAM,EAA2B,IAAM;QACvD,MAAMS,cAAAA,CAAAA,GAAAA;;;AAGJ,YAAA,MAAMC,WAAcV,GAAAA,OAAAA,CAAOW,EAAE,CAACC,QAAQ,CAACV,GAAG,CAACW,gBAAkBC,CAAAA,CAAAA,UAAU,CAACC,IAAI,CAACC,UAAU;AACvF,YAAA,MAAMC,WAAcjB,GAAAA,OAAAA,CAAOkB,QAAQ,CAACL,kBAAkBM,cAAc;AAEpE,YAAA,IAAIC,wBAA2B,GAAA,IAAA;AAC/B,YAAA,MAAMC,WAAc,GAAA;AAACX,gBAAAA;AAAY,aAAA;AACjC,YAAA,IAAK,IAAIY,CAAI,GAAA,CAAA,EAAGA,CAAI,GAAA,EAAA,EAAIA,KAAK,CAAG,CAAA;AAC9BF,gBAAAA,wBAAAA,GAA2B,CAAC,QAAQ,EAAEA,wBAAAA,CAAyB,OAAO,CAAC;gBACvEC,WAAYE,CAAAA,IAAI,CAACC,MAAAA,CAAOF,CAAI,CAAA,EAAA,EAAA,CAAA;AAC9B;AAEA;;;;;;;;;;;;;;;;;OAmBA,MAAMG,GAAO,GAAA,MAAMzB,OAAOW,CAAAA,EAAE,CACzBe,aAAa,CAACT,WAAAA,CAAAA,CACdU,MAAM,CACL3B,OAAOW,CAAAA,EAAE,CAACiB,UAAU,CAACC,GAAG,CACtB,CAAC,OAAO,EAAET,wBAAyB,CAAA,iCAAiC,CAAC,EACrEC,WAGHS,CAAAA,CAAAA,CAAAA,OAAO,CAAC,OAAA,CAAA;AAEX,YAAA,MAAMC,oBAAoBN,GAAIO,CAAAA,GAAG,CAAC,CAACA,OAAS;oBAC1CC,KAAOC,EAAAA,MAAAA,CAAOF,IAAIC,KAAK,CAAA;oBACvBE,SAAWD,EAAAA,MAAAA,CAAOF,IAAIG,SAAS;AACjC,iBAAA;AAEA,YAAA,IAAIC,OAAU,GAAA,CAAA;AACd,YAAA,IAAIC,YAAe,GAAA,CAAA;AACnB,YAAA,IAAIC,QAAW,GAAA,CAAA;YACf,KAAK,MAAMC,eAAeR,iBAAmB,CAAA;AAC3CK,gBAAAA,OAAAA,IAAWG,WAAYN,CAAAA,KAAK,GAAGM,WAAAA,CAAYJ,SAAS;AACpDE,gBAAAA,YAAAA,IAAgBE,YAAYJ,SAAS;gBACrC,IAAII,WAAAA,CAAYN,KAAK,GAAGK,QAAU,EAAA;AAChCA,oBAAAA,QAAAA,GAAWC,YAAYN,KAAK;AAC9B;AACF;AACA,YAAA,MAAMO,YAAeH,GAAAA,YAAAA,KAAiB,CAAID,GAAAA,OAAAA,GAAUC,YAAe,GAAA,CAAA;AAEnE,YAAA,IAAII,cAAiB,GAAA,CAAA;YACrB,KAAK,MAAMF,eAAeR,iBAAmB,CAAA;gBAC3CU,cAAkBC,IAAAA,IAAAA,CAAKC,GAAG,CAACJ,WAAAA,CAAYN,KAAK,GAAGO,YAAAA,CAAAA,GAAgBD,YAAYJ,SAAS;AACtF;AAEA,YAAA,MAAMS,qBAAwBP,GAAAA,YAAAA,KAAiB,CAAII,GAAAA,cAAAA,GAAiBJ,YAAe,GAAA,CAAA;;YAGnF,MAAMQ,WAAAA,GAAc,MAAM7C,OAAOW,CAAAA,EAAE,CAACmC,KAAK,CAACC,gBAAgBC,KAAK,EAAA;;YAG/D,MAAMC,QAAAA,GAAqB,MAAMjD,OAAOkD,CAAAA,MAAM,CAAC,QAAUC,CAAAA,CAAAA,OAAO,CAAC,QAAA,CAAA,CAAUC,WAAW,EAAA;AACtF,YAAA,MAAMC,6BAA6BJ,QAAUK,EAAAA,UAAAA;YAE7C,OAAO;AACLT,gBAAAA,WAAAA;AACAR,gBAAAA,YAAAA;AACAG,gBAAAA,YAAAA;AACAF,gBAAAA,QAAAA;AACAM,gBAAAA,qBAAAA;AACAS,gBAAAA;AACF,aAAA;AACF,SAAA;QAEA,MAAME,WAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMC,OAAU,GAAA,MAAM,IAAI,CAAC/C,cAAc,EAAA;AACzCT,YAAAA,OAAAA,CAAOyD,SAAS,CAACC,IAAI,CAAC,kCAAoC,EAAA;gBACxDC,eAAiB,EAAA;AAAEH,oBAAAA;AAAQ;AAC7B,aAAA,CAAA;AAEA,YAAA,MAAMI,oBAAoB,MAAM9D,oBAAAA,EAAAA;AAChC,YAAA,MAAMS,oBAAqB,CAAA;AAAE,gBAAA,GAAGqD,iBAAiB;gBAAEC,gBAAkB,EAAA,IAAIC,OAAOC,OAAO;AAAG,aAAA,CAAA;AAC5F,SAAA;QAEA,MAAMC,8BAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMJ,oBAAoB,MAAM9D,oBAAAA,EAAAA;AAChC,YAAA,MAAM,EAAEmE,cAAgBC,EAAAA,eAAe,EAAEL,gBAAgB,EAAE,GAAGD,iBAAAA;AAE9D,YAAA,MAAMO,MAAM,IAAIL,IAAAA,EAAAA;AAChB,YAAA,IAAIG,cAAiBC,GAAAA,eAAAA;YAErB,IAAI,CAACD,kBAAkB,CAACJ,gBAAAA,IAAoBA,mBAAmBhE,QAAWsE,GAAAA,GAAAA,CAAIJ,OAAO,EAAI,EAAA;gBACvFE,cAAiBG,GAAAA,uBAAAA,CAAwBC,IAAIF,GAAK,EAAA;oBAAEG,OAAS,EAAA;AAAG,iBAAA,CAAA,CAAA;AAChE,gBAAA,MAAM/D,oBAAqB,CAAA;AAAE,oBAAA,GAAGqD,iBAAiB;AAAEK,oBAAAA;AAAe,iBAAA,CAAA;gBAElE,OAAOA,cAAAA;AACT;YAEA,OAAOA,cAAAA;AACT,SAAA;QAEA,MAAMM,YAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMN,cAAiB,GAAA,MAAM,IAAI,CAACD,8BAA8B,EAAA;YAEhEhE,OAAOwE,CAAAA,IAAI,CAACH,GAAG,CAAC;gBACdI,YAAc,EAAA;AACZC,oBAAAA,IAAAA,EAAM,IAAI,CAACnB,WAAW,CAACoB,IAAI,CAAC,IAAI,CAAA;oBAChCC,OAASX,EAAAA;AACX;AACF,aAAA,CAAA;AACF;AACF,KAAA,CAAC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../server/src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAK1C,wBAAsB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../server/src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAK1C,wBAAsB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;CAAE,iBA0ClE"}
|
|
@@ -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;;4BASH,OAAO;wBA2BX,OAAO;qBA0BV,OAAO;qBAgCP,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;;4BASH,OAAO;wBA2BX,OAAO;qBA0BV,OAAO;qBAgCP,OAAO;gBA+FZ,OAAO;;AArL3B,wBAqME"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOxC,wBAQG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/src/services/index.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/src/services/index.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWpB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weekly-metrics.d.ts","sourceRoot":"","sources":["../../../../server/src/services/weekly-metrics.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"weekly-metrics.d.ts","sourceRoot":"","sources":["../../../../server/src/services/weekly-metrics.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;qCAoBd;IAAE,MAAM,EAAE,KAAK,MAAM,CAAA;CAAE;;;;;;;;;;sCA8FT,QAAQ,MAAM,CAAC;;;AA9FzD,wBAyHG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/upload",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.29.0",
|
|
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": {
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@reduxjs/toolkit": "1.9.7",
|
|
65
65
|
"@strapi/design-system": "2.0.0-rc.30",
|
|
66
66
|
"@strapi/icons": "2.0.0-rc.30",
|
|
67
|
-
"@strapi/provider-upload-local": "5.
|
|
68
|
-
"@strapi/utils": "5.
|
|
67
|
+
"@strapi/provider-upload-local": "5.29.0",
|
|
68
|
+
"@strapi/utils": "5.29.0",
|
|
69
69
|
"byte-size": "8.1.1",
|
|
70
70
|
"cropperjs": "1.6.1",
|
|
71
71
|
"date-fns": "2.30.0",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"zod": "3.25.67"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
-
"@strapi/admin": "5.
|
|
92
|
-
"@strapi/types": "5.
|
|
91
|
+
"@strapi/admin": "5.29.0",
|
|
92
|
+
"@strapi/types": "5.29.0",
|
|
93
93
|
"@testing-library/dom": "10.1.0",
|
|
94
94
|
"@testing-library/react": "15.0.7",
|
|
95
95
|
"@testing-library/user-event": "14.5.2",
|