@strapi/upload 5.52.0 → 5.52.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/future/components/TruncatedText.js +76 -0
- package/dist/admin/future/components/TruncatedText.js.map +1 -0
- package/dist/admin/future/components/TruncatedText.mjs +74 -0
- package/dist/admin/future/components/TruncatedText.mjs.map +1 -0
- package/dist/admin/future/components/UploadProgressDialog.js +36 -1
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +37 -2
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/hooks/useApiErrorMessage.js +61 -0
- package/dist/admin/future/hooks/useApiErrorMessage.js.map +1 -0
- package/dist/admin/future/hooks/useApiErrorMessage.mjs +39 -0
- package/dist/admin/future/hooks/useApiErrorMessage.mjs.map +1 -0
- package/dist/admin/future/hooks/useTracking.js +57 -0
- package/dist/admin/future/hooks/useTracking.js.map +1 -0
- package/dist/admin/future/hooks/useTracking.mjs +53 -0
- package/dist/admin/future/hooks/useTracking.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +31 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +31 -0
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +13 -11
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +13 -11
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +62 -51
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +62 -51
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +3 -4
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +4 -5
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +11 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +11 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +6 -4
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +6 -4
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +10 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +10 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +3 -2
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +3 -2
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +4 -2
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +4 -2
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +18 -2
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +18 -2
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +9 -7
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +9 -7
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.js +12 -0
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs +12 -0
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -38
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +23 -40
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.js +14 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +14 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +5 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +6 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js +30 -0
- package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +26 -0
- package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.js +17 -0
- package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -1
- package/dist/admin/future/pages/Assets/utils/selection.mjs +17 -1
- package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -1
- package/dist/admin/future/services/api.js +123 -19
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +125 -21
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +11 -23
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +11 -23
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/uploadFileViaXHR.js +9 -3
- package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
- package/dist/admin/future/services/uploadFileViaXHR.mjs +9 -3
- package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
- package/dist/admin/future/store/uploadProgress.js +44 -5
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +43 -6
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/future/utils/getApiErrorMessage.js +20 -0
- package/dist/admin/future/utils/getApiErrorMessage.js.map +1 -0
- package/dist/admin/future/utils/getApiErrorMessage.mjs +18 -0
- package/dist/admin/future/utils/getApiErrorMessage.mjs.map +1 -0
- package/dist/admin/hooks/useAIMetadataJob.js +1 -1
- package/dist/admin/hooks/useAIMetadataJob.js.map +1 -1
- package/dist/admin/hooks/useAIMetadataJob.mjs +1 -1
- package/dist/admin/hooks/useAIMetadataJob.mjs.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
- package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
- package/dist/admin/src/future/components/TruncatedText.d.ts +32 -0
- package/dist/admin/src/future/hooks/useApiErrorMessage.d.ts +17 -0
- package/dist/admin/src/future/hooks/useTracking.d.ts +29 -0
- package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +5 -3
- package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +5 -4
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +2 -0
- package/dist/admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +18 -0
- package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +10 -0
- package/dist/admin/src/future/services/api.d.ts +6 -7
- package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +6 -1
- package/dist/admin/src/future/store/uploadProgress.d.ts +16 -1
- package/dist/admin/src/future/utils/getApiErrorMessage.d.ts +1 -0
- package/dist/admin/translations/ca.json.js +1 -1
- package/dist/admin/translations/ca.json.mjs +1 -1
- package/dist/admin/translations/de.json.js +1 -1
- package/dist/admin/translations/de.json.mjs +1 -1
- package/dist/admin/translations/en.json.js +2 -1
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +2 -1
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/translations/es.json.js +1 -1
- package/dist/admin/translations/es.json.mjs +1 -1
- package/dist/admin/translations/fr.json.js +1 -1
- package/dist/admin/translations/fr.json.mjs +1 -1
- package/dist/admin/translations/ja.json.js +1 -1
- package/dist/admin/translations/ja.json.mjs +1 -1
- package/dist/admin/translations/ko.json.js +1 -1
- package/dist/admin/translations/ko.json.mjs +1 -1
- package/dist/admin/translations/nl.json.js +1 -1
- package/dist/admin/translations/nl.json.mjs +1 -1
- package/dist/admin/translations/pl.json.js +1 -1
- package/dist/admin/translations/pl.json.mjs +1 -1
- package/dist/admin/translations/pt-BR.json.js +1 -1
- package/dist/admin/translations/pt-BR.json.mjs +1 -1
- package/dist/admin/translations/ru.json.js +1 -1
- package/dist/admin/translations/ru.json.mjs +1 -1
- package/dist/admin/translations/sk.json.js +1 -1
- package/dist/admin/translations/sk.json.mjs +1 -1
- package/dist/admin/translations/tr.json.js +1 -1
- package/dist/admin/translations/tr.json.mjs +1 -1
- package/dist/admin/translations/uk.json.js +1 -1
- package/dist/admin/translations/uk.json.mjs +1 -1
- package/dist/admin/translations/zh-Hans.json.js +1 -1
- package/dist/admin/translations/zh-Hans.json.mjs +1 -1
- package/dist/admin/translations/zh.json.js +1 -1
- package/dist/admin/translations/zh.json.mjs +1 -1
- package/dist/server/controllers/admin-file.js +27 -9
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +27 -9
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-upload.js +24 -8
- package/dist/server/controllers/admin-upload.js.map +1 -1
- package/dist/server/controllers/admin-upload.mjs +24 -8
- package/dist/server/controllers/admin-upload.mjs.map +1 -1
- package/dist/server/routes/admin.js +41 -21
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +41 -21
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata.js +25 -5
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +1 -1
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/upload.js +4 -0
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +4 -0
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/controllers/admin-file.d.ts +29 -8
- package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-upload.d.ts +20 -4
- package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +4 -4
- package/dist/server/src/index.d.ts +4 -4
- package/dist/server/src/routes/admin.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata.d.ts +2 -2
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/dist/shared/contracts/ai-metadata-jobs.d.ts +10 -32
- package/dist/shared/contracts/ai-metadata-jobs.d.ts.map +1 -1
- package/dist/shared/contracts/files.d.ts +10 -10
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/admin/future/utils/getBulkMoveErrorMessage.js +0 -23
- package/dist/admin/future/utils/getBulkMoveErrorMessage.js.map +0 -1
- package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs +0 -21
- package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs.map +0 -1
- package/dist/admin/src/future/utils/getBulkMoveErrorMessage.d.ts +0 -1
|
@@ -101,7 +101,7 @@ var ko = {
|
|
|
101
101
|
"tabs.title": "자산을 업로드할 방법을 선택해주세요.",
|
|
102
102
|
"window.confirm.close-modal.file": "확실한가요? 변경 내용이 사라집니다.",
|
|
103
103
|
"window.confirm.close-modal.files": "확실하나요? 아직 업로드되지 않은 파일이 있습니다.",
|
|
104
|
-
"apiError.FileTooBig": "
|
|
104
|
+
"apiError.FileTooBig": "파일이 너무 커서 업로드할 수 없습니다. 서버에서 허용하는 최대 요청 크기를 초과했습니다.",
|
|
105
105
|
"assets.uploaded": "{number, plural, one {1 개} other {# 개}}의 에셋이 업로드되었습니다",
|
|
106
106
|
"asset-details.title": "파일 상세 정보",
|
|
107
107
|
"asset-details.description": "파일 정보와 메타데이터를 표시합니다.",
|
|
@@ -99,7 +99,7 @@ var ko = {
|
|
|
99
99
|
"tabs.title": "자산을 업로드할 방법을 선택해주세요.",
|
|
100
100
|
"window.confirm.close-modal.file": "확실한가요? 변경 내용이 사라집니다.",
|
|
101
101
|
"window.confirm.close-modal.files": "확실하나요? 아직 업로드되지 않은 파일이 있습니다.",
|
|
102
|
-
"apiError.FileTooBig": "
|
|
102
|
+
"apiError.FileTooBig": "파일이 너무 커서 업로드할 수 없습니다. 서버에서 허용하는 최대 요청 크기를 초과했습니다.",
|
|
103
103
|
"assets.uploaded": "{number, plural, one {1 개} other {# 개}}의 에셋이 업로드되었습니다",
|
|
104
104
|
"asset-details.title": "파일 상세 정보",
|
|
105
105
|
"asset-details.description": "파일 정보와 메타데이터를 표시합니다.",
|
|
@@ -4,7 +4,7 @@ var nl = {
|
|
|
4
4
|
"ai.modal.error": "Kon geen AI-metadata genereren voor de geüploade bestanden.",
|
|
5
5
|
"ai.modal.title": "{count, plural, one {# bestand geüpload} other {# bestanden geüpload}}, beoordeel door AI gegenereerde metadata",
|
|
6
6
|
"ai.modal.uploading.title": "Uploaden en verwerken met AI...",
|
|
7
|
-
"apiError.FileTooBig": "Het
|
|
7
|
+
"apiError.FileTooBig": "Het bestand is te groot om te uploaden. Het overschrijdt de maximale aanvraaggrootte die door de server is toegestaan.",
|
|
8
8
|
"asset-details.alternativeText": "Alternatieve tekst",
|
|
9
9
|
"asset-details.assetId": "Bestands-ID",
|
|
10
10
|
"asset-details.caption": "Onderschrift",
|
|
@@ -2,7 +2,7 @@ var nl = {
|
|
|
2
2
|
"ai.modal.error": "Kon geen AI-metadata genereren voor de geüploade bestanden.",
|
|
3
3
|
"ai.modal.title": "{count, plural, one {# bestand geüpload} other {# bestanden geüpload}}, beoordeel door AI gegenereerde metadata",
|
|
4
4
|
"ai.modal.uploading.title": "Uploaden en verwerken met AI...",
|
|
5
|
-
"apiError.FileTooBig": "Het
|
|
5
|
+
"apiError.FileTooBig": "Het bestand is te groot om te uploaden. Het overschrijdt de maximale aanvraaggrootte die door de server is toegestaan.",
|
|
6
6
|
"asset-details.alternativeText": "Alternatieve tekst",
|
|
7
7
|
"asset-details.assetId": "Bestands-ID",
|
|
8
8
|
"asset-details.caption": "Onderschrift",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var pl = {
|
|
4
|
-
"apiError.FileTooBig": "
|
|
4
|
+
"apiError.FileTooBig": "Plik jest zbyt duży, aby go przesłać. Przekracza maksymalny rozmiar żądania dozwolony przez serwer.",
|
|
5
5
|
"assets.uploaded": "{number, plural, one {# przesłany zasób} other {# przesłanych zasobów}} pomyślnie",
|
|
6
6
|
"asset-details.title": "Szczegóły pliku",
|
|
7
7
|
"asset-details.description": "Wyświetla informacje o pliku i jego metadane",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var pl = {
|
|
2
|
-
"apiError.FileTooBig": "
|
|
2
|
+
"apiError.FileTooBig": "Plik jest zbyt duży, aby go przesłać. Przekracza maksymalny rozmiar żądania dozwolony przez serwer.",
|
|
3
3
|
"assets.uploaded": "{number, plural, one {# przesłany zasób} other {# przesłanych zasobów}} pomyślnie",
|
|
4
4
|
"asset-details.title": "Szczegóły pliku",
|
|
5
5
|
"asset-details.description": "Wyświetla informacje o pliku i jego metadane",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var ptBR = {
|
|
4
|
-
"apiError.FileTooBig": "O
|
|
4
|
+
"apiError.FileTooBig": "O arquivo é muito grande para ser enviado. Ele excede o tamanho máximo de requisição permitido pelo servidor.",
|
|
5
5
|
"upload.generic-error": "Ocorreu um erro ao enviar o arquivo.",
|
|
6
6
|
"bulk.select.label": "Selecionar todos os ativos",
|
|
7
7
|
"button.next": "Próximo",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var ptBR = {
|
|
2
|
-
"apiError.FileTooBig": "O
|
|
2
|
+
"apiError.FileTooBig": "O arquivo é muito grande para ser enviado. Ele excede o tamanho máximo de requisição permitido pelo servidor.",
|
|
3
3
|
"upload.generic-error": "Ocorreu um erro ao enviar o arquivo.",
|
|
4
4
|
"bulk.select.label": "Selecionar todos os ativos",
|
|
5
5
|
"button.next": "Próximo",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var ru = {
|
|
4
|
-
"apiError.FileTooBig": "
|
|
4
|
+
"apiError.FileTooBig": "Файл слишком большой для загрузки. Он превышает максимальный размер запроса, разрешённый сервером.",
|
|
5
5
|
"upload.generic-error": "При загрузке файла произошла ошибка.",
|
|
6
6
|
"bulk.select.label": "Выбрать все ресурсы",
|
|
7
7
|
"button.next": "Далее",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var ru = {
|
|
2
|
-
"apiError.FileTooBig": "
|
|
2
|
+
"apiError.FileTooBig": "Файл слишком большой для загрузки. Он превышает максимальный размер запроса, разрешённый сервером.",
|
|
3
3
|
"upload.generic-error": "При загрузке файла произошла ошибка.",
|
|
4
4
|
"bulk.select.label": "Выбрать все ресурсы",
|
|
5
5
|
"button.next": "Далее",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var sk = {
|
|
4
|
-
"apiError.FileTooBig": "
|
|
4
|
+
"apiError.FileTooBig": "Súbor je príliš veľký na nahranie. Prekračuje maximálnu veľkosť požiadavky povolenú serverom.",
|
|
5
5
|
"assets.uploaded": "{number, plural, one {# súbor} other {# súbory}} boli úspešne nahraté",
|
|
6
6
|
"asset-details.title": "Podrobnosti súboru",
|
|
7
7
|
"asset-details.description": "Zobrazuje informácie o súbore a metadáta",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var sk = {
|
|
2
|
-
"apiError.FileTooBig": "
|
|
2
|
+
"apiError.FileTooBig": "Súbor je príliš veľký na nahranie. Prekračuje maximálnu veľkosť požiadavky povolenú serverom.",
|
|
3
3
|
"assets.uploaded": "{number, plural, one {# súbor} other {# súbory}} boli úspešne nahraté",
|
|
4
4
|
"asset-details.title": "Podrobnosti súboru",
|
|
5
5
|
"asset-details.description": "Zobrazuje informácie o súbore a metadáta",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var tr = {
|
|
4
|
-
"apiError.FileTooBig": "
|
|
4
|
+
"apiError.FileTooBig": "Dosya yüklenemeyecek kadar büyük. Sunucunun izin verdiği en yüksek istek boyutunu aşıyor.",
|
|
5
5
|
"upload.generic-error": "Dosyayı yüklerken bir hata oluştu.",
|
|
6
6
|
"bulk.select.label": "Tüm dosyaları seçin",
|
|
7
7
|
"button.next": "İleri",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var tr = {
|
|
2
|
-
"apiError.FileTooBig": "
|
|
2
|
+
"apiError.FileTooBig": "Dosya yüklenemeyecek kadar büyük. Sunucunun izin verdiği en yüksek istek boyutunu aşıyor.",
|
|
3
3
|
"upload.generic-error": "Dosyayı yüklerken bir hata oluştu.",
|
|
4
4
|
"bulk.select.label": "Tüm dosyaları seçin",
|
|
5
5
|
"button.next": "İleri",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var uk = {
|
|
4
|
-
"apiError.FileTooBig": "
|
|
4
|
+
"apiError.FileTooBig": "Файл занадто великий для завантаження. Він перевищує максимальний розмір запиту, дозволений сервером.",
|
|
5
5
|
"upload.generic-error": "Сталася помилка під час завантаження файлу.",
|
|
6
6
|
"bulk.select.label": "Вибрати всі активи",
|
|
7
7
|
"button.next": "Далі",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var uk = {
|
|
2
|
-
"apiError.FileTooBig": "
|
|
2
|
+
"apiError.FileTooBig": "Файл занадто великий для завантаження. Він перевищує максимальний розмір запиту, дозволений сервером.",
|
|
3
3
|
"upload.generic-error": "Сталася помилка під час завантаження файлу.",
|
|
4
4
|
"bulk.select.label": "Вибрати всі активи",
|
|
5
5
|
"button.next": "Далі",
|
|
@@ -125,7 +125,7 @@ var zhHans = {
|
|
|
125
125
|
"sort.updated_at_desc": "最近更新",
|
|
126
126
|
"window.confirm.close-modal.file": "你确定吗?你的更改将会丢失",
|
|
127
127
|
"window.confirm.close-modal.files": "你确定吗?你还有一些文件还没有被上传",
|
|
128
|
-
"apiError.FileTooBig": "
|
|
128
|
+
"apiError.FileTooBig": "文件太大,无法上传。已超出服务器允许的最大请求大小",
|
|
129
129
|
"upload.generic-error": "上传文件时发生错误",
|
|
130
130
|
"bulk.select.label": "选择所有资源",
|
|
131
131
|
"content.isLoading": "内容正在加载中",
|
|
@@ -123,7 +123,7 @@ var zhHans = {
|
|
|
123
123
|
"sort.updated_at_desc": "最近更新",
|
|
124
124
|
"window.confirm.close-modal.file": "你确定吗?你的更改将会丢失",
|
|
125
125
|
"window.confirm.close-modal.files": "你确定吗?你还有一些文件还没有被上传",
|
|
126
|
-
"apiError.FileTooBig": "
|
|
126
|
+
"apiError.FileTooBig": "文件太大,无法上传。已超出服务器允许的最大请求大小",
|
|
127
127
|
"upload.generic-error": "上传文件时发生错误",
|
|
128
128
|
"bulk.select.label": "选择所有资源",
|
|
129
129
|
"content.isLoading": "内容正在加载中",
|
|
@@ -56,7 +56,12 @@ var adminFile = {
|
|
|
56
56
|
]);
|
|
57
57
|
ctx.body = body;
|
|
58
58
|
},
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* `GET /upload/ai-metadata-jobs/pending-count`
|
|
61
|
+
*
|
|
62
|
+
* How many images a backfill job would have to process, i.e. those still
|
|
63
|
+
* missing AI metadata, alongside the total image count.
|
|
64
|
+
*/ async getAIMetadataPendingCount (ctx) {
|
|
60
65
|
const { userAbility } = ctx.state;
|
|
61
66
|
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
62
67
|
ability: userAbility,
|
|
@@ -86,7 +91,14 @@ var adminFile = {
|
|
|
86
91
|
ctx.badRequest(message);
|
|
87
92
|
}
|
|
88
93
|
},
|
|
89
|
-
|
|
94
|
+
/**
|
|
95
|
+
* `POST /upload/ai-metadata-jobs`
|
|
96
|
+
*
|
|
97
|
+
* Create a backfill job that generates AI metadata for every image still
|
|
98
|
+
* missing it, and return the job so the client can poll it. Processing runs
|
|
99
|
+
* detached from this request; see `generateAIMetadata` for the synchronous
|
|
100
|
+
* counterpart that works on an explicit selection.
|
|
101
|
+
*/ async createAIMetadataJob (ctx) {
|
|
90
102
|
const { userAbility } = ctx.state;
|
|
91
103
|
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
92
104
|
ability: userAbility,
|
|
@@ -135,14 +147,15 @@ var adminFile = {
|
|
|
135
147
|
}
|
|
136
148
|
},
|
|
137
149
|
/**
|
|
138
|
-
*
|
|
150
|
+
* `POST /upload/actions/generate-ai-metadata`
|
|
139
151
|
*
|
|
140
|
-
*
|
|
141
|
-
* every file has been processed and
|
|
142
|
-
* bad id or a non-image in the
|
|
152
|
+
* Generate AI metadata for the explicit selection of files in the body,
|
|
153
|
+
* synchronously: the request resolves once every file has been processed and
|
|
154
|
+
* reports the outcome per file, so a single bad id or a non-image in the
|
|
155
|
+
* selection never fails the whole batch.
|
|
143
156
|
*
|
|
144
|
-
*
|
|
145
|
-
*/ async
|
|
157
|
+
* For the whole-library backfill, see `createAIMetadataJob`.
|
|
158
|
+
*/ async generateAIMetadata (ctx) {
|
|
146
159
|
const { userAbility } = ctx.state;
|
|
147
160
|
const { body } = ctx.request;
|
|
148
161
|
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
@@ -163,7 +176,12 @@ var adminFile = {
|
|
|
163
176
|
data: results
|
|
164
177
|
};
|
|
165
178
|
},
|
|
166
|
-
|
|
179
|
+
/**
|
|
180
|
+
* `GET /upload/ai-metadata-jobs/latest`
|
|
181
|
+
*
|
|
182
|
+
* The most recent still-active backfill job, so a client that reloads mid-run
|
|
183
|
+
* can pick the progress bar back up. 404s when nothing is running.
|
|
184
|
+
*/ async getLatestAIMetadataJob (ctx) {
|
|
167
185
|
if (await index.getService('aiMetadata').isEnabled() === false) {
|
|
168
186
|
return ctx.notFound();
|
|
169
187
|
}
|
|
@@ -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 async getAIMetadataCount(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 async generateAIMetadata(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 * Generate AI metadata for an explicit selection of files, synchronously.\n *\n * Unlike `generateAIMetadata`, no job is created: the request resolves once\n * every file has been processed and reports the outcome per file, so a single\n * bad id or a non-image in the selection never fails the whole batch.\n *\n * @experimental\n */\n async unstable_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 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","getAIMetadataCount","aiMetadataService","isEnabled","badRequest","imagesWithoutMetadataCount","totalImages","countImagesWithoutMetadata","error","message","Error","log","generateAIMetadata","result","count","jobService","jobId","createJob","processExistingFiles","user","catch","err","status","cause","String","undefined","unstable_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/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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"}
|
|
@@ -54,7 +54,12 @@ var adminFile = {
|
|
|
54
54
|
]);
|
|
55
55
|
ctx.body = body;
|
|
56
56
|
},
|
|
57
|
-
|
|
57
|
+
/**
|
|
58
|
+
* `GET /upload/ai-metadata-jobs/pending-count`
|
|
59
|
+
*
|
|
60
|
+
* How many images a backfill job would have to process, i.e. those still
|
|
61
|
+
* missing AI metadata, alongside the total image count.
|
|
62
|
+
*/ async getAIMetadataPendingCount (ctx) {
|
|
58
63
|
const { userAbility } = ctx.state;
|
|
59
64
|
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
60
65
|
ability: userAbility,
|
|
@@ -84,7 +89,14 @@ var adminFile = {
|
|
|
84
89
|
ctx.badRequest(message);
|
|
85
90
|
}
|
|
86
91
|
},
|
|
87
|
-
|
|
92
|
+
/**
|
|
93
|
+
* `POST /upload/ai-metadata-jobs`
|
|
94
|
+
*
|
|
95
|
+
* Create a backfill job that generates AI metadata for every image still
|
|
96
|
+
* missing it, and return the job so the client can poll it. Processing runs
|
|
97
|
+
* detached from this request; see `generateAIMetadata` for the synchronous
|
|
98
|
+
* counterpart that works on an explicit selection.
|
|
99
|
+
*/ async createAIMetadataJob (ctx) {
|
|
88
100
|
const { userAbility } = ctx.state;
|
|
89
101
|
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
90
102
|
ability: userAbility,
|
|
@@ -133,14 +145,15 @@ var adminFile = {
|
|
|
133
145
|
}
|
|
134
146
|
},
|
|
135
147
|
/**
|
|
136
|
-
*
|
|
148
|
+
* `POST /upload/actions/generate-ai-metadata`
|
|
137
149
|
*
|
|
138
|
-
*
|
|
139
|
-
* every file has been processed and
|
|
140
|
-
* bad id or a non-image in the
|
|
150
|
+
* Generate AI metadata for the explicit selection of files in the body,
|
|
151
|
+
* synchronously: the request resolves once every file has been processed and
|
|
152
|
+
* reports the outcome per file, so a single bad id or a non-image in the
|
|
153
|
+
* selection never fails the whole batch.
|
|
141
154
|
*
|
|
142
|
-
*
|
|
143
|
-
*/ async
|
|
155
|
+
* For the whole-library backfill, see `createAIMetadataJob`.
|
|
156
|
+
*/ async generateAIMetadata (ctx) {
|
|
144
157
|
const { userAbility } = ctx.state;
|
|
145
158
|
const { body } = ctx.request;
|
|
146
159
|
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
@@ -161,7 +174,12 @@ var adminFile = {
|
|
|
161
174
|
data: results
|
|
162
175
|
};
|
|
163
176
|
},
|
|
164
|
-
|
|
177
|
+
/**
|
|
178
|
+
* `GET /upload/ai-metadata-jobs/latest`
|
|
179
|
+
*
|
|
180
|
+
* The most recent still-active backfill job, so a client that reloads mid-run
|
|
181
|
+
* can pick the progress bar back up. 404s when nothing is running.
|
|
182
|
+
*/ async getLatestAIMetadataJob (ctx) {
|
|
165
183
|
if (await getService('aiMetadata').isEnabled() === false) {
|
|
166
184
|
return ctx.notFound();
|
|
167
185
|
}
|
|
@@ -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 async getAIMetadataCount(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 async generateAIMetadata(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 * Generate AI metadata for an explicit selection of files, synchronously.\n *\n * Unlike `generateAIMetadata`, no job is created: the request resolves once\n * every file has been processed and reports the outcome per file, so a single\n * bad id or a non-image in the selection never fails the whole batch.\n *\n * @experimental\n */\n async unstable_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 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","getAIMetadataCount","aiMetadataService","isEnabled","badRequest","imagesWithoutMetadataCount","totalImages","countImagesWithoutMetadata","error","message","Error","log","generateAIMetadata","result","count","jobService","jobId","createJob","processExistingFiles","user","catch","err","status","cause","String","undefined","unstable_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/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":";;;;;;;AAUA,gBAAe;AACb,IAAA,MAAMA,MAAKC,GAAY,EAAA;AACrB,QAAA,MAAM,EACJC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACvB,GAAGF,GAAAA;AAEJ,QAAA,MAAMG,YAAAA,GAAe;YAAEC,QAAAA,EAAU;gBAAEC,MAAAA,EAAQ;AAAK;AAAE,SAAA;AAElD,QAAA,MAAMC,KAAKC,MAAAA,CAAOC,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAAA,EAASR,WAAAA;AACTS,YAAAA,MAAAA,EAAQC,QAAQC,IAAI;YACpBC,KAAAA,EAAOC;AACT,SAAA,CAAA;QAEA,IAAI,CAACT,EAAAA,CAAGU,SAAS,EAAE;AACjB,YAAA,OAAOhB,IAAIiB,SAAS,EAAA;AACtB,QAAA;;AAGA,QAAA,MAAMX,EAAAA,CAAGY,aAAa,CAAClB,GAAAA,CAAImB,KAAK,CAAA;AAEhC,QAAA,MAAMA,KAAAA,GAAQ,MAAMC,KAAAA,CAAMC,IAAI;AAE5B,QAAA,CAACC,CAAAA,GAAMhB,EAAAA,CAAGiB,aAAa,CAACD;AAExB,QAAA,CAACA,CAAAA,GAAME,KAAAA,CAAMrB,YAAAA,EAAcmB,CAAAA,CAAAA;AAE3B,QAAA,CAACA,IAAMhB,EAAAA,CAAGmB,qBAAqB,CAACH,CAAAA,CAAAA,CAAAA,CAChCtB,IAAImB,KAAK,CAAA;QAEX,MAAM,EAAEO,OAAAA,EAASC,KAAK,EAAEC,UAAU,EAAE,GAAG,MAAMC,UAAAA,CAAW,QAAA,CAAA,CAAUC,QAAQ,CAACX,KAAAA,CAAAA;;QAG3E,MAAMY,WAAAA,GAAc,MAAMX,KAAAA,CAAMY,GAAG,CAACL,KAAAA,EAAOE,UAAAA,CAAW,QAAQI,YAAY,CAAA;AAE1E,QAAA,MAAMC,cAAAA,GAAiB,MAAM5B,EAAAA,CAAG6B,cAAc,CAACJ,WAAAA,CAAAA;QAE/C,OAAO;YAAEL,OAAAA,EAASQ,cAAAA;AAAgBN,YAAAA;AAAW,SAAA;AAC/C,IAAA,CAAA;AAEA,IAAA,MAAMQ,SAAQpC,GAAY,EAAA;QACxB,MAAM,EACJC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACtBmC,MAAAA,EAAQ,EAAEC,EAAE,EAAE,EACf,GAAGtC,GAAAA;AAEJ,QAAA,MAAM,EAAEM,EAAE,EAAEiC,IAAI,EAAE,GAAG,MAAMC,6BAAAA,CACzBtC,WAAAA,EACAU,OAAAA,CAAQC,IAAI,EACZE,cAAAA,EACAuB,EAAAA,CAAAA;AAGF,QAAA,MAAMG,UAAAA,GAAa,MAAMZ,UAAAA,CAAW,MAAA,CAAA,CAAQI,YAAY,CAACM,IAAAA,CAAAA;AACzDvC,QAAAA,GAAAA,CAAI0C,IAAI,GAAG,MAAMpC,EAAAA,CAAG6B,cAAc,CAACM,UAAAA,CAAAA;AACrC,IAAA,CAAA;AAEA,IAAA,MAAME,SAAQ3C,GAAY,EAAA;AACxB,QAAA,MAAM,EAAEsC,EAAE,EAAE,GAAGtC,IAAIqC,MAAM;AACzB,QAAA,MAAM,EAAEnC,WAAW,EAAE,GAAGF,IAAIC,KAAK;AAEjC,QAAA,MAAM,EAAEK,EAAE,EAAEiC,IAAI,EAAE,GAAG,MAAMC,6BAAAA,CACzBtC,WAAAA,EACAU,OAAAA,CAAQgC,MAAM,EACd7B,cAAAA,EACAuB,EAAAA,CAAAA;AAGF,QAAA,MAAM,CAACI,IAAAA,CAAK,GAAG,MAAMG,OAAAA,CAAQC,GAAG,CAAC;YAC/BxC,EAAAA,CAAG6B,cAAc,CAACI,IAAAA,EAAM;AAAE5B,gBAAAA,MAAAA,EAAQC,QAAQC;AAAK,aAAA,CAAA;YAC/CgB,UAAAA,CAAW,QAAA,CAAA,CAAUkB,MAAM,CAACR,IAAAA;AAC7B,SAAA,CAAA;AAEDvC,QAAAA,GAAAA,CAAI0C,IAAI,GAAGA,IAAAA;AACb,IAAA,CAAA;AAEA;;;;;MAMA,MAAMM,2BAA0BhD,GAAY,EAAA;AAC1C,QAAA,MAAM,EAAEE,WAAW,EAAE,GAAGF,IAAIC,KAAK;AAEjC,QAAA,MAAMK,KAAKC,MAAAA,CAAOC,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAAA,EAASR,WAAAA;AACTS,YAAAA,MAAAA,EAAQC,QAAQC,IAAI;YACpBC,KAAAA,EAAOC;AACT,SAAA,CAAA;QAEA,IAAI,CAACT,EAAAA,CAAGU,SAAS,EAAE;AACjB,YAAA,OAAOhB,IAAIiB,SAAS,EAAA;AACtB,QAAA;AAEA,QAAA,MAAMgC,oBAAoBpB,UAAAA,CAAW,YAAA,CAAA;;AAGrC,QAAA,IAAI,CAAE,MAAMoB,iBAAAA,CAAkBC,SAAS,EAAA,EAAK;YAC1C,OAAOlD,GAAAA,CAAImD,UAAU,CAAC,oCAAA,CAAA;AACxB,QAAA;QAEA,IAAI;YACF,MAAM,EAAEC,0BAA0B,EAAEC,WAAW,EAAE,GAC/C,MAAMJ,kBAAkBK,0BAA0B,EAAA;AAEpDtD,YAAAA,GAAAA,CAAI0C,IAAI,GAAG;AACTU,gBAAAA,0BAAAA;AACAC,gBAAAA;AACF,aAAA;AACF,QAAA,CAAA,CAAE,OAAOE,KAAAA,EAAO;AACd,YAAA,MAAMC,OAAAA,GAAUD,KAAAA,YAAiBE,KAAAA,GAAQF,KAAAA,CAAMC,OAAO,GAAG,iCAAA;AAEzDjD,YAAAA,MAAAA,CAAOmD,GAAG,CAACH,KAAK,CAAC,iCAAA,EAAmC;AAClDC,gBAAAA,OAAAA;AACAD,gBAAAA;AACF,aAAA,CAAA;AAEAvD,YAAAA,GAAAA,CAAImD,UAAU,CAACK,OAAAA,CAAAA;AACjB,QAAA;AACF,IAAA,CAAA;AAEA;;;;;;;MAQA,MAAMG,qBAAoB3D,GAAY,EAAA;AACpC,QAAA,MAAM,EAAEE,WAAW,EAAE,GAAGF,IAAIC,KAAK;AAEjC,QAAA,MAAMK,KAAKC,MAAAA,CAAOC,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAAA,EAASR,WAAAA;AACTS,YAAAA,MAAAA,EAAQC,QAAQgC,MAAM;YACtB9B,KAAAA,EAAOC;AACT,SAAA,CAAA;QAEA,IAAI,CAACT,EAAAA,CAAGU,SAAS,EAAE;AACjB,YAAA,OAAOhB,IAAIiB,SAAS,EAAA;AACtB,QAAA;AAEA,QAAA,MAAMgC,oBAAoBpB,UAAAA,CAAW,YAAA,CAAA;;AAGrC,QAAA,IAAI,CAAE,MAAMoB,iBAAAA,CAAkBC,SAAS,EAAA,EAAK;YAC1C,OAAOlD,GAAAA,CAAImD,UAAU,CAAC,oCAAA,CAAA;AACxB,QAAA;QAEA,IAAI;;YAEF,MAAMS,MAAAA,GAAS,MAAMX,iBAAAA,CAAkBK,0BAA0B,EAAA;YAEjE,IAAIM,MAAAA,CAAOR,0BAA0B,KAAK,CAAA,EAAG;AAC3CpD,gBAAAA,GAAAA,CAAI0C,IAAI,GAAG;oBACTmB,KAAAA,EAAO,CAAA;oBACPL,OAAAA,EAAS;AACX,iBAAA;AACA,gBAAA;AACF,YAAA;;AAGA,YAAA,MAAMM,aAAajC,UAAAA,CAAW,gBAAA,CAAA;YAC9B,MAAMkC,KAAAA,GAAQ,MAAMD,UAAAA,CAAWE,SAAS,EAAA;;YAGxCf,iBAAAA,CAAkBgB,oBAAoB,CAACF,KAAAA,EAAO/D,GAAAA,CAAIC,KAAK,CAACiE,IAAI,CAAA,CAAEC,KAAK,CAAC,CAACC,GAAAA,GAAAA;AACnE7D,gBAAAA,MAAAA,CAAOmD,GAAG,CAACH,KAAK,CAAC,yBAAA,EAA2Ba,GAAAA,CAAAA;AAC9C,YAAA,CAAA,CAAA;;AAGApE,YAAAA,GAAAA,CAAI0C,IAAI,GAAG;AACTqB,gBAAAA,KAAAA;gBACAM,MAAAA,EAAQ;AACV,aAAA;AACF,QAAA,CAAA,CAAE,OAAOd,KAAAA,EAAO;AACd,YAAA,MAAMC,OAAAA,GAAUD,KAAAA,YAAiBE,KAAAA,GAAQF,KAAAA,CAAMC,OAAO,GAAG,gCAAA;YACzD,MAAMc,KAAAA,GAAQf,iBAAiBE,KAAAA,IAASF,KAAAA,CAAMe,KAAK,GAAGC,MAAAA,CAAOhB,KAAAA,CAAMe,KAAK,CAAA,GAAIE,SAAAA;AAE5EjE,YAAAA,MAAAA,CAAOmD,GAAG,CAACH,KAAK,CAAC,6CAAA,EAA+C;AAC9DC,gBAAAA,OAAAA;AACAc,gBAAAA,KAAAA;AACAf,gBAAAA;AACF,aAAA,CAAA;YAEAvD,GAAAA,CAAImD,UAAU,CAACmB,KAAAA,GAAQ,CAAA,EAAGd,QAAQ,EAAE,EAAEc,OAAO,GAAGd,OAAAA,CAAAA;AAClD,QAAA;AACF,IAAA,CAAA;AAEA;;;;;;;;;MAUA,MAAMiB,oBAAmBzE,GAAY,EAAA;AACnC,QAAA,MAAM,EAAEE,WAAW,EAAE,GAAGF,IAAIC,KAAK;AACjC,QAAA,MAAM,EAAEyC,IAAI,EAAE,GAAG1C,IAAI0E,OAAO;AAE5B,QAAA,MAAMpE,KAAKC,MAAAA,CAAOC,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAAA,EAASR,WAAAA;AACTS,YAAAA,MAAAA,EAAQC,QAAQgC,MAAM;YACtB9B,KAAAA,EAAOC;AACT,SAAA,CAAA;QAEA,IAAI,CAACT,EAAAA,CAAGU,SAAS,EAAE;AACjB,YAAA,OAAOhB,IAAIiB,SAAS,EAAA;AACtB,QAAA;AAEA,QAAA,MAAM,EAAE0D,OAAO,EAAE,GAAG,MAAMC,8BAAAA,CAA+BlC,IAAAA,CAAAA;AAEzD,QAAA,MAAMO,oBAAoBpB,UAAAA,CAAW,YAAA,CAAA;AAErC,QAAA,IAAI,CAAE,MAAMoB,iBAAAA,CAAkBC,SAAS,EAAA,EAAK;YAC1C,OAAOlD,GAAAA,CAAImD,UAAU,CAAC,oCAAA,CAAA;AACxB,QAAA;QAEA,MAAMzB,OAAAA,GAAU,MAAMuB,iBAAAA,CAAkB4B,gBAAgB,CAACF,OAAAA,EAAS3E,GAAAA,CAAIC,KAAK,CAACiE,IAAI,CAAA;AAEhFlE,QAAAA,GAAAA,CAAI0C,IAAI,GAAG;YAAEoC,IAAAA,EAAMpD;AAAQ,SAAA;AAC7B,IAAA,CAAA;AAEA;;;;;MAMA,MAAMqD,wBAAuB/E,GAAY,EAAA;AACvC,QAAA,IAAI,MAAO6B,UAAAA,CAAW,YAAA,CAAA,CAAcqB,SAAS,OAAQ,KAAA,EAAO;AAC1D,YAAA,OAAOlD,IAAIgF,QAAQ,EAAA;AACrB,QAAA;AAEA,QAAA,MAAMlB,aAAajC,UAAAA,CAAW,gBAAA,CAAA;QAC9B,MAAMoD,GAAAA,GAAM,MAAMnB,UAAAA,CAAWoB,kBAAkB,EAAA;AAE/C,QAAA,IAAI,CAACD,GAAAA,EAAK;YACR,OAAOjF,GAAAA,CAAIgF,QAAQ,CAAC,qBAAA,CAAA;AACtB,QAAA;AAEAhF,QAAAA,GAAAA,CAAI0C,IAAI,GAAGuC,GAAAA;AACb,IAAA;AACF,CAAA;;;;"}
|
|
@@ -36,8 +36,15 @@ var adminUpload = {
|
|
|
36
36
|
});
|
|
37
37
|
ctx.body = results;
|
|
38
38
|
},
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* `PUT /upload/files/:id`
|
|
41
|
+
*
|
|
42
|
+
* Update the editable metadata (`fileInfo`) of an existing file. Also reached
|
|
43
|
+
* through the `POST /upload` multiplexer, which delegates here with the id in
|
|
44
|
+
* `ctx.query` — hence the dual read below, route params winning.
|
|
45
|
+
*/ async updateFileInfo (ctx) {
|
|
46
|
+
const { state: { userAbility, user }, request: { body } } = ctx;
|
|
47
|
+
const id = ctx.params?.id ?? ctx.query.id;
|
|
41
48
|
if (typeof id !== 'string') {
|
|
42
49
|
throw new utils.errors.ValidationError('File id is required');
|
|
43
50
|
}
|
|
@@ -53,8 +60,15 @@ var adminUpload = {
|
|
|
53
60
|
action: constants.ACTIONS.read
|
|
54
61
|
});
|
|
55
62
|
},
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
/**
|
|
64
|
+
* `POST /upload/files/:id/replace`
|
|
65
|
+
*
|
|
66
|
+
* Replace the binary content of an existing file. Also reached through the
|
|
67
|
+
* `POST /upload` multiplexer, which delegates here with the id in `ctx.query`
|
|
68
|
+
* — hence the dual read below, route params winning.
|
|
69
|
+
*/ async replaceFile (ctx) {
|
|
70
|
+
const { state: { userAbility, user }, request: { body, files: { files: filesInput } = {} } } = ctx;
|
|
71
|
+
const id = ctx.params?.id ?? ctx.query.id;
|
|
58
72
|
if (typeof id !== 'string') {
|
|
59
73
|
throw new utils.errors.ValidationError('File id is required');
|
|
60
74
|
}
|
|
@@ -156,14 +170,15 @@ var adminUpload = {
|
|
|
156
170
|
ctx.status = 201;
|
|
157
171
|
},
|
|
158
172
|
/**
|
|
159
|
-
*
|
|
173
|
+
* `POST /upload/files`
|
|
174
|
+
*
|
|
160
175
|
* Upload a single file and return the created File.
|
|
161
176
|
*
|
|
162
177
|
* Accepts one file per request (multipart `files` + `fileInfo`) and returns a
|
|
163
178
|
* single `File` object. Unlike `uploadFiles`, it does **not** run AI metadata
|
|
164
179
|
* generation inline — that responsibility is decoupled and will be handled by a
|
|
165
180
|
* background job. Auth and permission checks mirror `POST /upload`.
|
|
166
|
-
*/ async
|
|
181
|
+
*/ async uploadFile (ctx) {
|
|
167
182
|
const { state: { userAbility, user }, request: { body, files: { files } = {} } } = ctx;
|
|
168
183
|
const uploadService = index.getService('upload');
|
|
169
184
|
const pm = strapi.service('admin::permission').createPermissionsManager({
|
|
@@ -227,7 +242,8 @@ var adminUpload = {
|
|
|
227
242
|
ctx.status = 201;
|
|
228
243
|
},
|
|
229
244
|
/**
|
|
230
|
-
*
|
|
245
|
+
* `POST /upload/actions/upload-from-urls`
|
|
246
|
+
*
|
|
231
247
|
* Upload files from URLs with SSE streaming for per-file progress
|
|
232
248
|
*
|
|
233
249
|
* Accepts JSON body with URLs and fetches them server-side.
|
|
@@ -237,7 +253,7 @@ var adminUpload = {
|
|
|
237
253
|
* - file:complete — when a file is successfully uploaded
|
|
238
254
|
* - file:error — when a URL fetch or upload fails
|
|
239
255
|
* - stream:complete — final summary with all results
|
|
240
|
-
*/ async
|
|
256
|
+
*/ async uploadFromUrls (ctx) {
|
|
241
257
|
const { state: { userAbility, user }, request: { body } } = ctx;
|
|
242
258
|
const uploadService = index.getService('upload');
|
|
243
259
|
const fileService = index.getService('file');
|