@strapi/upload 5.55.1 → 5.57.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/hooks/useAIMetadataAvailability.js +14 -0
- package/dist/admin/hooks/useAIMetadataAvailability.js.map +1 -0
- package/dist/admin/hooks/useAIMetadataAvailability.mjs +12 -0
- package/dist/admin/hooks/useAIMetadataAvailability.mjs.map +1 -0
- package/dist/admin/hooks/useAIMetadataEnabled.js +7 -5
- package/dist/admin/hooks/useAIMetadataEnabled.js.map +1 -1
- package/dist/admin/hooks/useAIMetadataEnabled.mjs +7 -5
- package/dist/admin/hooks/useAIMetadataEnabled.mjs.map +1 -1
- package/dist/admin/hooks/useTracking.js +4 -10
- package/dist/admin/hooks/useTracking.js.map +1 -1
- package/dist/admin/hooks/useTracking.mjs +5 -10
- package/dist/admin/hooks/useTracking.mjs.map +1 -1
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.js +14 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.js.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs +12 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.js +23 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.js.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs +21 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs.map +1 -0
- package/dist/admin/legacy/hooks/useTracking.js +5 -3
- package/dist/admin/legacy/hooks/useTracking.js.map +1 -1
- package/dist/admin/legacy/hooks/useTracking.mjs +5 -3
- package/dist/admin/legacy/hooks/useTracking.mjs.map +1 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.js +2 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.js.map +1 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.mjs +2 -1
- package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.mjs.map +1 -1
- package/dist/admin/legacy/pages/App/MediaLibrary.js +6 -5
- package/dist/admin/legacy/pages/App/MediaLibrary.js.map +1 -1
- package/dist/admin/legacy/pages/App/MediaLibrary.mjs +6 -5
- package/dist/admin/legacy/pages/App/MediaLibrary.mjs.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.js +24 -12
- package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.mjs +25 -13
- package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetActions.js +326 -0
- package/dist/admin/pages/Assets/components/AssetActions.js.map +1 -0
- package/dist/admin/pages/Assets/components/AssetActions.mjs +324 -0
- package/dist/admin/pages/Assets/components/AssetActions.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/AssetActionsMenu.js +28 -309
- package/dist/admin/pages/Assets/components/AssetActionsMenu.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetActionsMenu.mjs +31 -312
- package/dist/admin/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetContextMenu.js +79 -0
- package/dist/admin/pages/Assets/components/AssetContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/AssetContextMenu.mjs +77 -0
- package/dist/admin/pages/Assets/components/AssetContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.js +87 -5
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +89 -7
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +3 -3
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +3 -3
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.js +62 -5
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.mjs +60 -6
- package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsGrid.js +27 -4
- package/dist/admin/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsGrid.mjs +27 -4
- package/dist/admin/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsTable.js +27 -4
- package/dist/admin/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetsTable.mjs +27 -4
- package/dist/admin/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.js +77 -0
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.mjs +75 -0
- package/dist/admin/pages/Assets/components/CursorAnchoredMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.js +4 -2
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.js.map +1 -1
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs +4 -2
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FolderActions.js +139 -0
- package/dist/admin/pages/Assets/components/FolderActions.js.map +1 -0
- package/dist/admin/pages/Assets/components/FolderActions.mjs +137 -0
- package/dist/admin/pages/Assets/components/FolderActions.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/FolderActionsMenu.js +25 -119
- package/dist/admin/pages/Assets/components/FolderActionsMenu.js.map +1 -1
- package/dist/admin/pages/Assets/components/FolderActionsMenu.mjs +27 -121
- package/dist/admin/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FolderContextMenu.js +67 -0
- package/dist/admin/pages/Assets/components/FolderContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/FolderContextMenu.mjs +65 -0
- package/dist/admin/pages/Assets/components/FolderContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js +139 -45
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs +140 -47
- package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.js +115 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.mjs +113 -0
- package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.js +125 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.mjs +122 -0
- package/dist/admin/pages/Assets/components/ItemContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.js +88 -63
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.js.map +1 -1
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.mjs +88 -63
- package/dist/admin/pages/Assets/components/MainAreaContextMenu.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/SelectionActions.js +111 -0
- package/dist/admin/pages/Assets/components/SelectionActions.js.map +1 -0
- package/dist/admin/pages/Assets/components/SelectionActions.mjs +109 -0
- package/dist/admin/pages/Assets/components/SelectionActions.mjs.map +1 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.js +82 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.js.map +1 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.mjs +80 -0
- package/dist/admin/pages/Assets/components/SelectionContextMenu.mjs.map +1 -0
- package/dist/admin/pages/Assets/hooks/useAssetSelection.js +10 -0
- package/dist/admin/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs +10 -0
- package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.js +10 -3
- package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs +10 -3
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
- package/dist/admin/services/api.js.map +1 -1
- package/dist/admin/services/api.mjs.map +1 -1
- package/dist/admin/src/hooks/useAIMetadataAvailability.d.ts +5 -0
- package/dist/admin/src/hooks/useAIMetadataEnabled.d.ts +5 -3
- package/dist/admin/src/hooks/useTracking.d.ts +0 -7
- package/dist/admin/src/legacy/components/EditAssetDialog/PreviewBox/PreviewComponents.d.ts +2 -2
- package/dist/admin/src/legacy/hooks/useAIMetadataAvailability.d.ts +5 -0
- package/dist/admin/src/legacy/hooks/useAIMetadataEnabled.d.ts +12 -0
- package/dist/admin/src/legacy/hooks/useSettings.d.ts +1 -0
- package/dist/admin/src/pages/Assets/components/ActionsMenuContent.d.ts +2 -2
- package/dist/admin/src/pages/Assets/components/AssetActions.d.ts +70 -0
- package/dist/admin/src/pages/Assets/components/AssetActionsMenu.d.ts +5 -14
- package/dist/admin/src/pages/Assets/components/AssetContextMenu.d.ts +27 -0
- package/dist/admin/src/pages/Assets/components/AssetDetails/useCropImg.d.ts +16 -0
- package/dist/admin/src/pages/Assets/components/CursorAnchoredMenu.d.ts +32 -0
- package/dist/admin/src/pages/Assets/components/FolderActions.d.ts +48 -0
- package/dist/admin/src/pages/Assets/components/FolderActionsMenu.d.ts +4 -8
- package/dist/admin/src/pages/Assets/components/FolderContextMenu.d.ts +21 -0
- package/dist/admin/src/pages/Assets/components/FolderTree/FolderTree.d.ts +5 -1
- package/dist/admin/src/pages/Assets/components/FolderTree/FolderTreeRowMenu.d.ts +25 -0
- package/dist/admin/src/pages/Assets/components/ItemContextMenu.d.ts +53 -0
- package/dist/admin/src/pages/Assets/components/MainAreaContextMenu.d.ts +15 -1
- package/dist/admin/src/pages/Assets/components/SelectionActions.d.ts +40 -0
- package/dist/admin/src/pages/Assets/components/SelectionContextMenu.d.ts +25 -0
- package/dist/admin/src/pages/Assets/hooks/useAssetSelection.d.ts +5 -0
- package/dist/admin/src/utils/mediaLibraryVersion.d.ts +18 -0
- package/dist/admin/translations/en.json.js +9 -3
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +9 -3
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/mediaLibraryVersion.js +24 -0
- package/dist/admin/utils/mediaLibraryVersion.js.map +1 -0
- package/dist/admin/utils/mediaLibraryVersion.mjs +20 -0
- package/dist/admin/utils/mediaLibraryVersion.mjs.map +1 -0
- package/dist/server/bootstrap.js +7 -0
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs +7 -0
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-file.js +2 -2
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +2 -2
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-folder-file.js +51 -4
- package/dist/server/controllers/admin-folder-file.js.map +1 -1
- package/dist/server/controllers/admin-folder-file.mjs +52 -5
- package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
- package/dist/server/controllers/admin-folder.js +5 -5
- package/dist/server/controllers/admin-folder.js.map +1 -1
- package/dist/server/controllers/admin-folder.mjs +5 -5
- package/dist/server/controllers/admin-folder.mjs.map +1 -1
- package/dist/server/controllers/admin-settings.js +2 -1
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +2 -1
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +30 -7
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +30 -7
- package/dist/server/controllers/content-api.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/folder-file.js +4 -4
- package/dist/server/controllers/validation/admin/folder-file.js.map +1 -1
- package/dist/server/controllers/validation/admin/folder-file.mjs +2 -2
- package/dist/server/controllers/validation/admin/folder-file.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/folder.js +3 -4
- package/dist/server/controllers/validation/admin/folder.js.map +1 -1
- package/dist/server/controllers/validation/admin/folder.mjs +3 -4
- package/dist/server/controllers/validation/admin/folder.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/upload.js +1 -2
- package/dist/server/controllers/validation/admin/upload.js.map +1 -1
- package/dist/server/controllers/validation/admin/upload.mjs +1 -2
- package/dist/server/controllers/validation/admin/upload.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/utils.js +1 -2
- package/dist/server/controllers/validation/admin/utils.js.map +1 -1
- package/dist/server/controllers/validation/admin/utils.mjs +1 -2
- package/dist/server/controllers/validation/admin/utils.mjs.map +1 -1
- package/dist/server/errors.js +12 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/errors.mjs +10 -0
- package/dist/server/errors.mjs.map +1 -0
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.js +2 -2
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.js.map +1 -1
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.mjs +1 -1
- package/dist/server/migrations/unsign-richtext-and-blocks-urls.mjs.map +1 -1
- package/dist/server/routes/content-api.js +3 -3
- package/dist/server/routes/content-api.js.map +1 -1
- package/dist/server/routes/content-api.mjs +3 -3
- package/dist/server/routes/content-api.mjs.map +1 -1
- package/dist/server/routes/validation/upload.js +8 -0
- package/dist/server/routes/validation/upload.js.map +1 -1
- package/dist/server/routes/validation/upload.mjs +8 -0
- package/dist/server/routes/validation/upload.mjs.map +1 -1
- package/dist/server/services/ai-metadata-provider.js +83 -0
- package/dist/server/services/ai-metadata-provider.js.map +1 -0
- package/dist/server/services/ai-metadata-provider.mjs +61 -0
- package/dist/server/services/ai-metadata-provider.mjs.map +1 -0
- package/dist/server/services/ai-metadata-strapi-managed.js +48 -0
- package/dist/server/services/ai-metadata-strapi-managed.js.map +1 -0
- package/dist/server/services/ai-metadata-strapi-managed.mjs +46 -0
- package/dist/server/services/ai-metadata-strapi-managed.mjs.map +1 -0
- package/dist/server/services/ai-metadata.js +5 -58
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +6 -39
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/api-upload-folder.js +3 -3
- package/dist/server/services/api-upload-folder.js.map +1 -1
- package/dist/server/services/api-upload-folder.mjs +3 -3
- package/dist/server/services/api-upload-folder.mjs.map +1 -1
- package/dist/server/services/extensions/utils.js +19 -15
- package/dist/server/services/extensions/utils.js.map +1 -1
- package/dist/server/services/extensions/utils.mjs +19 -15
- package/dist/server/services/extensions/utils.mjs.map +1 -1
- package/dist/server/services/file.js +2 -2
- package/dist/server/services/file.js.map +1 -1
- package/dist/server/services/file.mjs +1 -1
- package/dist/server/services/file.mjs.map +1 -1
- package/dist/server/services/folder.js +9 -6
- package/dist/server/services/folder.js.map +1 -1
- package/dist/server/services/folder.mjs +9 -6
- package/dist/server/services/folder.mjs.map +1 -1
- package/dist/server/services/index.js +3 -1
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/index.mjs +3 -1
- package/dist/server/services/index.mjs.map +1 -1
- package/dist/server/services/metrics.js +1 -1
- package/dist/server/services/metrics.js.map +1 -1
- package/dist/server/services/metrics.mjs +1 -1
- package/dist/server/services/metrics.mjs.map +1 -1
- package/dist/server/services/provider.js +3 -4
- package/dist/server/services/provider.js.map +1 -1
- package/dist/server/services/provider.mjs +3 -4
- package/dist/server/services/provider.mjs.map +1 -1
- package/dist/server/services/upload.js +5 -6
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +4 -5
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/services/weekly-metrics.js +2 -2
- package/dist/server/services/weekly-metrics.js.map +1 -1
- package/dist/server/services/weekly-metrics.mjs +2 -2
- 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-folder-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-folder.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/controllers/content-api.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/folder.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/upload.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/utils.d.ts.map +1 -1
- package/dist/server/src/errors.d.ts +6 -0
- package/dist/server/src/errors.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +6 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/mcp/ambient-instance.d.ts +3 -1
- package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
- package/dist/server/src/routes/validation/upload.d.ts +4 -0
- package/dist/server/src/routes/validation/upload.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata-provider.d.ts +26 -0
- package/dist/server/src/services/ai-metadata-provider.d.ts.map +1 -0
- package/dist/server/src/services/ai-metadata-strapi-managed.d.ts +7 -0
- package/dist/server/src/services/ai-metadata-strapi-managed.d.ts.map +1 -0
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/extensions/utils.d.ts +33 -1
- package/dist/server/src/services/extensions/utils.d.ts.map +1 -1
- package/dist/server/src/services/folder.d.ts +5 -2
- package/dist/server/src/services/folder.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +6 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/provider.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/dist/server/src/utils/images.d.ts +2 -2
- package/dist/server/src/utils/images.d.ts.map +1 -1
- package/dist/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/server/utils/images.js +6 -7
- package/dist/server/utils/images.js.map +1 -1
- package/dist/server/utils/images.mjs +6 -7
- package/dist/server/utils/images.mjs.map +1 -1
- package/dist/server/utils/index.js.map +1 -1
- package/dist/server/utils/index.mjs.map +1 -1
- package/dist/shared/contracts/settings.d.ts +6 -0
- package/package.json +11 -11
- package/dist/admin/legacy/hooks/useAiAvailability.js +0 -22
- package/dist/admin/legacy/hooks/useAiAvailability.js.map +0 -1
- package/dist/admin/legacy/hooks/useAiAvailability.mjs +0 -20
- package/dist/admin/legacy/hooks/useAiAvailability.mjs.map +0 -1
- package/dist/admin/src/legacy/hooks/useAiAvailability.d.ts +0 -4
|
@@ -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';\nimport { notifyMediaLibraryDefault } from './media-library-default-notice';\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 // Whether this plugin has stored settings from an earlier boot, which is what separates\n // an upgrading app from a fresh install for the Media Library default notice below.\n let isExistingApp = false;\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\n if (config) {\n isExistingApp = true;\n }\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 notifyMediaLibraryDefault({ strapi, isExistingApp });\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","isExistingApp","key","defaultValue","Object","entries","configurator","store","type","name","config","get","keys","every","prototype","hasOwnProperty","call","set","value","assign","notifyMediaLibraryDefault","registerPermissionActions","registerWebhookEvents","getService","registerCron","sendUploadPluginMetrics","signFileUrlsOnDocumentService","ALLOWED_WEBHOOK_EVENTS","forEach","addAllowedEvent","actions","section","displayName","uid","pluginName","subCategory","category","service","actionProvider","registerMany"],"mappings":";;;;AAMO,eAAeA,SAAAA,CAAU,EAAEC,MAAAA,EAAAA,OAAM,EAA2B,EAAA;AACjE,IAAA,MAAMC,aAAAA,GAAgB;QACpBC,QAAAA,EAAU;YACRC,gBAAAA,EAAkB,IAAA;YAClBC,oBAAAA,EAAsB,IAAA;YACtBC,eAAAA,EAAiB,KAAA;YACjBC,UAAAA,EAAY;AACd,SAAA;QACAC,kBAAAA,EAAoB;YAClBC,QAAAA,EAAU,EAAA;YACVC,IAAAA,EAAMC,oBAAoB,CAAC,CAAA;AAC7B;AACF,KAAA;;;AAIA,IAAA,IAAIC,aAAAA,GAAgB,KAAA;IAEpB,KAAK,MAAM,CAACC,GAAAA,EAAKC,YAAAA,CAAa,IAAIC,MAAAA,CAAOC,OAAO,CAACd,aAAAA,CAAAA,CAAgB;;QAE/D,MAAMe,YAAAA,GAAehB,OAAAA,CAAOiB,KAAK,CAAE;YAAEC,IAAAA,EAAM,QAAA;YAAUC,IAAAA,EAAM,QAAA;AAAUP,YAAAA;AAAI,SAAA,CAAA;AAEzE,QAAA,MAAMQ,MAAAA,GAAS,MAAMJ,YAAAA,CAAaK,GAAG,CAAC,EAAC,CAAA;AAEvC,QAAA,IAAID,MAAAA,EAAQ;YACVT,aAAAA,GAAgB,IAAA;AAClB,QAAA;AACA,QAAA,IACES,UACAN,MAAAA,CAAOQ,IAAI,CAACT,YAAAA,CAAAA,CAAcU,KAAK,CAAC,CAACX,GAAAA,GAAQE,MAAAA,CAAOU,SAAS,CAACC,cAAc,CAACC,IAAI,CAACN,QAAQR,GAAAA,CAAAA,CAAAA,EACtF;AAEA,YAAA;AACF,QAAA;;;QAIA,MAAMI,YAAAA,CAAaW,GAAG,CAAC;AACrBC,YAAAA,KAAAA,EAAOd,MAAAA,CAAOe,MAAM,CAAChB,YAAAA,EAAcO,UAAU,EAAC;AAChD,SAAA,CAAA;AACF,IAAA;AAEA,IAAA,MAAMU,yBAAAA,CAA0B;QAAE9B,MAAAA,EAAAA,OAAAA;AAAQW,QAAAA;AAAc,KAAA,CAAA;IAExD,MAAMoB,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';\nimport { notifyMediaLibraryDefault } from './media-library-default-notice';\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 // Whether this plugin has stored settings from an earlier boot, which is what separates\n // an upgrading app from a fresh install for the Media Library default notice below.\n let isExistingApp = false;\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\n if (config) {\n isExistingApp = true;\n }\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 notifyMediaLibraryDefault({ strapi, isExistingApp });\n\n await registerPermissionActions();\n await registerWebhookEvents();\n\n await getService('weeklyMetrics').registerCron();\n\n // AI metadata\n if (strapi.ai.admin.isAvailable()) {\n const aiMetadataProvider = getService('aiMetadataProvider');\n\n if (!aiMetadataProvider.hasProvider() && strapi.ai.admin.isStrapiManagedAiEnabled()) {\n aiMetadataProvider.registerStrapiManagedProvider();\n }\n }\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","isExistingApp","key","defaultValue","Object","entries","configurator","store","type","name","config","get","keys","every","prototype","hasOwnProperty","call","set","value","assign","notifyMediaLibraryDefault","registerPermissionActions","registerWebhookEvents","getService","registerCron","ai","admin","isAvailable","aiMetadataProvider","hasProvider","isStrapiManagedAiEnabled","registerStrapiManagedProvider","sendUploadPluginMetrics","signFileUrlsOnDocumentService","ALLOWED_WEBHOOK_EVENTS","forEach","addAllowedEvent","actions","section","displayName","uid","pluginName","subCategory","category","service","actionProvider","registerMany"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _ = require('lodash');
|
|
4
4
|
var utils = require('@strapi/utils');
|
|
5
5
|
var index = require('../utils/index.js');
|
|
6
6
|
var constants = require('../constants.js');
|
|
@@ -27,7 +27,7 @@ var adminFile = {
|
|
|
27
27
|
await pm.validateQuery(ctx.query);
|
|
28
28
|
const query = await utils.async.pipe(// Start by sanitizing the incoming query
|
|
29
29
|
(q)=>pm.sanitizeQuery(q), // Add the default query which should not be validated or sanitized
|
|
30
|
-
(q)=>
|
|
30
|
+
(q)=>_.merge({}, defaultQuery, q), // Add the dynamic filters based on permissions' conditions
|
|
31
31
|
(q)=>pm.addPermissionsQueryTo(q))(ctx.query);
|
|
32
32
|
const { results: files, pagination } = await index.getService('upload').findPage(query);
|
|
33
33
|
// Sign file urls for private providers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-file.js","sources":["../../../server/src/controllers/admin-file.ts"],"sourcesContent":["import { merge } from 'lodash/fp';\nimport { async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { validateGenerateAIMetadataBody } from './validation/admin/ai-metadata';\n\nexport default {\n async find(ctx: Context) {\n const {\n state: { userAbility },\n } = ctx;\n\n const defaultQuery = { populate: { folder: true } };\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // validate the incoming user query params\n await pm.validateQuery(ctx.query);\n\n const query = await async.pipe(\n // Start by sanitizing the incoming query\n (q) => pm.sanitizeQuery(q),\n // Add the default query which should not be validated or sanitized\n (q) => merge(defaultQuery, q),\n // Add the dynamic filters based on permissions' conditions\n (q) => pm.addPermissionsQueryTo(q)\n )(ctx.query);\n\n const { results: files, pagination } = await getService('upload').findPage(query);\n\n // Sign file urls for private providers\n const signedFiles = await async.map(files, getService('file').signFileUrls);\n\n const sanitizedFiles = await pm.sanitizeOutput(signedFiles);\n\n return { results: sanitizedFiles, pagination };\n },\n\n async findOne(ctx: Context) {\n const {\n state: { userAbility },\n params: { id },\n } = ctx;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.read,\n FILE_MODEL_UID,\n id\n );\n\n const signedFile = await getService('file').signFileUrls(file);\n ctx.body = await pm.sanitizeOutput(signedFile);\n },\n\n async destroy(ctx: Context) {\n const { id } = ctx.params;\n const { userAbility } = ctx.state;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const [body] = await Promise.all([\n pm.sanitizeOutput(file, { action: ACTIONS.read }),\n getService('upload').remove(file),\n ]);\n\n ctx.body = body;\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/pending-count`\n *\n * How many images a backfill job would have to process, i.e. those still\n * missing AI metadata, alongside the total image count.\n */\n async getAIMetadataPendingCount(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n const { imagesWithoutMetadataCount, totalImages } =\n await aiMetadataService.countImagesWithoutMetadata();\n\n ctx.body = {\n imagesWithoutMetadataCount,\n totalImages,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to get AI metadata count';\n\n strapi.log.error('Failed to get AI metadata count', {\n message,\n error,\n });\n\n ctx.badRequest(message);\n }\n },\n\n /**\n * `POST /upload/ai-metadata-jobs`\n *\n * Create a backfill job that generates AI metadata for every image still\n * missing it, and return the job so the client can poll it. Processing runs\n * detached from this request; see `generateAIMetadata` for the synchronous\n * counterpart that works on an explicit selection.\n */\n async createAIMetadataJob(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n // Get count first to check if there are images to process\n const result = await aiMetadataService.countImagesWithoutMetadata();\n\n if (result.imagesWithoutMetadataCount === 0) {\n ctx.body = {\n count: 0,\n message: 'No images without metadata found',\n };\n return;\n }\n\n // Create job\n const jobService = getService('aiMetadataJobs');\n const jobId = await jobService.createJob();\n\n // Start async processing (fire and forget)\n aiMetadataService.processExistingFiles(jobId, ctx.state.user).catch((err: Error) => {\n strapi.log.error('AI metadata job failed:', err);\n });\n\n // Return immediately with job ID\n ctx.body = {\n jobId,\n status: 'pending',\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to generate AI metadata';\n const cause = error instanceof Error && error.cause ? String(error.cause) : undefined;\n\n strapi.log.error('AI metadata generation failed in controller', {\n message,\n cause,\n error,\n });\n\n ctx.badRequest(cause ? `${message}: ${cause}` : message);\n }\n },\n\n /**\n * `POST /upload/actions/generate-ai-metadata`\n *\n * Generate AI metadata for the explicit selection of files in the body,\n * synchronously: the request resolves once every file has been processed and\n * reports the outcome per file, so a single bad id or a non-image in the\n * selection never fails the whole batch.\n *\n * For the whole-library backfill, see `createAIMetadataJob`.\n */\n async generateAIMetadata(ctx: Context) {\n const { userAbility } = ctx.state;\n const { body } = ctx.request;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const { fileIds } = await validateGenerateAIMetadataBody(body);\n\n const aiMetadataService = getService('aiMetadata');\n\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n const results = await aiMetadataService.generateForFiles(fileIds, ctx.state.user);\n\n ctx.body = { data: results };\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/latest`\n *\n * The most recent still-active backfill job, so a client that reloads mid-run\n * can pick the progress bar back up. 404s when nothing is running.\n */\n async getLatestAIMetadataJob(ctx: Context) {\n if ((await getService('aiMetadata').isEnabled()) === false) {\n return ctx.notFound();\n }\n\n const jobService = getService('aiMetadataJobs');\n const job = await jobService.getLatestActiveJob();\n\n if (!job) {\n return ctx.notFound('No active job found');\n }\n\n ctx.body = job;\n },\n};\n"],"names":["find","ctx","state","userAbility","defaultQuery","populate","folder","pm","strapi","service","createPermissionsManager","ability","action","ACTIONS","read","model","FILE_MODEL_UID","isAllowed","forbidden","validateQuery","query","async","pipe","q","sanitizeQuery","merge","addPermissionsQueryTo","results","files","pagination","getService","findPage","signedFiles","map","signFileUrls","sanitizedFiles","sanitizeOutput","findOne","params","id","file","findEntityAndCheckPermissions","signedFile","body","destroy","update","Promise","all","remove","getAIMetadataPendingCount","aiMetadataService","isEnabled","badRequest","imagesWithoutMetadataCount","totalImages","countImagesWithoutMetadata","error","message","Error","log","createAIMetadataJob","result","count","jobService","jobId","createJob","processExistingFiles","user","catch","err","status","cause","String","undefined","generateAIMetadata","request","fileIds","validateGenerateAIMetadataBody","generateForFiles","data","getLatestAIMetadataJob","notFound","job","getLatestActiveJob"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin-file.js","sources":["../../../server/src/controllers/admin-file.ts"],"sourcesContent":["import { merge } from 'lodash';\nimport { async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { validateGenerateAIMetadataBody } from './validation/admin/ai-metadata';\n\nexport default {\n async find(ctx: Context) {\n const {\n state: { userAbility },\n } = ctx;\n\n const defaultQuery = { populate: { folder: true } };\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // validate the incoming user query params\n await pm.validateQuery(ctx.query);\n\n const query = await async.pipe(\n // Start by sanitizing the incoming query\n (q) => pm.sanitizeQuery(q),\n // Add the default query which should not be validated or sanitized\n (q) => merge({}, defaultQuery, q),\n // Add the dynamic filters based on permissions' conditions\n (q) => pm.addPermissionsQueryTo(q)\n )(ctx.query);\n\n const { results: files, pagination } = await getService('upload').findPage(query);\n\n // Sign file urls for private providers\n const signedFiles = await async.map(files, getService('file').signFileUrls);\n\n const sanitizedFiles = await pm.sanitizeOutput(signedFiles);\n\n return { results: sanitizedFiles, pagination };\n },\n\n async findOne(ctx: Context) {\n const {\n state: { userAbility },\n params: { id },\n } = ctx;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.read,\n FILE_MODEL_UID,\n id\n );\n\n const signedFile = await getService('file').signFileUrls(file);\n ctx.body = await pm.sanitizeOutput(signedFile);\n },\n\n async destroy(ctx: Context) {\n const { id } = ctx.params;\n const { userAbility } = ctx.state;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const [body] = await Promise.all([\n pm.sanitizeOutput(file, { action: ACTIONS.read }),\n getService('upload').remove(file),\n ]);\n\n ctx.body = body;\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/pending-count`\n *\n * How many images a backfill job would have to process, i.e. those still\n * missing AI metadata, alongside the total image count.\n */\n async getAIMetadataPendingCount(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n const { imagesWithoutMetadataCount, totalImages } =\n await aiMetadataService.countImagesWithoutMetadata();\n\n ctx.body = {\n imagesWithoutMetadataCount,\n totalImages,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to get AI metadata count';\n\n strapi.log.error('Failed to get AI metadata count', {\n message,\n error,\n });\n\n ctx.badRequest(message);\n }\n },\n\n /**\n * `POST /upload/ai-metadata-jobs`\n *\n * Create a backfill job that generates AI metadata for every image still\n * missing it, and return the job so the client can poll it. Processing runs\n * detached from this request; see `generateAIMetadata` for the synchronous\n * counterpart that works on an explicit selection.\n */\n async createAIMetadataJob(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n // Get count first to check if there are images to process\n const result = await aiMetadataService.countImagesWithoutMetadata();\n\n if (result.imagesWithoutMetadataCount === 0) {\n ctx.body = {\n count: 0,\n message: 'No images without metadata found',\n };\n return;\n }\n\n // Create job\n const jobService = getService('aiMetadataJobs');\n const jobId = await jobService.createJob();\n\n // Start async processing (fire and forget)\n aiMetadataService.processExistingFiles(jobId, ctx.state.user).catch((err: Error) => {\n strapi.log.error('AI metadata job failed:', err);\n });\n\n // Return immediately with job ID\n ctx.body = {\n jobId,\n status: 'pending',\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to generate AI metadata';\n const cause = error instanceof Error && error.cause ? String(error.cause) : undefined;\n\n strapi.log.error('AI metadata generation failed in controller', {\n message,\n cause,\n error,\n });\n\n ctx.badRequest(cause ? `${message}: ${cause}` : message);\n }\n },\n\n /**\n * `POST /upload/actions/generate-ai-metadata`\n *\n * Generate AI metadata for the explicit selection of files in the body,\n * synchronously: the request resolves once every file has been processed and\n * reports the outcome per file, so a single bad id or a non-image in the\n * selection never fails the whole batch.\n *\n * For the whole-library backfill, see `createAIMetadataJob`.\n */\n async generateAIMetadata(ctx: Context) {\n const { userAbility } = ctx.state;\n const { body } = ctx.request;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const { fileIds } = await validateGenerateAIMetadataBody(body);\n\n const aiMetadataService = getService('aiMetadata');\n\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n const results = await aiMetadataService.generateForFiles(fileIds, ctx.state.user);\n\n ctx.body = { data: results };\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/latest`\n *\n * The most recent still-active backfill job, so a client that reloads mid-run\n * can pick the progress bar back up. 404s when nothing is running.\n */\n async getLatestAIMetadataJob(ctx: Context) {\n if ((await getService('aiMetadata').isEnabled()) === false) {\n return ctx.notFound();\n }\n\n const jobService = getService('aiMetadataJobs');\n const job = await jobService.getLatestActiveJob();\n\n if (!job) {\n return ctx.notFound('No active job found');\n }\n\n ctx.body = job;\n },\n};\n"],"names":["find","ctx","state","userAbility","defaultQuery","populate","folder","pm","strapi","service","createPermissionsManager","ability","action","ACTIONS","read","model","FILE_MODEL_UID","isAllowed","forbidden","validateQuery","query","async","pipe","q","sanitizeQuery","merge","addPermissionsQueryTo","results","files","pagination","getService","findPage","signedFiles","map","signFileUrls","sanitizedFiles","sanitizeOutput","findOne","params","id","file","findEntityAndCheckPermissions","signedFile","body","destroy","update","Promise","all","remove","getAIMetadataPendingCount","aiMetadataService","isEnabled","badRequest","imagesWithoutMetadataCount","totalImages","countImagesWithoutMetadata","error","message","Error","log","createAIMetadataJob","result","count","jobService","jobId","createJob","processExistingFiles","user","catch","err","status","cause","String","undefined","generateAIMetadata","request","fileIds","validateGenerateAIMetadataBody","generateForFiles","data","getLatestAIMetadataJob","notFound","job","getLatestActiveJob"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { merge } from 'lodash
|
|
1
|
+
import { merge } from 'lodash';
|
|
2
2
|
import { async } from '@strapi/utils';
|
|
3
3
|
import { getService } from '../utils/index.mjs';
|
|
4
4
|
import { FILE_MODEL_UID, ACTIONS } from '../constants.mjs';
|
|
@@ -25,7 +25,7 @@ var adminFile = {
|
|
|
25
25
|
await pm.validateQuery(ctx.query);
|
|
26
26
|
const query = await async.pipe(// Start by sanitizing the incoming query
|
|
27
27
|
(q)=>pm.sanitizeQuery(q), // Add the default query which should not be validated or sanitized
|
|
28
|
-
(q)=>merge(defaultQuery, q), // Add the dynamic filters based on permissions' conditions
|
|
28
|
+
(q)=>merge({}, defaultQuery, q), // Add the dynamic filters based on permissions' conditions
|
|
29
29
|
(q)=>pm.addPermissionsQueryTo(q))(ctx.query);
|
|
30
30
|
const { results: files, pagination } = await getService('upload').findPage(query);
|
|
31
31
|
// Sign file urls for private providers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-file.mjs","sources":["../../../server/src/controllers/admin-file.ts"],"sourcesContent":["import { merge } from 'lodash/fp';\nimport { async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { validateGenerateAIMetadataBody } from './validation/admin/ai-metadata';\n\nexport default {\n async find(ctx: Context) {\n const {\n state: { userAbility },\n } = ctx;\n\n const defaultQuery = { populate: { folder: true } };\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // validate the incoming user query params\n await pm.validateQuery(ctx.query);\n\n const query = await async.pipe(\n // Start by sanitizing the incoming query\n (q) => pm.sanitizeQuery(q),\n // Add the default query which should not be validated or sanitized\n (q) => merge(defaultQuery, q),\n // Add the dynamic filters based on permissions' conditions\n (q) => pm.addPermissionsQueryTo(q)\n )(ctx.query);\n\n const { results: files, pagination } = await getService('upload').findPage(query);\n\n // Sign file urls for private providers\n const signedFiles = await async.map(files, getService('file').signFileUrls);\n\n const sanitizedFiles = await pm.sanitizeOutput(signedFiles);\n\n return { results: sanitizedFiles, pagination };\n },\n\n async findOne(ctx: Context) {\n const {\n state: { userAbility },\n params: { id },\n } = ctx;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.read,\n FILE_MODEL_UID,\n id\n );\n\n const signedFile = await getService('file').signFileUrls(file);\n ctx.body = await pm.sanitizeOutput(signedFile);\n },\n\n async destroy(ctx: Context) {\n const { id } = ctx.params;\n const { userAbility } = ctx.state;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const [body] = await Promise.all([\n pm.sanitizeOutput(file, { action: ACTIONS.read }),\n getService('upload').remove(file),\n ]);\n\n ctx.body = body;\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/pending-count`\n *\n * How many images a backfill job would have to process, i.e. those still\n * missing AI metadata, alongside the total image count.\n */\n async getAIMetadataPendingCount(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n const { imagesWithoutMetadataCount, totalImages } =\n await aiMetadataService.countImagesWithoutMetadata();\n\n ctx.body = {\n imagesWithoutMetadataCount,\n totalImages,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to get AI metadata count';\n\n strapi.log.error('Failed to get AI metadata count', {\n message,\n error,\n });\n\n ctx.badRequest(message);\n }\n },\n\n /**\n * `POST /upload/ai-metadata-jobs`\n *\n * Create a backfill job that generates AI metadata for every image still\n * missing it, and return the job so the client can poll it. Processing runs\n * detached from this request; see `generateAIMetadata` for the synchronous\n * counterpart that works on an explicit selection.\n */\n async createAIMetadataJob(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n // Get count first to check if there are images to process\n const result = await aiMetadataService.countImagesWithoutMetadata();\n\n if (result.imagesWithoutMetadataCount === 0) {\n ctx.body = {\n count: 0,\n message: 'No images without metadata found',\n };\n return;\n }\n\n // Create job\n const jobService = getService('aiMetadataJobs');\n const jobId = await jobService.createJob();\n\n // Start async processing (fire and forget)\n aiMetadataService.processExistingFiles(jobId, ctx.state.user).catch((err: Error) => {\n strapi.log.error('AI metadata job failed:', err);\n });\n\n // Return immediately with job ID\n ctx.body = {\n jobId,\n status: 'pending',\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to generate AI metadata';\n const cause = error instanceof Error && error.cause ? String(error.cause) : undefined;\n\n strapi.log.error('AI metadata generation failed in controller', {\n message,\n cause,\n error,\n });\n\n ctx.badRequest(cause ? `${message}: ${cause}` : message);\n }\n },\n\n /**\n * `POST /upload/actions/generate-ai-metadata`\n *\n * Generate AI metadata for the explicit selection of files in the body,\n * synchronously: the request resolves once every file has been processed and\n * reports the outcome per file, so a single bad id or a non-image in the\n * selection never fails the whole batch.\n *\n * For the whole-library backfill, see `createAIMetadataJob`.\n */\n async generateAIMetadata(ctx: Context) {\n const { userAbility } = ctx.state;\n const { body } = ctx.request;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const { fileIds } = await validateGenerateAIMetadataBody(body);\n\n const aiMetadataService = getService('aiMetadata');\n\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n const results = await aiMetadataService.generateForFiles(fileIds, ctx.state.user);\n\n ctx.body = { data: results };\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/latest`\n *\n * The most recent still-active backfill job, so a client that reloads mid-run\n * can pick the progress bar back up. 404s when nothing is running.\n */\n async getLatestAIMetadataJob(ctx: Context) {\n if ((await getService('aiMetadata').isEnabled()) === false) {\n return ctx.notFound();\n }\n\n const jobService = getService('aiMetadataJobs');\n const job = await jobService.getLatestActiveJob();\n\n if (!job) {\n return ctx.notFound('No active job found');\n }\n\n ctx.body = job;\n },\n};\n"],"names":["find","ctx","state","userAbility","defaultQuery","populate","folder","pm","strapi","service","createPermissionsManager","ability","action","ACTIONS","read","model","FILE_MODEL_UID","isAllowed","forbidden","validateQuery","query","async","pipe","q","sanitizeQuery","merge","addPermissionsQueryTo","results","files","pagination","getService","findPage","signedFiles","map","signFileUrls","sanitizedFiles","sanitizeOutput","findOne","params","id","file","findEntityAndCheckPermissions","signedFile","body","destroy","update","Promise","all","remove","getAIMetadataPendingCount","aiMetadataService","isEnabled","badRequest","imagesWithoutMetadataCount","totalImages","countImagesWithoutMetadata","error","message","Error","log","createAIMetadataJob","result","count","jobService","jobId","createJob","processExistingFiles","user","catch","err","status","cause","String","undefined","generateAIMetadata","request","fileIds","validateGenerateAIMetadataBody","generateForFiles","data","getLatestAIMetadataJob","notFound","job","getLatestActiveJob"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin-file.mjs","sources":["../../../server/src/controllers/admin-file.ts"],"sourcesContent":["import { merge } from 'lodash';\nimport { async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { validateGenerateAIMetadataBody } from './validation/admin/ai-metadata';\n\nexport default {\n async find(ctx: Context) {\n const {\n state: { userAbility },\n } = ctx;\n\n const defaultQuery = { populate: { folder: true } };\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // validate the incoming user query params\n await pm.validateQuery(ctx.query);\n\n const query = await async.pipe(\n // Start by sanitizing the incoming query\n (q) => pm.sanitizeQuery(q),\n // Add the default query which should not be validated or sanitized\n (q) => merge({}, defaultQuery, q),\n // Add the dynamic filters based on permissions' conditions\n (q) => pm.addPermissionsQueryTo(q)\n )(ctx.query);\n\n const { results: files, pagination } = await getService('upload').findPage(query);\n\n // Sign file urls for private providers\n const signedFiles = await async.map(files, getService('file').signFileUrls);\n\n const sanitizedFiles = await pm.sanitizeOutput(signedFiles);\n\n return { results: sanitizedFiles, pagination };\n },\n\n async findOne(ctx: Context) {\n const {\n state: { userAbility },\n params: { id },\n } = ctx;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.read,\n FILE_MODEL_UID,\n id\n );\n\n const signedFile = await getService('file').signFileUrls(file);\n ctx.body = await pm.sanitizeOutput(signedFile);\n },\n\n async destroy(ctx: Context) {\n const { id } = ctx.params;\n const { userAbility } = ctx.state;\n\n const { pm, file } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const [body] = await Promise.all([\n pm.sanitizeOutput(file, { action: ACTIONS.read }),\n getService('upload').remove(file),\n ]);\n\n ctx.body = body;\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/pending-count`\n *\n * How many images a backfill job would have to process, i.e. those still\n * missing AI metadata, alongside the total image count.\n */\n async getAIMetadataPendingCount(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n const { imagesWithoutMetadataCount, totalImages } =\n await aiMetadataService.countImagesWithoutMetadata();\n\n ctx.body = {\n imagesWithoutMetadataCount,\n totalImages,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to get AI metadata count';\n\n strapi.log.error('Failed to get AI metadata count', {\n message,\n error,\n });\n\n ctx.badRequest(message);\n }\n },\n\n /**\n * `POST /upload/ai-metadata-jobs`\n *\n * Create a backfill job that generates AI metadata for every image still\n * missing it, and return the job so the client can poll it. Processing runs\n * detached from this request; see `generateAIMetadata` for the synchronous\n * counterpart that works on an explicit selection.\n */\n async createAIMetadataJob(ctx: Context) {\n const { userAbility } = ctx.state;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // Check if AI service is enabled\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n try {\n // Get count first to check if there are images to process\n const result = await aiMetadataService.countImagesWithoutMetadata();\n\n if (result.imagesWithoutMetadataCount === 0) {\n ctx.body = {\n count: 0,\n message: 'No images without metadata found',\n };\n return;\n }\n\n // Create job\n const jobService = getService('aiMetadataJobs');\n const jobId = await jobService.createJob();\n\n // Start async processing (fire and forget)\n aiMetadataService.processExistingFiles(jobId, ctx.state.user).catch((err: Error) => {\n strapi.log.error('AI metadata job failed:', err);\n });\n\n // Return immediately with job ID\n ctx.body = {\n jobId,\n status: 'pending',\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Failed to generate AI metadata';\n const cause = error instanceof Error && error.cause ? String(error.cause) : undefined;\n\n strapi.log.error('AI metadata generation failed in controller', {\n message,\n cause,\n error,\n });\n\n ctx.badRequest(cause ? `${message}: ${cause}` : message);\n }\n },\n\n /**\n * `POST /upload/actions/generate-ai-metadata`\n *\n * Generate AI metadata for the explicit selection of files in the body,\n * synchronously: the request resolves once every file has been processed and\n * reports the outcome per file, so a single bad id or a non-image in the\n * selection never fails the whole batch.\n *\n * For the whole-library backfill, see `createAIMetadataJob`.\n */\n async generateAIMetadata(ctx: Context) {\n const { userAbility } = ctx.state;\n const { body } = ctx.request;\n\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const { fileIds } = await validateGenerateAIMetadataBody(body);\n\n const aiMetadataService = getService('aiMetadata');\n\n if (!(await aiMetadataService.isEnabled())) {\n return ctx.badRequest('AI Metadata service is not enabled');\n }\n\n const results = await aiMetadataService.generateForFiles(fileIds, ctx.state.user);\n\n ctx.body = { data: results };\n },\n\n /**\n * `GET /upload/ai-metadata-jobs/latest`\n *\n * The most recent still-active backfill job, so a client that reloads mid-run\n * can pick the progress bar back up. 404s when nothing is running.\n */\n async getLatestAIMetadataJob(ctx: Context) {\n if ((await getService('aiMetadata').isEnabled()) === false) {\n return ctx.notFound();\n }\n\n const jobService = getService('aiMetadataJobs');\n const job = await jobService.getLatestActiveJob();\n\n if (!job) {\n return ctx.notFound('No active job found');\n }\n\n ctx.body = job;\n },\n};\n"],"names":["find","ctx","state","userAbility","defaultQuery","populate","folder","pm","strapi","service","createPermissionsManager","ability","action","ACTIONS","read","model","FILE_MODEL_UID","isAllowed","forbidden","validateQuery","query","async","pipe","q","sanitizeQuery","merge","addPermissionsQueryTo","results","files","pagination","getService","findPage","signedFiles","map","signFileUrls","sanitizedFiles","sanitizeOutput","findOne","params","id","file","findEntityAndCheckPermissions","signedFile","body","destroy","update","Promise","all","remove","getAIMetadataPendingCount","aiMetadataService","isEnabled","badRequest","imagesWithoutMetadataCount","totalImages","countImagesWithoutMetadata","error","message","Error","log","createAIMetadataJob","result","count","jobService","jobId","createJob","processExistingFiles","user","catch","err","status","cause","String","undefined","generateAIMetadata","request","fileIds","validateGenerateAIMetadataBody","generateForFiles","data","getLatestAIMetadataJob","notFound","job","getLatestActiveJob"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var utils = require('@strapi/utils');
|
|
4
4
|
var index = require('../utils/index.js');
|
|
5
5
|
var constants = require('../constants.js');
|
|
6
|
+
var errors = require('../errors.js');
|
|
6
7
|
var folderFile = require('./validation/admin/folder-file.js');
|
|
7
8
|
|
|
8
9
|
var adminFolderFile = {
|
|
@@ -15,14 +16,58 @@ var adminFolderFile = {
|
|
|
15
16
|
});
|
|
16
17
|
const pmFile = strapi.service('admin::permission').createPermissionsManager({
|
|
17
18
|
ability: userAbility,
|
|
18
|
-
action: constants.ACTIONS.
|
|
19
|
+
action: constants.ACTIONS.update,
|
|
19
20
|
model: constants.FILE_MODEL_UID
|
|
20
21
|
});
|
|
21
22
|
await folderFile.validateDeleteManyFoldersFiles(body);
|
|
22
23
|
const fileService = index.getService('file');
|
|
23
24
|
const folderService = index.getService('folder');
|
|
24
|
-
const
|
|
25
|
-
const
|
|
25
|
+
const { fileIds = [], folderIds = [] } = body;
|
|
26
|
+
const canDeleteFiles = async (files)=>{
|
|
27
|
+
const ids = [
|
|
28
|
+
...new Set(files.map(({ id })=>id))
|
|
29
|
+
];
|
|
30
|
+
if (ids.length === 0) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const query = pmFile.addPermissionsQueryTo({
|
|
34
|
+
filters: {
|
|
35
|
+
id: {
|
|
36
|
+
$in: ids
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const permittedFiles = await strapi.db.query(constants.FILE_MODEL_UID).findMany({
|
|
41
|
+
select: [
|
|
42
|
+
'id'
|
|
43
|
+
],
|
|
44
|
+
where: query.filters
|
|
45
|
+
});
|
|
46
|
+
return permittedFiles.length === ids.length;
|
|
47
|
+
};
|
|
48
|
+
// https://github.com/strapi/strapi/issues/27649
|
|
49
|
+
// Validate explicitly selected files before deleting a folder. Otherwise a mixed request
|
|
50
|
+
// could delete permitted files first and only then discover a forbidden file in the folder.
|
|
51
|
+
if (fileIds.length > 0) {
|
|
52
|
+
const selectedFiles = await strapi.db.query(constants.FILE_MODEL_UID).findMany({
|
|
53
|
+
where: {
|
|
54
|
+
id: {
|
|
55
|
+
$in: fileIds
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (!await canDeleteFiles(selectedFiles)) {
|
|
60
|
+
throw new utils.errors.ForbiddenError();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const { folders: deletedFolders, totalFolderNumber, totalFileNumber } = await folderService.deleteByIds(folderIds, {
|
|
64
|
+
async validateFiles (files) {
|
|
65
|
+
if (!await canDeleteFiles(files)) {
|
|
66
|
+
throw new errors.FolderContainsUnauthorizedAssetsError();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const deletedFiles = await fileService.deleteByIds(fileIds);
|
|
26
71
|
if (deletedFiles.length + deletedFolders.length > 1) {
|
|
27
72
|
await index.getService('metrics').trackUsage('didBulkDeleteMediaLibraryElements', {
|
|
28
73
|
eventProperties: {
|
|
@@ -35,7 +80,9 @@ var adminFolderFile = {
|
|
|
35
80
|
}
|
|
36
81
|
ctx.body = {
|
|
37
82
|
data: {
|
|
38
|
-
files: await pmFile.sanitizeOutput(deletedFiles
|
|
83
|
+
files: await pmFile.sanitizeOutput(deletedFiles, {
|
|
84
|
+
action: constants.ACTIONS.read
|
|
85
|
+
}),
|
|
39
86
|
folders: await pmFolder.sanitizeOutput(deletedFolders)
|
|
40
87
|
}
|
|
41
88
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-folder-file.js","sources":["../../../server/src/controllers/admin-folder-file.ts"],"sourcesContent":["import { strings } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\nimport {\n validateDeleteManyFoldersFiles,\n validateMoveManyFoldersFiles,\n} from './validation/admin/folder-file';\n\nimport type { File, Folder } from '../types';\n\nexport default {\n async deleteMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n await validateDeleteManyFoldersFiles(body);\n\n const fileService = getService('file');\n const folderService = getService('folder');\n\n const deletedFiles = await fileService.deleteByIds(body.fileIds);\n const {\n folders: deletedFolders,\n totalFolderNumber,\n totalFileNumber,\n } = await folderService.deleteByIds(body.folderIds);\n\n if (deletedFiles.length + deletedFolders.length > 1) {\n await getService('metrics').trackUsage('didBulkDeleteMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: deletedFolders.length,\n rootAssetNumber: deletedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + deletedFiles.length,\n },\n });\n }\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(deletedFiles),\n folders: await pmFolder.sanitizeOutput(deletedFolders),\n },\n };\n },\n async moveMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n await validateMoveManyFoldersFiles(body);\n const { folderIds = [], fileIds = [], destinationFolderId } = body;\n\n let totalFolderNumber = 0;\n let totalFileNumber = 0;\n\n const trx = await strapi.db.transaction();\n try {\n // fetch folders\n const existingFolders = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select(['id', 'pathId', 'path'])\n .where({ id: { $in: folderIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<Folder[]>();\n\n // fetch files\n const existingFiles = await strapi.db\n .queryBuilder(FILE_MODEL_UID)\n .select(['id'])\n .where({ id: { $in: fileIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<File[]>();\n\n // fetch destinationFolder path\n let destinationFolderPath = '/';\n if (destinationFolderId !== null) {\n const destinationFolder = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select('path')\n .where({ id: destinationFolderId })\n .transacting(trx.get())\n .first()\n .execute<Folder>();\n destinationFolderPath = destinationFolder.path;\n }\n\n const fileTable = strapi.getModel(FILE_MODEL_UID).collectionName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n const folderPathColName =\n // @ts-expect-error - no dynamic typings for the models\n strapi.db.metadata.get(FILE_MODEL_UID).attributes.folderPath.columnName;\n // @ts-expect-error - no dynamic typings for the models\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n\n if (existingFolders.length > 0) {\n // update folders' parent relation\n // @ts-expect-error - no dynamic typings for the models\n const { joinTable } = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.parent;\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [joinTable.joinColumn.name]: { $in: folderIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .insert(\n existingFolders.map((folder) => ({\n [joinTable.inverseJoinColumn.name]: destinationFolderId,\n [joinTable.joinColumn.name]: folder.id,\n }))\n )\n .execute();\n }\n\n for (const existingFolder of existingFolders) {\n let replaceQuery;\n switch (strapi.db.dialect.client) {\n case 'sqlite':\n replaceQuery = '? || SUBSTRING(??, ?)';\n break;\n case 'postgres':\n replaceQuery = 'CONCAT(?::TEXT, SUBSTRING(??, ?::INTEGER))';\n break;\n default:\n replaceQuery = 'CONCAT(?, SUBSTRING(??, ?))';\n }\n\n // update path for folders themselves & folders below\n totalFolderNumber = await strapi.db\n .getConnection(folderTable)\n .transacting(trx.get())\n .where(pathColName, existingFolder.path)\n .orWhere(pathColName, 'like', `${existingFolder.path}/%`)\n .update(\n pathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n pathColName,\n existingFolder.path.length + 1,\n ])\n );\n\n // update path of files below\n totalFileNumber = await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .where(folderPathColName, existingFolder.path)\n .orWhere(folderPathColName, 'like', `${existingFolder.path}/%`)\n .update(\n folderPathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n folderPathColName,\n existingFolder.path.length + 1,\n ])\n );\n }\n }\n\n if (existingFiles.length > 0) {\n // update files' folder relation (delete + insert; upsert not possible)\n // @ts-expect-error - no dynamic typings for the models\n const fileJoinTable = strapi.db.metadata.get(FILE_MODEL_UID).attributes.folder.joinTable;\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [fileJoinTable.joinColumn.name]: { $in: fileIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .insert(\n existingFiles.map((file) => ({\n [fileJoinTable.inverseJoinColumn.name]: destinationFolderId,\n [fileJoinTable.joinColumn.name]: file.id,\n }))\n )\n .execute();\n }\n\n // update files main fields (path + updatedBy)\n await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .whereIn('id', fileIds)\n .update(folderPathColName, destinationFolderPath);\n }\n\n await trx.commit();\n } catch (e) {\n await trx.rollback();\n throw e;\n }\n\n const updatedFolders = await strapi.db.query(FOLDER_MODEL_UID).findMany({\n where: { id: { $in: folderIds } },\n });\n\n const updatedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n where: { id: { $in: fileIds } },\n });\n\n await getService('metrics').trackUsage('didBulkMoveMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: updatedFolders.length,\n rootAssetNumber: updatedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + updatedFiles.length,\n },\n });\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(updatedFiles),\n folders: await pmFolder.sanitizeOutput(updatedFolders),\n },\n };\n },\n};\n"],"names":["deleteMany","ctx","body","request","state","userAbility","pmFolder","strapi","service","createPermissionsManager","ability","model","FOLDER_MODEL_UID","pmFile","action","ACTIONS","read","FILE_MODEL_UID","validateDeleteManyFoldersFiles","fileService","getService","folderService","deletedFiles","deleteByIds","fileIds","folders","deletedFolders","totalFolderNumber","totalFileNumber","folderIds","length","trackUsage","eventProperties","rootFolderNumber","rootAssetNumber","totalAssetNumber","data","files","sanitizeOutput","moveMany","validateMoveManyFoldersFiles","destinationFolderId","trx","db","transaction","existingFolders","queryBuilder","select","where","id","$in","transacting","get","forUpdate","execute","existingFiles","destinationFolderPath","destinationFolder","first","path","fileTable","getModel","collectionName","folderTable","folderPathColName","metadata","attributes","folderPath","columnName","pathColName","joinTable","parent","name","delete","joinColumn","insert","map","folder","inverseJoinColumn","existingFolder","replaceQuery","dialect","client","getConnection","orWhere","update","connection","raw","strings","joinBy","pathId","fileJoinTable","file","whereIn","commit","e","rollback","updatedFolders","query","findMany","updatedFiles"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin-folder-file.js","sources":["../../../server/src/controllers/admin-folder-file.ts"],"sourcesContent":["import { errors, strings } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\nimport { FolderContainsUnauthorizedAssetsError } from '../errors';\nimport {\n validateDeleteManyFoldersFiles,\n validateMoveManyFoldersFiles,\n} from './validation/admin/folder-file';\n\nimport type { File, Folder } from '../types';\n\nexport default {\n async deleteMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n await validateDeleteManyFoldersFiles(body);\n\n const fileService = getService('file');\n const folderService = getService('folder');\n\n const { fileIds = [], folderIds = [] } = body;\n\n const canDeleteFiles = async (files: File[]) => {\n const ids = [...new Set(files.map(({ id }) => id))];\n\n if (ids.length === 0) {\n return true;\n }\n\n const query = pmFile.addPermissionsQueryTo({ filters: { id: { $in: ids } } });\n const permittedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n select: ['id'],\n where: query.filters,\n });\n\n return permittedFiles.length === ids.length;\n };\n\n // https://github.com/strapi/strapi/issues/27649\n // Validate explicitly selected files before deleting a folder. Otherwise a mixed request\n // could delete permitted files first and only then discover a forbidden file in the folder.\n if (fileIds.length > 0) {\n const selectedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n where: { id: { $in: fileIds } },\n });\n\n if (!(await canDeleteFiles(selectedFiles))) {\n throw new errors.ForbiddenError();\n }\n }\n\n const {\n folders: deletedFolders,\n totalFolderNumber,\n totalFileNumber,\n } = await folderService.deleteByIds(folderIds, {\n async validateFiles(files) {\n if (!(await canDeleteFiles(files))) {\n throw new FolderContainsUnauthorizedAssetsError();\n }\n },\n });\n const deletedFiles = await fileService.deleteByIds(fileIds);\n\n if (deletedFiles.length + deletedFolders.length > 1) {\n await getService('metrics').trackUsage('didBulkDeleteMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: deletedFolders.length,\n rootAssetNumber: deletedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + deletedFiles.length,\n },\n });\n }\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(deletedFiles, { action: ACTIONS.read }),\n folders: await pmFolder.sanitizeOutput(deletedFolders),\n },\n };\n },\n async moveMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n await validateMoveManyFoldersFiles(body);\n const { folderIds = [], fileIds = [], destinationFolderId } = body;\n\n let totalFolderNumber = 0;\n let totalFileNumber = 0;\n\n const trx = await strapi.db.transaction();\n try {\n // fetch folders\n const existingFolders = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select(['id', 'pathId', 'path'])\n .where({ id: { $in: folderIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<Folder[]>();\n\n // fetch files\n const existingFiles = await strapi.db\n .queryBuilder(FILE_MODEL_UID)\n .select(['id'])\n .where({ id: { $in: fileIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<File[]>();\n\n // fetch destinationFolder path\n let destinationFolderPath = '/';\n if (destinationFolderId !== null) {\n const destinationFolder = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select('path')\n .where({ id: destinationFolderId })\n .transacting(trx.get())\n .first()\n .execute<Folder>();\n destinationFolderPath = destinationFolder.path;\n }\n\n const fileTable = strapi.getModel(FILE_MODEL_UID).collectionName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n const folderPathColName =\n // @ts-expect-error - no dynamic typings for the models\n strapi.db.metadata.get(FILE_MODEL_UID).attributes.folderPath.columnName;\n // @ts-expect-error - no dynamic typings for the models\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n\n if (existingFolders.length > 0) {\n // update folders' parent relation\n // @ts-expect-error - no dynamic typings for the models\n const { joinTable } = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.parent;\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [joinTable.joinColumn.name]: { $in: folderIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .insert(\n existingFolders.map((folder) => ({\n [joinTable.inverseJoinColumn.name]: destinationFolderId,\n [joinTable.joinColumn.name]: folder.id,\n }))\n )\n .execute();\n }\n\n for (const existingFolder of existingFolders) {\n let replaceQuery;\n switch (strapi.db.dialect.client) {\n case 'sqlite':\n replaceQuery = '? || SUBSTRING(??, ?)';\n break;\n case 'postgres':\n replaceQuery = 'CONCAT(?::TEXT, SUBSTRING(??, ?::INTEGER))';\n break;\n default:\n replaceQuery = 'CONCAT(?, SUBSTRING(??, ?))';\n }\n\n // update path for folders themselves & folders below\n totalFolderNumber = await strapi.db\n .getConnection(folderTable)\n .transacting(trx.get())\n .where(pathColName, existingFolder.path)\n .orWhere(pathColName, 'like', `${existingFolder.path}/%`)\n .update(\n pathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n pathColName,\n existingFolder.path.length + 1,\n ])\n );\n\n // update path of files below\n totalFileNumber = await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .where(folderPathColName, existingFolder.path)\n .orWhere(folderPathColName, 'like', `${existingFolder.path}/%`)\n .update(\n folderPathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n folderPathColName,\n existingFolder.path.length + 1,\n ])\n );\n }\n }\n\n if (existingFiles.length > 0) {\n // update files' folder relation (delete + insert; upsert not possible)\n // @ts-expect-error - no dynamic typings for the models\n const fileJoinTable = strapi.db.metadata.get(FILE_MODEL_UID).attributes.folder.joinTable;\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [fileJoinTable.joinColumn.name]: { $in: fileIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .insert(\n existingFiles.map((file) => ({\n [fileJoinTable.inverseJoinColumn.name]: destinationFolderId,\n [fileJoinTable.joinColumn.name]: file.id,\n }))\n )\n .execute();\n }\n\n // update files main fields (path + updatedBy)\n await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .whereIn('id', fileIds)\n .update(folderPathColName, destinationFolderPath);\n }\n\n await trx.commit();\n } catch (e) {\n await trx.rollback();\n throw e;\n }\n\n const updatedFolders = await strapi.db.query(FOLDER_MODEL_UID).findMany({\n where: { id: { $in: folderIds } },\n });\n\n const updatedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n where: { id: { $in: fileIds } },\n });\n\n await getService('metrics').trackUsage('didBulkMoveMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: updatedFolders.length,\n rootAssetNumber: updatedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + updatedFiles.length,\n },\n });\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(updatedFiles),\n folders: await pmFolder.sanitizeOutput(updatedFolders),\n },\n };\n },\n};\n"],"names":["deleteMany","ctx","body","request","state","userAbility","pmFolder","strapi","service","createPermissionsManager","ability","model","FOLDER_MODEL_UID","pmFile","action","ACTIONS","update","FILE_MODEL_UID","validateDeleteManyFoldersFiles","fileService","getService","folderService","fileIds","folderIds","canDeleteFiles","files","ids","Set","map","id","length","query","addPermissionsQueryTo","filters","$in","permittedFiles","db","findMany","select","where","selectedFiles","errors","ForbiddenError","folders","deletedFolders","totalFolderNumber","totalFileNumber","deleteByIds","validateFiles","FolderContainsUnauthorizedAssetsError","deletedFiles","trackUsage","eventProperties","rootFolderNumber","rootAssetNumber","totalAssetNumber","data","sanitizeOutput","read","moveMany","validateMoveManyFoldersFiles","destinationFolderId","trx","transaction","existingFolders","queryBuilder","transacting","get","forUpdate","execute","existingFiles","destinationFolderPath","destinationFolder","first","path","fileTable","getModel","collectionName","folderTable","folderPathColName","metadata","attributes","folderPath","columnName","pathColName","joinTable","parent","name","delete","joinColumn","insert","folder","inverseJoinColumn","existingFolder","replaceQuery","dialect","client","getConnection","orWhere","connection","raw","strings","joinBy","pathId","fileJoinTable","file","whereIn","commit","e","rollback","updatedFolders","updatedFiles"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { strings } from '@strapi/utils';
|
|
1
|
+
import { strings, errors } from '@strapi/utils';
|
|
2
2
|
import { getService } from '../utils/index.mjs';
|
|
3
3
|
import { FOLDER_MODEL_UID, FILE_MODEL_UID, ACTIONS } from '../constants.mjs';
|
|
4
|
+
import { FolderContainsUnauthorizedAssetsError } from '../errors.mjs';
|
|
4
5
|
import { validateMoveManyFoldersFiles, validateDeleteManyFoldersFiles } from './validation/admin/folder-file.mjs';
|
|
5
6
|
|
|
6
7
|
var adminFolderFile = {
|
|
@@ -13,14 +14,58 @@ var adminFolderFile = {
|
|
|
13
14
|
});
|
|
14
15
|
const pmFile = strapi.service('admin::permission').createPermissionsManager({
|
|
15
16
|
ability: userAbility,
|
|
16
|
-
action: ACTIONS.
|
|
17
|
+
action: ACTIONS.update,
|
|
17
18
|
model: FILE_MODEL_UID
|
|
18
19
|
});
|
|
19
20
|
await validateDeleteManyFoldersFiles(body);
|
|
20
21
|
const fileService = getService('file');
|
|
21
22
|
const folderService = getService('folder');
|
|
22
|
-
const
|
|
23
|
-
const
|
|
23
|
+
const { fileIds = [], folderIds = [] } = body;
|
|
24
|
+
const canDeleteFiles = async (files)=>{
|
|
25
|
+
const ids = [
|
|
26
|
+
...new Set(files.map(({ id })=>id))
|
|
27
|
+
];
|
|
28
|
+
if (ids.length === 0) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
const query = pmFile.addPermissionsQueryTo({
|
|
32
|
+
filters: {
|
|
33
|
+
id: {
|
|
34
|
+
$in: ids
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const permittedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({
|
|
39
|
+
select: [
|
|
40
|
+
'id'
|
|
41
|
+
],
|
|
42
|
+
where: query.filters
|
|
43
|
+
});
|
|
44
|
+
return permittedFiles.length === ids.length;
|
|
45
|
+
};
|
|
46
|
+
// https://github.com/strapi/strapi/issues/27649
|
|
47
|
+
// Validate explicitly selected files before deleting a folder. Otherwise a mixed request
|
|
48
|
+
// could delete permitted files first and only then discover a forbidden file in the folder.
|
|
49
|
+
if (fileIds.length > 0) {
|
|
50
|
+
const selectedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({
|
|
51
|
+
where: {
|
|
52
|
+
id: {
|
|
53
|
+
$in: fileIds
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (!await canDeleteFiles(selectedFiles)) {
|
|
58
|
+
throw new errors.ForbiddenError();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const { folders: deletedFolders, totalFolderNumber, totalFileNumber } = await folderService.deleteByIds(folderIds, {
|
|
62
|
+
async validateFiles (files) {
|
|
63
|
+
if (!await canDeleteFiles(files)) {
|
|
64
|
+
throw new FolderContainsUnauthorizedAssetsError();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const deletedFiles = await fileService.deleteByIds(fileIds);
|
|
24
69
|
if (deletedFiles.length + deletedFolders.length > 1) {
|
|
25
70
|
await getService('metrics').trackUsage('didBulkDeleteMediaLibraryElements', {
|
|
26
71
|
eventProperties: {
|
|
@@ -33,7 +78,9 @@ var adminFolderFile = {
|
|
|
33
78
|
}
|
|
34
79
|
ctx.body = {
|
|
35
80
|
data: {
|
|
36
|
-
files: await pmFile.sanitizeOutput(deletedFiles
|
|
81
|
+
files: await pmFile.sanitizeOutput(deletedFiles, {
|
|
82
|
+
action: ACTIONS.read
|
|
83
|
+
}),
|
|
37
84
|
folders: await pmFolder.sanitizeOutput(deletedFolders)
|
|
38
85
|
}
|
|
39
86
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-folder-file.mjs","sources":["../../../server/src/controllers/admin-folder-file.ts"],"sourcesContent":["import { strings } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\nimport {\n validateDeleteManyFoldersFiles,\n validateMoveManyFoldersFiles,\n} from './validation/admin/folder-file';\n\nimport type { File, Folder } from '../types';\n\nexport default {\n async deleteMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n await validateDeleteManyFoldersFiles(body);\n\n const fileService = getService('file');\n const folderService = getService('folder');\n\n const deletedFiles = await fileService.deleteByIds(body.fileIds);\n const {\n folders: deletedFolders,\n totalFolderNumber,\n totalFileNumber,\n } = await folderService.deleteByIds(body.folderIds);\n\n if (deletedFiles.length + deletedFolders.length > 1) {\n await getService('metrics').trackUsage('didBulkDeleteMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: deletedFolders.length,\n rootAssetNumber: deletedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + deletedFiles.length,\n },\n });\n }\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(deletedFiles),\n folders: await pmFolder.sanitizeOutput(deletedFolders),\n },\n };\n },\n async moveMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n await validateMoveManyFoldersFiles(body);\n const { folderIds = [], fileIds = [], destinationFolderId } = body;\n\n let totalFolderNumber = 0;\n let totalFileNumber = 0;\n\n const trx = await strapi.db.transaction();\n try {\n // fetch folders\n const existingFolders = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select(['id', 'pathId', 'path'])\n .where({ id: { $in: folderIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<Folder[]>();\n\n // fetch files\n const existingFiles = await strapi.db\n .queryBuilder(FILE_MODEL_UID)\n .select(['id'])\n .where({ id: { $in: fileIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<File[]>();\n\n // fetch destinationFolder path\n let destinationFolderPath = '/';\n if (destinationFolderId !== null) {\n const destinationFolder = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select('path')\n .where({ id: destinationFolderId })\n .transacting(trx.get())\n .first()\n .execute<Folder>();\n destinationFolderPath = destinationFolder.path;\n }\n\n const fileTable = strapi.getModel(FILE_MODEL_UID).collectionName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n const folderPathColName =\n // @ts-expect-error - no dynamic typings for the models\n strapi.db.metadata.get(FILE_MODEL_UID).attributes.folderPath.columnName;\n // @ts-expect-error - no dynamic typings for the models\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n\n if (existingFolders.length > 0) {\n // update folders' parent relation\n // @ts-expect-error - no dynamic typings for the models\n const { joinTable } = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.parent;\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [joinTable.joinColumn.name]: { $in: folderIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .insert(\n existingFolders.map((folder) => ({\n [joinTable.inverseJoinColumn.name]: destinationFolderId,\n [joinTable.joinColumn.name]: folder.id,\n }))\n )\n .execute();\n }\n\n for (const existingFolder of existingFolders) {\n let replaceQuery;\n switch (strapi.db.dialect.client) {\n case 'sqlite':\n replaceQuery = '? || SUBSTRING(??, ?)';\n break;\n case 'postgres':\n replaceQuery = 'CONCAT(?::TEXT, SUBSTRING(??, ?::INTEGER))';\n break;\n default:\n replaceQuery = 'CONCAT(?, SUBSTRING(??, ?))';\n }\n\n // update path for folders themselves & folders below\n totalFolderNumber = await strapi.db\n .getConnection(folderTable)\n .transacting(trx.get())\n .where(pathColName, existingFolder.path)\n .orWhere(pathColName, 'like', `${existingFolder.path}/%`)\n .update(\n pathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n pathColName,\n existingFolder.path.length + 1,\n ])\n );\n\n // update path of files below\n totalFileNumber = await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .where(folderPathColName, existingFolder.path)\n .orWhere(folderPathColName, 'like', `${existingFolder.path}/%`)\n .update(\n folderPathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n folderPathColName,\n existingFolder.path.length + 1,\n ])\n );\n }\n }\n\n if (existingFiles.length > 0) {\n // update files' folder relation (delete + insert; upsert not possible)\n // @ts-expect-error - no dynamic typings for the models\n const fileJoinTable = strapi.db.metadata.get(FILE_MODEL_UID).attributes.folder.joinTable;\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [fileJoinTable.joinColumn.name]: { $in: fileIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .insert(\n existingFiles.map((file) => ({\n [fileJoinTable.inverseJoinColumn.name]: destinationFolderId,\n [fileJoinTable.joinColumn.name]: file.id,\n }))\n )\n .execute();\n }\n\n // update files main fields (path + updatedBy)\n await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .whereIn('id', fileIds)\n .update(folderPathColName, destinationFolderPath);\n }\n\n await trx.commit();\n } catch (e) {\n await trx.rollback();\n throw e;\n }\n\n const updatedFolders = await strapi.db.query(FOLDER_MODEL_UID).findMany({\n where: { id: { $in: folderIds } },\n });\n\n const updatedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n where: { id: { $in: fileIds } },\n });\n\n await getService('metrics').trackUsage('didBulkMoveMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: updatedFolders.length,\n rootAssetNumber: updatedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + updatedFiles.length,\n },\n });\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(updatedFiles),\n folders: await pmFolder.sanitizeOutput(updatedFolders),\n },\n };\n },\n};\n"],"names":["deleteMany","ctx","body","request","state","userAbility","pmFolder","strapi","service","createPermissionsManager","ability","model","FOLDER_MODEL_UID","pmFile","action","ACTIONS","read","FILE_MODEL_UID","validateDeleteManyFoldersFiles","fileService","getService","folderService","deletedFiles","deleteByIds","fileIds","folders","deletedFolders","totalFolderNumber","totalFileNumber","folderIds","length","trackUsage","eventProperties","rootFolderNumber","rootAssetNumber","totalAssetNumber","data","files","sanitizeOutput","moveMany","validateMoveManyFoldersFiles","destinationFolderId","trx","db","transaction","existingFolders","queryBuilder","select","where","id","$in","transacting","get","forUpdate","execute","existingFiles","destinationFolderPath","destinationFolder","first","path","fileTable","getModel","collectionName","folderTable","folderPathColName","metadata","attributes","folderPath","columnName","pathColName","joinTable","parent","name","delete","joinColumn","insert","map","folder","inverseJoinColumn","existingFolder","replaceQuery","dialect","client","getConnection","orWhere","update","connection","raw","strings","joinBy","pathId","fileJoinTable","file","whereIn","commit","e","rollback","updatedFolders","query","findMany","updatedFiles"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin-folder-file.mjs","sources":["../../../server/src/controllers/admin-folder-file.ts"],"sourcesContent":["import { errors, strings } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FOLDER_MODEL_UID, FILE_MODEL_UID } from '../constants';\nimport { FolderContainsUnauthorizedAssetsError } from '../errors';\nimport {\n validateDeleteManyFoldersFiles,\n validateMoveManyFoldersFiles,\n} from './validation/admin/folder-file';\n\nimport type { File, Folder } from '../types';\n\nexport default {\n async deleteMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.update,\n model: FILE_MODEL_UID,\n });\n\n await validateDeleteManyFoldersFiles(body);\n\n const fileService = getService('file');\n const folderService = getService('folder');\n\n const { fileIds = [], folderIds = [] } = body;\n\n const canDeleteFiles = async (files: File[]) => {\n const ids = [...new Set(files.map(({ id }) => id))];\n\n if (ids.length === 0) {\n return true;\n }\n\n const query = pmFile.addPermissionsQueryTo({ filters: { id: { $in: ids } } });\n const permittedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n select: ['id'],\n where: query.filters,\n });\n\n return permittedFiles.length === ids.length;\n };\n\n // https://github.com/strapi/strapi/issues/27649\n // Validate explicitly selected files before deleting a folder. Otherwise a mixed request\n // could delete permitted files first and only then discover a forbidden file in the folder.\n if (fileIds.length > 0) {\n const selectedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n where: { id: { $in: fileIds } },\n });\n\n if (!(await canDeleteFiles(selectedFiles))) {\n throw new errors.ForbiddenError();\n }\n }\n\n const {\n folders: deletedFolders,\n totalFolderNumber,\n totalFileNumber,\n } = await folderService.deleteByIds(folderIds, {\n async validateFiles(files) {\n if (!(await canDeleteFiles(files))) {\n throw new FolderContainsUnauthorizedAssetsError();\n }\n },\n });\n const deletedFiles = await fileService.deleteByIds(fileIds);\n\n if (deletedFiles.length + deletedFolders.length > 1) {\n await getService('metrics').trackUsage('didBulkDeleteMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: deletedFolders.length,\n rootAssetNumber: deletedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + deletedFiles.length,\n },\n });\n }\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(deletedFiles, { action: ACTIONS.read }),\n folders: await pmFolder.sanitizeOutput(deletedFolders),\n },\n };\n },\n async moveMany(ctx: Context) {\n const { body } = ctx.request;\n const {\n state: { userAbility },\n } = ctx;\n\n const pmFolder = strapi.service('admin::permission').createPermissionsManager({\n ability: ctx.state.userAbility,\n model: FOLDER_MODEL_UID,\n });\n\n const pmFile = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.read,\n model: FILE_MODEL_UID,\n });\n\n await validateMoveManyFoldersFiles(body);\n const { folderIds = [], fileIds = [], destinationFolderId } = body;\n\n let totalFolderNumber = 0;\n let totalFileNumber = 0;\n\n const trx = await strapi.db.transaction();\n try {\n // fetch folders\n const existingFolders = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select(['id', 'pathId', 'path'])\n .where({ id: { $in: folderIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<Folder[]>();\n\n // fetch files\n const existingFiles = await strapi.db\n .queryBuilder(FILE_MODEL_UID)\n .select(['id'])\n .where({ id: { $in: fileIds } })\n .transacting(trx.get())\n .forUpdate()\n .execute<File[]>();\n\n // fetch destinationFolder path\n let destinationFolderPath = '/';\n if (destinationFolderId !== null) {\n const destinationFolder = await strapi.db\n .queryBuilder(FOLDER_MODEL_UID)\n .select('path')\n .where({ id: destinationFolderId })\n .transacting(trx.get())\n .first()\n .execute<Folder>();\n destinationFolderPath = destinationFolder.path;\n }\n\n const fileTable = strapi.getModel(FILE_MODEL_UID).collectionName;\n const folderTable = strapi.getModel(FOLDER_MODEL_UID).collectionName;\n const folderPathColName =\n // @ts-expect-error - no dynamic typings for the models\n strapi.db.metadata.get(FILE_MODEL_UID).attributes.folderPath.columnName;\n // @ts-expect-error - no dynamic typings for the models\n const pathColName = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.path.columnName;\n\n if (existingFolders.length > 0) {\n // update folders' parent relation\n // @ts-expect-error - no dynamic typings for the models\n const { joinTable } = strapi.db.metadata.get(FOLDER_MODEL_UID).attributes.parent;\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [joinTable.joinColumn.name]: { $in: folderIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(joinTable.name)\n .transacting(trx.get())\n .insert(\n existingFolders.map((folder) => ({\n [joinTable.inverseJoinColumn.name]: destinationFolderId,\n [joinTable.joinColumn.name]: folder.id,\n }))\n )\n .execute();\n }\n\n for (const existingFolder of existingFolders) {\n let replaceQuery;\n switch (strapi.db.dialect.client) {\n case 'sqlite':\n replaceQuery = '? || SUBSTRING(??, ?)';\n break;\n case 'postgres':\n replaceQuery = 'CONCAT(?::TEXT, SUBSTRING(??, ?::INTEGER))';\n break;\n default:\n replaceQuery = 'CONCAT(?, SUBSTRING(??, ?))';\n }\n\n // update path for folders themselves & folders below\n totalFolderNumber = await strapi.db\n .getConnection(folderTable)\n .transacting(trx.get())\n .where(pathColName, existingFolder.path)\n .orWhere(pathColName, 'like', `${existingFolder.path}/%`)\n .update(\n pathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n pathColName,\n existingFolder.path.length + 1,\n ])\n );\n\n // update path of files below\n totalFileNumber = await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .where(folderPathColName, existingFolder.path)\n .orWhere(folderPathColName, 'like', `${existingFolder.path}/%`)\n .update(\n folderPathColName,\n strapi.db.connection.raw(replaceQuery, [\n strings.joinBy('/', destinationFolderPath, `${existingFolder.pathId}`),\n folderPathColName,\n existingFolder.path.length + 1,\n ])\n );\n }\n }\n\n if (existingFiles.length > 0) {\n // update files' folder relation (delete + insert; upsert not possible)\n // @ts-expect-error - no dynamic typings for the models\n const fileJoinTable = strapi.db.metadata.get(FILE_MODEL_UID).attributes.folder.joinTable;\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .delete()\n .where({ [fileJoinTable.joinColumn.name]: { $in: fileIds } })\n .execute();\n\n if (destinationFolderId !== null) {\n await strapi.db\n .queryBuilder(fileJoinTable.name)\n .transacting(trx.get())\n .insert(\n existingFiles.map((file) => ({\n [fileJoinTable.inverseJoinColumn.name]: destinationFolderId,\n [fileJoinTable.joinColumn.name]: file.id,\n }))\n )\n .execute();\n }\n\n // update files main fields (path + updatedBy)\n await strapi.db\n .getConnection(fileTable)\n .transacting(trx.get())\n .whereIn('id', fileIds)\n .update(folderPathColName, destinationFolderPath);\n }\n\n await trx.commit();\n } catch (e) {\n await trx.rollback();\n throw e;\n }\n\n const updatedFolders = await strapi.db.query(FOLDER_MODEL_UID).findMany({\n where: { id: { $in: folderIds } },\n });\n\n const updatedFiles = await strapi.db.query(FILE_MODEL_UID).findMany({\n where: { id: { $in: fileIds } },\n });\n\n await getService('metrics').trackUsage('didBulkMoveMediaLibraryElements', {\n eventProperties: {\n rootFolderNumber: updatedFolders.length,\n rootAssetNumber: updatedFiles.length,\n totalFolderNumber,\n totalAssetNumber: totalFileNumber + updatedFiles.length,\n },\n });\n\n ctx.body = {\n data: {\n files: await pmFile.sanitizeOutput(updatedFiles),\n folders: await pmFolder.sanitizeOutput(updatedFolders),\n },\n };\n },\n};\n"],"names":["deleteMany","ctx","body","request","state","userAbility","pmFolder","strapi","service","createPermissionsManager","ability","model","FOLDER_MODEL_UID","pmFile","action","ACTIONS","update","FILE_MODEL_UID","validateDeleteManyFoldersFiles","fileService","getService","folderService","fileIds","folderIds","canDeleteFiles","files","ids","Set","map","id","length","query","addPermissionsQueryTo","filters","$in","permittedFiles","db","findMany","select","where","selectedFiles","errors","ForbiddenError","folders","deletedFolders","totalFolderNumber","totalFileNumber","deleteByIds","validateFiles","FolderContainsUnauthorizedAssetsError","deletedFiles","trackUsage","eventProperties","rootFolderNumber","rootAssetNumber","totalAssetNumber","data","sanitizeOutput","read","moveMany","validateMoveManyFoldersFiles","destinationFolderId","trx","transaction","existingFolders","queryBuilder","transacting","get","forUpdate","execute","existingFiles","destinationFolderPath","destinationFolder","first","path","fileTable","getModel","collectionName","folderTable","folderPathColName","metadata","attributes","folderPath","columnName","pathColName","joinTable","parent","name","delete","joinColumn","insert","folder","inverseJoinColumn","existingFolder","replaceQuery","dialect","client","getConnection","orWhere","connection","raw","strings","joinBy","pathId","fileJoinTable","file","whereIn","commit","e","rollback","updatedFolders","updatedFiles"],"mappings":"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"}
|