@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.
Files changed (198) hide show
  1. package/dist/admin/future/components/TruncatedText.js +76 -0
  2. package/dist/admin/future/components/TruncatedText.js.map +1 -0
  3. package/dist/admin/future/components/TruncatedText.mjs +74 -0
  4. package/dist/admin/future/components/TruncatedText.mjs.map +1 -0
  5. package/dist/admin/future/components/UploadProgressDialog.js +36 -1
  6. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  7. package/dist/admin/future/components/UploadProgressDialog.mjs +37 -2
  8. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  9. package/dist/admin/future/hooks/useApiErrorMessage.js +61 -0
  10. package/dist/admin/future/hooks/useApiErrorMessage.js.map +1 -0
  11. package/dist/admin/future/hooks/useApiErrorMessage.mjs +39 -0
  12. package/dist/admin/future/hooks/useApiErrorMessage.mjs.map +1 -0
  13. package/dist/admin/future/hooks/useTracking.js +57 -0
  14. package/dist/admin/future/hooks/useTracking.js.map +1 -0
  15. package/dist/admin/future/hooks/useTracking.mjs +53 -0
  16. package/dist/admin/future/hooks/useTracking.mjs.map +1 -0
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +31 -0
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +31 -0
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +13 -11
  22. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +13 -11
  24. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +62 -51
  26. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +62 -51
  28. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +3 -4
  30. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +4 -5
  32. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +11 -1
  34. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +11 -1
  36. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsTable.js +6 -4
  38. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +6 -4
  40. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +10 -1
  42. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +10 -1
  44. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +3 -2
  46. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +3 -2
  48. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +4 -2
  50. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +4 -2
  52. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/FilterMenu.js +18 -2
  54. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +18 -2
  56. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +9 -7
  58. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -1
  59. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +9 -7
  60. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
  61. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js +12 -0
  62. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -1
  63. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs +12 -0
  64. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -1
  65. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -38
  66. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +23 -40
  68. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/SortMenu.js +14 -0
  70. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
  71. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +14 -0
  72. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
  73. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +5 -0
  74. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +6 -1
  76. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js +30 -0
  78. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -0
  79. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +26 -0
  80. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -0
  81. package/dist/admin/future/pages/Assets/utils/selection.js +17 -0
  82. package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/utils/selection.mjs +17 -1
  84. package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -1
  85. package/dist/admin/future/services/api.js +123 -19
  86. package/dist/admin/future/services/api.js.map +1 -1
  87. package/dist/admin/future/services/api.mjs +125 -21
  88. package/dist/admin/future/services/api.mjs.map +1 -1
  89. package/dist/admin/future/services/assets.js +11 -23
  90. package/dist/admin/future/services/assets.js.map +1 -1
  91. package/dist/admin/future/services/assets.mjs +11 -23
  92. package/dist/admin/future/services/assets.mjs.map +1 -1
  93. package/dist/admin/future/services/uploadFileViaXHR.js +9 -3
  94. package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
  95. package/dist/admin/future/services/uploadFileViaXHR.mjs +9 -3
  96. package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
  97. package/dist/admin/future/store/uploadProgress.js +44 -5
  98. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  99. package/dist/admin/future/store/uploadProgress.mjs +43 -6
  100. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  101. package/dist/admin/future/utils/getApiErrorMessage.js +20 -0
  102. package/dist/admin/future/utils/getApiErrorMessage.js.map +1 -0
  103. package/dist/admin/future/utils/getApiErrorMessage.mjs +18 -0
  104. package/dist/admin/future/utils/getApiErrorMessage.mjs.map +1 -0
  105. package/dist/admin/hooks/useAIMetadataJob.js +1 -1
  106. package/dist/admin/hooks/useAIMetadataJob.js.map +1 -1
  107. package/dist/admin/hooks/useAIMetadataJob.mjs +1 -1
  108. package/dist/admin/hooks/useAIMetadataJob.mjs.map +1 -1
  109. package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
  110. package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
  111. package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
  112. package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
  113. package/dist/admin/src/future/components/TruncatedText.d.ts +32 -0
  114. package/dist/admin/src/future/hooks/useApiErrorMessage.d.ts +17 -0
  115. package/dist/admin/src/future/hooks/useTracking.d.ts +29 -0
  116. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +5 -3
  117. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +5 -4
  118. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +2 -0
  119. package/dist/admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +18 -0
  120. package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +10 -0
  121. package/dist/admin/src/future/services/api.d.ts +6 -7
  122. package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +6 -1
  123. package/dist/admin/src/future/store/uploadProgress.d.ts +16 -1
  124. package/dist/admin/src/future/utils/getApiErrorMessage.d.ts +1 -0
  125. package/dist/admin/translations/ca.json.js +1 -1
  126. package/dist/admin/translations/ca.json.mjs +1 -1
  127. package/dist/admin/translations/de.json.js +1 -1
  128. package/dist/admin/translations/de.json.mjs +1 -1
  129. package/dist/admin/translations/en.json.js +2 -1
  130. package/dist/admin/translations/en.json.js.map +1 -1
  131. package/dist/admin/translations/en.json.mjs +2 -1
  132. package/dist/admin/translations/en.json.mjs.map +1 -1
  133. package/dist/admin/translations/es.json.js +1 -1
  134. package/dist/admin/translations/es.json.mjs +1 -1
  135. package/dist/admin/translations/fr.json.js +1 -1
  136. package/dist/admin/translations/fr.json.mjs +1 -1
  137. package/dist/admin/translations/ja.json.js +1 -1
  138. package/dist/admin/translations/ja.json.mjs +1 -1
  139. package/dist/admin/translations/ko.json.js +1 -1
  140. package/dist/admin/translations/ko.json.mjs +1 -1
  141. package/dist/admin/translations/nl.json.js +1 -1
  142. package/dist/admin/translations/nl.json.mjs +1 -1
  143. package/dist/admin/translations/pl.json.js +1 -1
  144. package/dist/admin/translations/pl.json.mjs +1 -1
  145. package/dist/admin/translations/pt-BR.json.js +1 -1
  146. package/dist/admin/translations/pt-BR.json.mjs +1 -1
  147. package/dist/admin/translations/ru.json.js +1 -1
  148. package/dist/admin/translations/ru.json.mjs +1 -1
  149. package/dist/admin/translations/sk.json.js +1 -1
  150. package/dist/admin/translations/sk.json.mjs +1 -1
  151. package/dist/admin/translations/tr.json.js +1 -1
  152. package/dist/admin/translations/tr.json.mjs +1 -1
  153. package/dist/admin/translations/uk.json.js +1 -1
  154. package/dist/admin/translations/uk.json.mjs +1 -1
  155. package/dist/admin/translations/zh-Hans.json.js +1 -1
  156. package/dist/admin/translations/zh-Hans.json.mjs +1 -1
  157. package/dist/admin/translations/zh.json.js +1 -1
  158. package/dist/admin/translations/zh.json.mjs +1 -1
  159. package/dist/server/controllers/admin-file.js +27 -9
  160. package/dist/server/controllers/admin-file.js.map +1 -1
  161. package/dist/server/controllers/admin-file.mjs +27 -9
  162. package/dist/server/controllers/admin-file.mjs.map +1 -1
  163. package/dist/server/controllers/admin-upload.js +24 -8
  164. package/dist/server/controllers/admin-upload.js.map +1 -1
  165. package/dist/server/controllers/admin-upload.mjs +24 -8
  166. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  167. package/dist/server/routes/admin.js +41 -21
  168. package/dist/server/routes/admin.js.map +1 -1
  169. package/dist/server/routes/admin.mjs +41 -21
  170. package/dist/server/routes/admin.mjs.map +1 -1
  171. package/dist/server/services/ai-metadata.js +25 -5
  172. package/dist/server/services/ai-metadata.js.map +1 -1
  173. package/dist/server/services/ai-metadata.mjs +1 -1
  174. package/dist/server/services/ai-metadata.mjs.map +1 -1
  175. package/dist/server/services/upload.js +4 -0
  176. package/dist/server/services/upload.js.map +1 -1
  177. package/dist/server/services/upload.mjs +4 -0
  178. package/dist/server/services/upload.mjs.map +1 -1
  179. package/dist/server/src/controllers/admin-file.d.ts +29 -8
  180. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  181. package/dist/server/src/controllers/admin-upload.d.ts +20 -4
  182. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  183. package/dist/server/src/controllers/index.d.ts +4 -4
  184. package/dist/server/src/index.d.ts +4 -4
  185. package/dist/server/src/routes/admin.d.ts.map +1 -1
  186. package/dist/server/src/services/ai-metadata.d.ts +2 -2
  187. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  188. package/dist/server/src/services/upload.d.ts.map +1 -1
  189. package/dist/shared/contracts/ai-metadata-jobs.d.ts +10 -32
  190. package/dist/shared/contracts/ai-metadata-jobs.d.ts.map +1 -1
  191. package/dist/shared/contracts/files.d.ts +10 -10
  192. package/dist/shared/contracts/files.d.ts.map +1 -1
  193. package/package.json +7 -7
  194. package/dist/admin/future/utils/getBulkMoveErrorMessage.js +0 -23
  195. package/dist/admin/future/utils/getBulkMoveErrorMessage.js.map +0 -1
  196. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs +0 -21
  197. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs.map +0 -1
  198. package/dist/admin/src/future/utils/getBulkMoveErrorMessage.d.ts +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"admin-upload.js","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\nimport fse from 'fs-extra';\nimport _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { Config, FileInfo } from '../types';\nimport { prepareUploadRequest, type FileUploadError } from '../utils/mime-validation';\nimport type { UploadFileInfo } from '../../../shared/contracts/files';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(updated);\n\n return pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(file);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(filesInput) && filesInput.length > 1) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const files = Array.isArray(filesInput) ? filesInput[0] : filesInput;\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: validFiles[0] }, { user });\n\n // Regenerate AI metadata for image replacements so the alt text / caption\n // reflect the new file content. Mirrors the post-upload hook in\n // `uploadFiles`; failure is logged and swallowed to keep the replace flow\n // resilient when the AI provider is unavailable.\n const aiMetadataService = getService('aiMetadata');\n if (replacedFile?.mime?.startsWith('image/') && (await aiMetadataService.isEnabled())) {\n try {\n const metadataResults = await aiMetadataService.processFiles([replacedFile]);\n await aiMetadataService.updateFilesWithAIMetadata([replacedFile], metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed on replace, proceeding without it', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const isMultipleFiles = validFiles.length > 1;\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - generate AI metadata for images\n if (await aiMetadataService.isEnabled()) {\n try {\n const metadataResults = await aiMetadataService.processFiles(uploadedFiles);\n // Update the uploaded files with AI metadata\n await aiMetadataService.updateFilesWithAIMetadata(uploadedFiles, metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * @experimental\n * Upload a single file and return the created File.\n *\n * Accepts one file per request (multipart `files` + `fileInfo`) and returns a\n * single `File` object. Unlike `uploadFiles`, it does **not** run AI metadata\n * generation inline — that responsibility is decoupled and will be handled by a\n * background job. Auth and permission checks mirror `POST /upload`.\n */\n async unstable_uploadFile(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n throw new errors.ApplicationError('Files are empty');\n }\n\n // Accept a single file per request; ignore any extras defensively.\n const file = Array.isArray(files) ? files[0] : files;\n const fileName = file.originalFilename || 'unknown';\n\n // Parse the single fileInfo object from the multipart body.\n let fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: null,\n };\n if (body?.fileInfo) {\n const raw = body.fileInfo;\n if (typeof raw === 'string') {\n fileInfo = JSON.parse(raw);\n } else if (Array.isArray(raw)) {\n fileInfo = (typeof raw[0] === 'string' ? JSON.parse(raw[0]) : raw[0]) as UploadFileInfo;\n } else {\n fileInfo = raw as UploadFileInfo;\n }\n }\n\n // Validate this single file using security checks.\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0]?.message || 'Validation failed');\n }\n\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload({ data, files: [validFiles[0]] }, { user });\n\n if (uploadedFile.mime?.startsWith('image/')) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // No inline AI metadata generation — intentionally decoupled for the async job.\n\n // Sign file url for private providers.\n const signedFile = await getService('file').signFileUrls(uploadedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * @experimental\n * Upload files from URLs with SSE streaming for per-file progress\n *\n * Accepts JSON body with URLs and fetches them server-side.\n * Streams Server-Sent Events as each URL is fetched and uploaded:\n * - file:fetching — when starting to fetch a URL\n * - file:uploading — when upload starts for a fetched file\n * - file:complete — when a file is successfully uploaded\n * - file:error — when a URL fetch or upload fails\n * - stream:complete — final summary with all results\n */\n async unstable_uploadFromUrls(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const uploadService = getService('upload');\n const fileService = getService('file');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // Parse and validate request body\n const { urls, folderId } = body as { urls?: string[]; folderId?: number | null };\n\n if (!urls || !Array.isArray(urls) || urls.length === 0) {\n throw new errors.ApplicationError('URLs are required');\n }\n\n if (urls.length > 20) {\n throw new errors.ApplicationError('Maximum 20 URLs allowed per request');\n }\n\n // Take manual control of the response for SSE streaming\n ctx.respond = false;\n const res = ctx.res;\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n const writeSSE = (event: string, data: Record<string, unknown>) => {\n res.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n };\n\n const total = urls.length;\n const uploadErrors: FileUploadError[] = [];\n const successfulFiles: any[] = [];\n\n // Create temp directory for fetched files\n const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-url-upload-'));\n const { sizeLimit } = strapi.config.get<Config>('plugin::upload');\n\n try {\n // Process each URL sequentially\n for (let i = 0; i < urls.length; i += 1) {\n const url = urls[i];\n\n writeSSE('file:fetching', { url, index: i, total });\n\n try {\n // Fetch URL to temp file\n const { file } = await fileService.fetchUrlToInputFile(\n url,\n tmpWorkingDirectory,\n sizeLimit\n );\n const fileName = file.originalFilename;\n\n writeSSE('file:uploading', {\n name: fileName,\n index: i,\n total,\n size: file.size,\n });\n\n // Validate using security checks\n const fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: folderId ?? null,\n };\n\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n const errorMessage = validationErrors[0]?.message || 'Validation failed';\n uploadErrors.push({ name: fileName, message: errorMessage });\n writeSSE('file:error', { name: fileName, url, index: i, message: errorMessage });\n } else {\n // Upload the file\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload(\n { data, files: [validFiles[0]] },\n { user }\n );\n\n // Sign file url\n const signedFile = await fileService.signFileUrls(uploadedFile);\n successfulFiles.push(signedFile);\n\n writeSSE('file:complete', { name: fileName, index: i, file: signedFile });\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n uploadErrors.push({ name: url, message: errorMessage });\n writeSSE('file:error', { url, index: i, message: errorMessage });\n }\n }\n\n // Track image upload metric once if any images were uploaded\n if (successfulFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // Send final stream summary\n writeSSE('stream:complete', {\n data: await pm.sanitizeOutput(successfulFiles, { action: ACTIONS.read }),\n errors: uploadErrors,\n });\n } finally {\n // Clean up temp directory\n await fse.remove(tmpWorkingDirectory);\n }\n\n res.end();\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","signedFile","signFileUrls","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","filesInput","Array","isArray","length","ApplicationError","validFiles","filteredBody","validationErrors","prepareUploadRequest","strapi","message","replacedFile","replace","aiMetadataService","mime","startsWith","isEnabled","metadataResults","processFiles","updateFilesWithAIMetadata","error","log","warn","Error","String","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","isMultipleFiles","filesArray","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","trackUsage","signedFiles","status","unstable_uploadFile","_","isEmpty","size","fileName","caption","alternativeText","folder","raw","JSON","parse","stringify","uploadedFile","unstable_uploadFromUrls","fileService","urls","folderId","respond","res","writeHead","Connection","writeSSE","event","write","total","uploadErrors","successfulFiles","tmpWorkingDirectory","fse","mkdtemp","path","join","os","tmpdir","sizeLimit","config","get","i","url","index","fetchUrlToInputFile","errorMessage","push","remove","end"],"mappings":"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"}
1
+ {"version":3,"file":"admin-upload.js","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\nimport fse from 'fs-extra';\nimport _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { Config, FileInfo } from '../types';\nimport { prepareUploadRequest, type FileUploadError } from '../utils/mime-validation';\nimport type { UploadFileInfo } from '../../../shared/contracts/files';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(updated);\n\n return pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n /**\n * `PUT /upload/files/:id`\n *\n * Update the editable metadata (`fileInfo`) of an existing file. Also reached\n * through the `POST /upload` multiplexer, which delegates here with the id in\n * `ctx.query` — hence the dual read below, route params winning.\n */\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const id = ctx.params?.id ?? ctx.query.id;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(file);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n /**\n * `POST /upload/files/:id/replace`\n *\n * Replace the binary content of an existing file. Also reached through the\n * `POST /upload` multiplexer, which delegates here with the id in `ctx.query`\n * — hence the dual read below, route params winning.\n */\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n const id = ctx.params?.id ?? ctx.query.id;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(filesInput) && filesInput.length > 1) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const files = Array.isArray(filesInput) ? filesInput[0] : filesInput;\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: validFiles[0] }, { user });\n\n // Regenerate AI metadata for image replacements so the alt text / caption\n // reflect the new file content. Mirrors the post-upload hook in\n // `uploadFiles`; failure is logged and swallowed to keep the replace flow\n // resilient when the AI provider is unavailable.\n const aiMetadataService = getService('aiMetadata');\n if (replacedFile?.mime?.startsWith('image/') && (await aiMetadataService.isEnabled())) {\n try {\n const metadataResults = await aiMetadataService.processFiles([replacedFile]);\n await aiMetadataService.updateFilesWithAIMetadata([replacedFile], metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed on replace, proceeding without it', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const isMultipleFiles = validFiles.length > 1;\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - generate AI metadata for images\n if (await aiMetadataService.isEnabled()) {\n try {\n const metadataResults = await aiMetadataService.processFiles(uploadedFiles);\n // Update the uploaded files with AI metadata\n await aiMetadataService.updateFilesWithAIMetadata(uploadedFiles, metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * `POST /upload/files`\n *\n * Upload a single file and return the created File.\n *\n * Accepts one file per request (multipart `files` + `fileInfo`) and returns a\n * single `File` object. Unlike `uploadFiles`, it does **not** run AI metadata\n * generation inline — that responsibility is decoupled and will be handled by a\n * background job. Auth and permission checks mirror `POST /upload`.\n */\n async uploadFile(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n throw new errors.ApplicationError('Files are empty');\n }\n\n // Accept a single file per request; ignore any extras defensively.\n const file = Array.isArray(files) ? files[0] : files;\n const fileName = file.originalFilename || 'unknown';\n\n // Parse the single fileInfo object from the multipart body.\n let fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: null,\n };\n if (body?.fileInfo) {\n const raw = body.fileInfo;\n if (typeof raw === 'string') {\n fileInfo = JSON.parse(raw);\n } else if (Array.isArray(raw)) {\n fileInfo = (typeof raw[0] === 'string' ? JSON.parse(raw[0]) : raw[0]) as UploadFileInfo;\n } else {\n fileInfo = raw as UploadFileInfo;\n }\n }\n\n // Validate this single file using security checks.\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0]?.message || 'Validation failed');\n }\n\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload({ data, files: [validFiles[0]] }, { user });\n\n if (uploadedFile.mime?.startsWith('image/')) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // No inline AI metadata generation — intentionally decoupled for the async job.\n\n // Sign file url for private providers.\n const signedFile = await getService('file').signFileUrls(uploadedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * `POST /upload/actions/upload-from-urls`\n *\n * Upload files from URLs with SSE streaming for per-file progress\n *\n * Accepts JSON body with URLs and fetches them server-side.\n * Streams Server-Sent Events as each URL is fetched and uploaded:\n * - file:fetching — when starting to fetch a URL\n * - file:uploading — when upload starts for a fetched file\n * - file:complete — when a file is successfully uploaded\n * - file:error — when a URL fetch or upload fails\n * - stream:complete — final summary with all results\n */\n async uploadFromUrls(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const uploadService = getService('upload');\n const fileService = getService('file');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // Parse and validate request body\n const { urls, folderId } = body as { urls?: string[]; folderId?: number | null };\n\n if (!urls || !Array.isArray(urls) || urls.length === 0) {\n throw new errors.ApplicationError('URLs are required');\n }\n\n if (urls.length > 20) {\n throw new errors.ApplicationError('Maximum 20 URLs allowed per request');\n }\n\n // Take manual control of the response for SSE streaming\n ctx.respond = false;\n const res = ctx.res;\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n const writeSSE = (event: string, data: Record<string, unknown>) => {\n res.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n };\n\n const total = urls.length;\n const uploadErrors: FileUploadError[] = [];\n const successfulFiles: any[] = [];\n\n // Create temp directory for fetched files\n const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-url-upload-'));\n const { sizeLimit } = strapi.config.get<Config>('plugin::upload');\n\n try {\n // Process each URL sequentially\n for (let i = 0; i < urls.length; i += 1) {\n const url = urls[i];\n\n writeSSE('file:fetching', { url, index: i, total });\n\n try {\n // Fetch URL to temp file\n const { file } = await fileService.fetchUrlToInputFile(\n url,\n tmpWorkingDirectory,\n sizeLimit\n );\n const fileName = file.originalFilename;\n\n writeSSE('file:uploading', {\n name: fileName,\n index: i,\n total,\n size: file.size,\n });\n\n // Validate using security checks\n const fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: folderId ?? null,\n };\n\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n const errorMessage = validationErrors[0]?.message || 'Validation failed';\n uploadErrors.push({ name: fileName, message: errorMessage });\n writeSSE('file:error', { name: fileName, url, index: i, message: errorMessage });\n } else {\n // Upload the file\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload(\n { data, files: [validFiles[0]] },\n { user }\n );\n\n // Sign file url\n const signedFile = await fileService.signFileUrls(uploadedFile);\n successfulFiles.push(signedFile);\n\n writeSSE('file:complete', { name: fileName, index: i, file: signedFile });\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n uploadErrors.push({ name: url, message: errorMessage });\n writeSSE('file:error', { url, index: i, message: errorMessage });\n }\n }\n\n // Track image upload metric once if any images were uploaded\n if (successfulFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // Send final stream summary\n writeSSE('stream:complete', {\n data: await pm.sanitizeOutput(successfulFiles, { action: ACTIONS.read }),\n errors: uploadErrors,\n });\n } finally {\n // Clean up temp directory\n await fse.remove(tmpWorkingDirectory);\n }\n\n res.end();\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","signedFile","signFileUrls","sanitizeOutput","action","read","params","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","filesInput","Array","isArray","length","ApplicationError","validFiles","filteredBody","validationErrors","prepareUploadRequest","strapi","message","replacedFile","replace","aiMetadataService","mime","startsWith","isEnabled","metadataResults","processFiles","updateFilesWithAIMetadata","error","log","warn","Error","String","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","isMultipleFiles","filesArray","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","trackUsage","signedFiles","status","uploadFile","_","isEmpty","size","fileName","caption","alternativeText","folder","raw","JSON","parse","stringify","uploadedFile","uploadFromUrls","fileService","urls","folderId","respond","res","writeHead","Connection","writeSSE","event","write","total","uploadErrors","successfulFiles","tmpWorkingDirectory","fse","mkdtemp","path","join","os","tmpdir","sizeLimit","config","get","i","url","index","fetchUrlToInputFile","errorMessage","push","remove","end"],"mappings":"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"}
@@ -27,8 +27,15 @@ var adminUpload = {
27
27
  });
28
28
  ctx.body = results;
29
29
  },
30
- async updateFileInfo (ctx) {
31
- const { state: { userAbility, user }, query: { id }, request: { body } } = ctx;
30
+ /**
31
+ * `PUT /upload/files/:id`
32
+ *
33
+ * Update the editable metadata (`fileInfo`) of an existing file. Also reached
34
+ * through the `POST /upload` multiplexer, which delegates here with the id in
35
+ * `ctx.query` — hence the dual read below, route params winning.
36
+ */ async updateFileInfo (ctx) {
37
+ const { state: { userAbility, user }, request: { body } } = ctx;
38
+ const id = ctx.params?.id ?? ctx.query.id;
32
39
  if (typeof id !== 'string') {
33
40
  throw new errors.ValidationError('File id is required');
34
41
  }
@@ -44,8 +51,15 @@ var adminUpload = {
44
51
  action: ACTIONS.read
45
52
  });
46
53
  },
47
- async replaceFile (ctx) {
48
- const { state: { userAbility, user }, query: { id }, request: { body, files: { files: filesInput } = {} } } = ctx;
54
+ /**
55
+ * `POST /upload/files/:id/replace`
56
+ *
57
+ * Replace the binary content of an existing file. Also reached through the
58
+ * `POST /upload` multiplexer, which delegates here with the id in `ctx.query`
59
+ * — hence the dual read below, route params winning.
60
+ */ async replaceFile (ctx) {
61
+ const { state: { userAbility, user }, request: { body, files: { files: filesInput } = {} } } = ctx;
62
+ const id = ctx.params?.id ?? ctx.query.id;
49
63
  if (typeof id !== 'string') {
50
64
  throw new errors.ValidationError('File id is required');
51
65
  }
@@ -147,14 +161,15 @@ var adminUpload = {
147
161
  ctx.status = 201;
148
162
  },
149
163
  /**
150
- * @experimental
164
+ * `POST /upload/files`
165
+ *
151
166
  * Upload a single file and return the created File.
152
167
  *
153
168
  * Accepts one file per request (multipart `files` + `fileInfo`) and returns a
154
169
  * single `File` object. Unlike `uploadFiles`, it does **not** run AI metadata
155
170
  * generation inline — that responsibility is decoupled and will be handled by a
156
171
  * background job. Auth and permission checks mirror `POST /upload`.
157
- */ async unstable_uploadFile (ctx) {
172
+ */ async uploadFile (ctx) {
158
173
  const { state: { userAbility, user }, request: { body, files: { files } = {} } } = ctx;
159
174
  const uploadService = getService('upload');
160
175
  const pm = strapi.service('admin::permission').createPermissionsManager({
@@ -218,7 +233,8 @@ var adminUpload = {
218
233
  ctx.status = 201;
219
234
  },
220
235
  /**
221
- * @experimental
236
+ * `POST /upload/actions/upload-from-urls`
237
+ *
222
238
  * Upload files from URLs with SSE streaming for per-file progress
223
239
  *
224
240
  * Accepts JSON body with URLs and fetches them server-side.
@@ -228,7 +244,7 @@ var adminUpload = {
228
244
  * - file:complete — when a file is successfully uploaded
229
245
  * - file:error — when a URL fetch or upload fails
230
246
  * - stream:complete — final summary with all results
231
- */ async unstable_uploadFromUrls (ctx) {
247
+ */ async uploadFromUrls (ctx) {
232
248
  const { state: { userAbility, user }, request: { body } } = ctx;
233
249
  const uploadService = getService('upload');
234
250
  const fileService = getService('file');
@@ -1 +1 @@
1
- {"version":3,"file":"admin-upload.mjs","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\nimport fse from 'fs-extra';\nimport _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { Config, FileInfo } from '../types';\nimport { prepareUploadRequest, type FileUploadError } from '../utils/mime-validation';\nimport type { UploadFileInfo } from '../../../shared/contracts/files';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(updated);\n\n return pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(file);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n query: { id },\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(filesInput) && filesInput.length > 1) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const files = Array.isArray(filesInput) ? filesInput[0] : filesInput;\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: validFiles[0] }, { user });\n\n // Regenerate AI metadata for image replacements so the alt text / caption\n // reflect the new file content. Mirrors the post-upload hook in\n // `uploadFiles`; failure is logged and swallowed to keep the replace flow\n // resilient when the AI provider is unavailable.\n const aiMetadataService = getService('aiMetadata');\n if (replacedFile?.mime?.startsWith('image/') && (await aiMetadataService.isEnabled())) {\n try {\n const metadataResults = await aiMetadataService.processFiles([replacedFile]);\n await aiMetadataService.updateFilesWithAIMetadata([replacedFile], metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed on replace, proceeding without it', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const isMultipleFiles = validFiles.length > 1;\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - generate AI metadata for images\n if (await aiMetadataService.isEnabled()) {\n try {\n const metadataResults = await aiMetadataService.processFiles(uploadedFiles);\n // Update the uploaded files with AI metadata\n await aiMetadataService.updateFilesWithAIMetadata(uploadedFiles, metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * @experimental\n * Upload a single file and return the created File.\n *\n * Accepts one file per request (multipart `files` + `fileInfo`) and returns a\n * single `File` object. Unlike `uploadFiles`, it does **not** run AI metadata\n * generation inline — that responsibility is decoupled and will be handled by a\n * background job. Auth and permission checks mirror `POST /upload`.\n */\n async unstable_uploadFile(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n throw new errors.ApplicationError('Files are empty');\n }\n\n // Accept a single file per request; ignore any extras defensively.\n const file = Array.isArray(files) ? files[0] : files;\n const fileName = file.originalFilename || 'unknown';\n\n // Parse the single fileInfo object from the multipart body.\n let fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: null,\n };\n if (body?.fileInfo) {\n const raw = body.fileInfo;\n if (typeof raw === 'string') {\n fileInfo = JSON.parse(raw);\n } else if (Array.isArray(raw)) {\n fileInfo = (typeof raw[0] === 'string' ? JSON.parse(raw[0]) : raw[0]) as UploadFileInfo;\n } else {\n fileInfo = raw as UploadFileInfo;\n }\n }\n\n // Validate this single file using security checks.\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0]?.message || 'Validation failed');\n }\n\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload({ data, files: [validFiles[0]] }, { user });\n\n if (uploadedFile.mime?.startsWith('image/')) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // No inline AI metadata generation — intentionally decoupled for the async job.\n\n // Sign file url for private providers.\n const signedFile = await getService('file').signFileUrls(uploadedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * @experimental\n * Upload files from URLs with SSE streaming for per-file progress\n *\n * Accepts JSON body with URLs and fetches them server-side.\n * Streams Server-Sent Events as each URL is fetched and uploaded:\n * - file:fetching — when starting to fetch a URL\n * - file:uploading — when upload starts for a fetched file\n * - file:complete — when a file is successfully uploaded\n * - file:error — when a URL fetch or upload fails\n * - stream:complete — final summary with all results\n */\n async unstable_uploadFromUrls(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const uploadService = getService('upload');\n const fileService = getService('file');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // Parse and validate request body\n const { urls, folderId } = body as { urls?: string[]; folderId?: number | null };\n\n if (!urls || !Array.isArray(urls) || urls.length === 0) {\n throw new errors.ApplicationError('URLs are required');\n }\n\n if (urls.length > 20) {\n throw new errors.ApplicationError('Maximum 20 URLs allowed per request');\n }\n\n // Take manual control of the response for SSE streaming\n ctx.respond = false;\n const res = ctx.res;\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n const writeSSE = (event: string, data: Record<string, unknown>) => {\n res.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n };\n\n const total = urls.length;\n const uploadErrors: FileUploadError[] = [];\n const successfulFiles: any[] = [];\n\n // Create temp directory for fetched files\n const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-url-upload-'));\n const { sizeLimit } = strapi.config.get<Config>('plugin::upload');\n\n try {\n // Process each URL sequentially\n for (let i = 0; i < urls.length; i += 1) {\n const url = urls[i];\n\n writeSSE('file:fetching', { url, index: i, total });\n\n try {\n // Fetch URL to temp file\n const { file } = await fileService.fetchUrlToInputFile(\n url,\n tmpWorkingDirectory,\n sizeLimit\n );\n const fileName = file.originalFilename;\n\n writeSSE('file:uploading', {\n name: fileName,\n index: i,\n total,\n size: file.size,\n });\n\n // Validate using security checks\n const fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: folderId ?? null,\n };\n\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n const errorMessage = validationErrors[0]?.message || 'Validation failed';\n uploadErrors.push({ name: fileName, message: errorMessage });\n writeSSE('file:error', { name: fileName, url, index: i, message: errorMessage });\n } else {\n // Upload the file\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload(\n { data, files: [validFiles[0]] },\n { user }\n );\n\n // Sign file url\n const signedFile = await fileService.signFileUrls(uploadedFile);\n successfulFiles.push(signedFile);\n\n writeSSE('file:complete', { name: fileName, index: i, file: signedFile });\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n uploadErrors.push({ name: url, message: errorMessage });\n writeSSE('file:error', { url, index: i, message: errorMessage });\n }\n }\n\n // Track image upload metric once if any images were uploaded\n if (successfulFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // Send final stream summary\n writeSSE('stream:complete', {\n data: await pm.sanitizeOutput(successfulFiles, { action: ACTIONS.read }),\n errors: uploadErrors,\n });\n } finally {\n // Clean up temp directory\n await fse.remove(tmpWorkingDirectory);\n }\n\n res.end();\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","signedFile","signFileUrls","sanitizeOutput","action","read","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","filesInput","Array","isArray","length","ApplicationError","validFiles","filteredBody","validationErrors","prepareUploadRequest","strapi","message","replacedFile","replace","aiMetadataService","mime","startsWith","isEnabled","metadataResults","processFiles","updateFilesWithAIMetadata","error","log","warn","Error","String","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","isMultipleFiles","filesArray","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","trackUsage","signedFiles","status","unstable_uploadFile","_","isEmpty","size","fileName","caption","alternativeText","folder","raw","JSON","parse","stringify","uploadedFile","unstable_uploadFromUrls","fileService","urls","folderId","respond","res","writeHead","Connection","writeSSE","event","write","total","uploadErrors","successfulFiles","tmpWorkingDirectory","fse","mkdtemp","path","join","os","tmpdir","sizeLimit","config","get","i","url","index","fetchUrlToInputFile","errorMessage","push","remove","end"],"mappings":";;;;;;;;;;;AAgBA,kBAAe;AACb,IAAA,MAAMA,oBAAmBC,GAAY,EAAA;AACnC,QAAA,MAAM,EACJC,KAAAA,EAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EAClB,GAAGL,GAAAA;AAEJ,QAAA,MAAM,EAAEM,OAAO,EAAE,GAAG,MAAMC,sBAAAA,CAAuBF,IAAAA,CAAAA;AACjD,QAAA,MAAMG,gBAAgBC,UAAAA,CAAW,QAAA,CAAA;QAEjC,MAAMC,OAAAA,GAAU,MAAMC,KAAAA,CAAMC,GAAG,CAC7BN,OAAAA,EACA,OAAO,EAAEO,EAAE,EAAEC,QAAQ,EAAsC,GAAA;YACzD,MAAM,EAAEC,EAAE,EAAE,GAAG,MAAMC,8BACnBd,WAAAA,EACAe,OAAAA,CAAQC,MAAM,EACdC,cAAAA,EACAN,EAAAA,CAAAA;AAGF,YAAA,MAAMO,UAAU,MAAMZ,aAAAA,CAAca,cAAc,CAACR,IAAIC,QAAAA,EAAiB;AAAEX,gBAAAA;AAAK,aAAA,CAAA;;AAG/E,YAAA,MAAMmB,UAAAA,GAAa,MAAMb,UAAAA,CAAW,MAAA,CAAA,CAAQc,YAAY,CAACH,OAAAA,CAAAA;YAEzD,OAAOL,EAAAA,CAAGS,cAAc,CAACF,UAAAA,EAAY;AAAEG,gBAAAA,MAAAA,EAAQR,QAAQS;AAAK,aAAA,CAAA;AAC9D,QAAA,CAAA,CAAA;AAGF1B,QAAAA,GAAAA,CAAIK,IAAI,GAAGK,OAAAA;AACb,IAAA,CAAA;AAEA,IAAA,MAAMW,gBAAerB,GAAY,EAAA;AAC/B,QAAA,MAAM,EACJC,KAAAA,EAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BwB,KAAAA,EAAO,EAAEd,EAAE,EAAE,EACbT,OAAAA,EAAS,EAAEC,IAAI,EAAE,EAClB,GAAGL,GAAAA;QAEJ,IAAI,OAAOa,OAAO,QAAA,EAAU;YAC1B,MAAM,IAAIe,MAAAA,CAAOC,eAAe,CAAC,qBAAA,CAAA;AACnC,QAAA;AAEA,QAAA,MAAMrB,gBAAgBC,UAAAA,CAAW,QAAA,CAAA;QACjC,MAAM,EAAEM,EAAE,EAAE,GAAG,MAAMC,8BACnBd,WAAAA,EACAe,OAAAA,CAAQC,MAAM,EACdC,cAAAA,EACAN,EAAAA,CAAAA;QAGF,MAAMiB,IAAAA,GAAO,MAAMC,kBAAAA,CAAmB1B,IAAAA,CAAAA;QAEtC,MAAM2B,IAAAA,GAAO,MAAMxB,aAAAA,CAAca,cAAc,CAACR,EAAAA,EAAIiB,IAAAA,CAAKhB,QAAQ,EAAS;AAAEX,YAAAA;AAAK,SAAA,CAAA;;AAGjF,QAAA,MAAMmB,UAAAA,GAAa,MAAMb,UAAAA,CAAW,MAAA,CAAA,CAAQc,YAAY,CAACS,IAAAA,CAAAA;AAEzDhC,QAAAA,GAAAA,CAAIK,IAAI,GAAG,MAAMU,EAAAA,CAAGS,cAAc,CAACF,UAAAA,EAAY;AAAEG,YAAAA,MAAAA,EAAQR,QAAQS;AAAK,SAAA,CAAA;AACxE,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAYjC,GAAY,EAAA;QAC5B,MAAM,EACJC,KAAAA,EAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BwB,KAAAA,EAAO,EAAEd,EAAE,EAAE,EACbT,OAAAA,EAAS,EAAEC,IAAI,EAAE6B,KAAAA,EAAO,EAAEA,KAAAA,EAAOC,UAAU,EAAE,GAAG,EAAE,EAAE,EACrD,GAAGnC,GAAAA;QAEJ,IAAI,OAAOa,OAAO,QAAA,EAAU;YAC1B,MAAM,IAAIe,MAAAA,CAAOC,eAAe,CAAC,qBAAA,CAAA;AACnC,QAAA;AAEA,QAAA,MAAMrB,gBAAgBC,UAAAA,CAAW,QAAA,CAAA;QACjC,MAAM,EAAEM,EAAE,EAAE,GAAG,MAAMC,8BACnBd,WAAAA,EACAe,OAAAA,CAAQC,MAAM,EACdC,cAAAA,EACAN,EAAAA,CAAAA;AAGF,QAAA,IAAIuB,MAAMC,OAAO,CAACF,eAAeA,UAAAA,CAAWG,MAAM,GAAG,CAAA,EAAG;YACtD,MAAM,IAAIV,MAAAA,CAAOW,gBAAgB,CAAC,0CAAA,CAAA;AACpC,QAAA;QAEA,MAAML,KAAAA,GAAQE,MAAMC,OAAO,CAACF,cAAcA,UAAU,CAAC,EAAE,GAAGA,UAAAA;AAE1D,QAAA,MAAM,EACJK,UAAU,EACVC,YAAY,EACZb,MAAAA,EAAQc,gBAAgB,EACzB,GAAG,MAAMC,oBAAAA,CAAqBT,KAAAA,EAAO7B,IAAAA,EAAMuC,MAAAA,CAAAA;QAC5C,IAAIJ,UAAAA,CAAWF,MAAM,KAAK,CAAA,EAAG;YAC3B,MAAM,IAAIV,OAAOC,eAAe,CAACa,gBAAgB,CAAC,CAAA,CAAE,CAACG,OAAO,CAAA;AAC9D,QAAA;QAEA,MAAMf,IAAAA,GAAQ,MAAMC,kBAAAA,CAAmBU,YAAAA,CAAAA;AACvC,QAAA,MAAMK,YAAAA,GAAe,MAAMtC,aAAAA,CAAcuC,OAAO,CAAClC,EAAAA,EAAI;AAAEiB,YAAAA,IAAAA;YAAME,IAAAA,EAAMQ,UAAU,CAAC,CAAA;SAAG,EAAG;AAAErC,YAAAA;AAAK,SAAA,CAAA;;;;;AAM3F,QAAA,MAAM6C,oBAAoBvC,UAAAA,CAAW,YAAA,CAAA;AACrC,QAAA,IAAIqC,cAAcG,IAAAA,EAAMC,UAAAA,CAAW,aAAc,MAAMF,iBAAAA,CAAkBG,SAAS,EAAA,EAAK;YACrF,IAAI;AACF,gBAAA,MAAMC,eAAAA,GAAkB,MAAMJ,iBAAAA,CAAkBK,YAAY,CAAC;AAACP,oBAAAA;AAAa,iBAAA,CAAA;gBAC3E,MAAME,iBAAAA,CAAkBM,yBAAyB,CAAC;AAACR,oBAAAA;AAAa,iBAAA,EAAEM,eAAAA,EAAiBjD,IAAAA,CAAAA;AACrF,YAAA,CAAA,CAAE,OAAOoD,KAAAA,EAAO;AACdX,gBAAAA,MAAAA,CAAOY,GAAG,CAACC,IAAI,CAAC,iEAAA,EAAmE;AACjFF,oBAAAA,KAAAA,EAAOA,KAAAA,YAAiBG,KAAAA,GAAQH,KAAAA,CAAMV,OAAO,GAAGc,MAAAA,CAAOJ,KAAAA;AACzD,iBAAA,CAAA;AACF,YAAA;AACF,QAAA;;AAGA,QAAA,MAAMjC,UAAAA,GAAa,MAAMb,UAAAA,CAAW,MAAA,CAAA,CAAQc,YAAY,CAACuB,YAAAA,CAAAA;AAEzD9C,QAAAA,GAAAA,CAAIK,IAAI,GAAG,MAAMU,EAAAA,CAAGS,cAAc,CAACF,UAAAA,EAAY;AAAEG,YAAAA,MAAAA,EAAQR,QAAQS;AAAK,SAAA,CAAA;AACxE,IAAA,CAAA;AAEA,IAAA,MAAMkC,aAAY5D,GAAY,EAAA;QAC5B,MAAM,EACJC,OAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BC,OAAAA,EAAS,EAAEC,IAAI,EAAE6B,KAAAA,EAAO,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACzC,GAAGlC,GAAAA;AAEJ,QAAA,MAAMQ,gBAAgBC,UAAAA,CAAW,QAAA,CAAA;AACjC,QAAA,MAAMM,KAAK6B,MAAAA,CAAOiB,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAAA,EAAS7D,WAAAA;AACTuB,YAAAA,MAAAA,EAAQR,QAAQ+C,MAAM;YACtBC,KAAAA,EAAO9C;AACT,SAAA,CAAA;QAEA,IAAI,CAACJ,EAAAA,CAAGmD,SAAS,EAAE;AACjB,YAAA,OAAOlE,IAAImE,SAAS,EAAA;AACtB,QAAA;AAEA,QAAA,MAAM,EACJ3B,UAAU,EACVC,YAAY,EACZb,MAAAA,EAAQc,gBAAgB,EACzB,GAAG,MAAMC,oBAAAA,CAAqBT,KAAAA,EAAO7B,IAAAA,EAAMuC,MAAAA,CAAAA;QAC5C,IAAIJ,UAAAA,CAAWF,MAAM,KAAK,CAAA,EAAG;YAC3B,MAAM,IAAIV,OAAOC,eAAe,CAACa,gBAAgB,CAAC,CAAA,CAAE,CAACG,OAAO,CAAA;AAC9D,QAAA;QAEA,MAAMuB,eAAAA,GAAkB5B,UAAAA,CAAWF,MAAM,GAAG,CAAA;QAC5C,MAAMR,IAAAA,GAAO,MAAMC,kBAAAA,CAAmBU,YAAAA,EAAc2B,eAAAA,CAAAA;AAEpD,QAAA,IAAIC,UAAAA,GAAa7B,UAAAA;AAEjB,QAAA,IACEV,KAAKhB,QAAQ,IACbsB,KAAAA,CAAMC,OAAO,CAACP,IAAAA,CAAKhB,QAAQ,CAAA,IAC3BuD,UAAAA,CAAW/B,MAAM,KAAKR,IAAAA,CAAKhB,QAAQ,CAACwB,MAAM,EAC1C;;AAEA,YAAA,MAAMgC,oBAAoBxC,IAAAA,CAAKhB,QAAQ,CACpCF,GAAG,CAAC,CAAC2D,IAAAA,GAAAA;gBACJ,OAAOF,UAAAA,CAAWG,IAAI,CAAC,CAACxC,OAASA,IAAAA,CAAKyC,gBAAgB,KAAKF,IAAAA,CAAKG,IAAI,CAAA;AACtE,YAAA,CAAA,CAAA,CACCC,MAAM,CAACC,OAAAA,CAAAA;YAEVP,UAAAA,GAAaC,iBAAAA;AACf,QAAA;;AAGA,QAAA,MAAMO,aAAAA,GAAgB,MAAMrE,aAAAA,CAAcsE,MAAM,CAAC;AAAEhD,YAAAA,IAAAA;YAAMI,KAAAA,EAAOmC;SAAW,EAAG;AAAElE,YAAAA;AAAK,SAAA,CAAA;QACrF,IAAI0E,aAAAA,CAAcE,IAAI,CAAC,CAAC/C,OAASA,IAAAA,CAAKiB,IAAI,EAAEC,UAAAA,CAAW,QAAA,CAAA,CAAA,EAAY;YACjE,MAAMzC,UAAAA,CAAW,SAAA,CAAA,CAAWuE,UAAU,CAAC,gBAAA,CAAA;AACzC,QAAA;AAEA,QAAA,MAAMhC,oBAAoBvC,UAAAA,CAAW,YAAA,CAAA;;QAGrC,IAAI,MAAMuC,iBAAAA,CAAkBG,SAAS,EAAA,EAAI;YACvC,IAAI;AACF,gBAAA,MAAMC,eAAAA,GAAkB,MAAMJ,iBAAAA,CAAkBK,YAAY,CAACwB,aAAAA,CAAAA;;AAE7D,gBAAA,MAAM7B,iBAAAA,CAAkBM,yBAAyB,CAACuB,aAAAA,EAAezB,eAAAA,EAAiBjD,IAAAA,CAAAA;AACpF,YAAA,CAAA,CAAE,OAAOoD,KAAAA,EAAO;AACdX,gBAAAA,MAAAA,CAAOY,GAAG,CAACC,IAAI,CAAC,mEAAA,EAAqE;AACnFF,oBAAAA,KAAAA,EAAOA,KAAAA,YAAiBG,KAAAA,GAAQH,KAAAA,CAAMV,OAAO,GAAGc,MAAAA,CAAOJ,KAAAA;AACzD,iBAAA,CAAA;AACF,YAAA;AACF,QAAA;;QAGA,MAAM0B,WAAAA,GAAc,MAAMtE,KAAAA,CAAMC,GAAG,CAACiE,aAAAA,EAAepE,UAAAA,CAAW,QAAQc,YAAY,CAAA;AAElFvB,QAAAA,GAAAA,CAAIK,IAAI,GAAG,MAAMU,EAAAA,CAAGS,cAAc,CAACyD,WAAAA,EAAa;AAAExD,YAAAA,MAAAA,EAAQR,QAAQS;AAAK,SAAA,CAAA;AACvE1B,QAAAA,GAAAA,CAAIkF,MAAM,GAAG,GAAA;AACf,IAAA,CAAA;AAEA;;;;;;;;MASA,MAAMC,qBAAoBnF,GAAY,EAAA;QACpC,MAAM,EACJC,OAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BC,OAAAA,EAAS,EAAEC,IAAI,EAAE6B,KAAAA,EAAO,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACzC,GAAGlC,GAAAA;AAEJ,QAAA,MAAMQ,gBAAgBC,UAAAA,CAAW,QAAA,CAAA;AACjC,QAAA,MAAMM,KAAK6B,MAAAA,CAAOiB,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAAA,EAAS7D,WAAAA;AACTuB,YAAAA,MAAAA,EAAQR,QAAQ+C,MAAM;YACtBC,KAAAA,EAAO9C;AACT,SAAA,CAAA;QAEA,IAAI,CAACJ,EAAAA,CAAGmD,SAAS,EAAE;AACjB,YAAA,OAAOlE,IAAImE,SAAS,EAAA;AACtB,QAAA;AAEA,QAAA,IAAIiB,CAAAA,CAAEC,OAAO,CAACnD,KAAAA,CAAAA,IAAW,CAACE,KAAAA,CAAMC,OAAO,CAACH,KAAAA,CAAAA,IAAUA,KAAAA,CAAMoD,IAAI,KAAK,CAAA,EAAI;YACnE,MAAM,IAAI1D,MAAAA,CAAOW,gBAAgB,CAAC,iBAAA,CAAA;AACpC,QAAA;;QAGA,MAAMP,IAAAA,GAAOI,MAAMC,OAAO,CAACH,SAASA,KAAK,CAAC,EAAE,GAAGA,KAAAA;QAC/C,MAAMqD,QAAAA,GAAWvD,IAAAA,CAAKyC,gBAAgB,IAAI,SAAA;;AAG1C,QAAA,IAAI3D,QAAAA,GAA2B;YAC7B4D,IAAAA,EAAMa,QAAAA;YACNC,OAAAA,EAAS,IAAA;YACTC,eAAAA,EAAiB,IAAA;YACjBC,MAAAA,EAAQ;AACV,SAAA;AACA,QAAA,IAAIrF,MAAMS,QAAAA,EAAU;YAClB,MAAM6E,GAAAA,GAAMtF,KAAKS,QAAQ;YACzB,IAAI,OAAO6E,QAAQ,QAAA,EAAU;gBAC3B7E,QAAAA,GAAW8E,IAAAA,CAAKC,KAAK,CAACF,GAAAA,CAAAA;AACxB,YAAA,CAAA,MAAO,IAAIvD,KAAAA,CAAMC,OAAO,CAACsD,GAAAA,CAAAA,EAAM;AAC7B7E,gBAAAA,QAAAA,GAAY,OAAO6E,GAAG,CAAC,CAAA,CAAE,KAAK,QAAA,GAAWC,IAAAA,CAAKC,KAAK,CAACF,GAAG,CAAC,CAAA,CAAE,CAAA,GAAIA,GAAG,CAAC,CAAA,CAAE;YACtE,CAAA,MAAO;gBACL7E,QAAAA,GAAW6E,GAAAA;AACb,YAAA;AACF,QAAA;;QAGA,MAAM,EAAEnD,UAAU,EAAEZ,MAAAA,EAAQc,gBAAgB,EAAE,GAAG,MAAMC,oBAAAA,CACrDX,IAAAA,EACA;YAAElB,QAAAA,EAAU8E,IAAAA,CAAKE,SAAS,CAAChF,QAAAA;SAAU,EACrC8B,MAAAA,CAAAA;QAGF,IAAIJ,UAAAA,CAAWF,MAAM,KAAK,CAAA,EAAG;YAC3B,MAAM,IAAIV,OAAOC,eAAe,CAACa,gBAAgB,CAAC,CAAA,CAAE,EAAEG,OAAAA,IAAW,mBAAA,CAAA;AACnE,QAAA;QAEA,MAAMf,IAAAA,GAAO,MAAMC,kBAAAA,CAAmB;AAAEjB,YAAAA;SAAS,EAAG,KAAA,CAAA;AACpD,QAAA,MAAM,CAACiF,YAAAA,CAAa,GAAG,MAAMvF,aAAAA,CAAcsE,MAAM,CAAC;AAAEhD,YAAAA,IAAAA;YAAMI,KAAAA,EAAO;AAACM,gBAAAA,UAAU,CAAC,CAAA;AAAG;SAAC,EAAG;AAAErC,YAAAA;AAAK,SAAA,CAAA;AAE3F,QAAA,IAAI4F,YAAAA,CAAa9C,IAAI,EAAEC,UAAAA,CAAW,QAAA,CAAA,EAAW;YAC3C,MAAMzC,UAAAA,CAAW,SAAA,CAAA,CAAWuE,UAAU,CAAC,gBAAA,CAAA;AACzC,QAAA;;;AAKA,QAAA,MAAM1D,UAAAA,GAAa,MAAMb,UAAAA,CAAW,MAAA,CAAA,CAAQc,YAAY,CAACwE,YAAAA,CAAAA;AAEzD/F,QAAAA,GAAAA,CAAIK,IAAI,GAAG,MAAMU,EAAAA,CAAGS,cAAc,CAACF,UAAAA,EAAY;AAAEG,YAAAA,MAAAA,EAAQR,QAAQS;AAAK,SAAA,CAAA;AACtE1B,QAAAA,GAAAA,CAAIkF,MAAM,GAAG,GAAA;AACf,IAAA,CAAA;AAEA;;;;;;;;;;;MAYA,MAAMc,yBAAwBhG,GAAY,EAAA;AACxC,QAAA,MAAM,EACJC,KAAAA,EAAO,EAAEC,WAAW,EAAEC,IAAI,EAAE,EAC5BC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EAClB,GAAGL,GAAAA;AAEJ,QAAA,MAAMQ,gBAAgBC,UAAAA,CAAW,QAAA,CAAA;AACjC,QAAA,MAAMwF,cAAcxF,UAAAA,CAAW,MAAA,CAAA;AAC/B,QAAA,MAAMM,KAAK6B,MAAAA,CAAOiB,OAAO,CAAC,mBAAA,CAAA,CAAqBC,wBAAwB,CAAC;YACtEC,OAAAA,EAAS7D,WAAAA;AACTuB,YAAAA,MAAAA,EAAQR,QAAQ+C,MAAM;YACtBC,KAAAA,EAAO9C;AACT,SAAA,CAAA;QAEA,IAAI,CAACJ,EAAAA,CAAGmD,SAAS,EAAE;AACjB,YAAA,OAAOlE,IAAImE,SAAS,EAAA;AACtB,QAAA;;AAGA,QAAA,MAAM,EAAE+B,IAAI,EAAEC,QAAQ,EAAE,GAAG9F,IAAAA;QAE3B,IAAI,CAAC6F,IAAAA,IAAQ,CAAC9D,KAAAA,CAAMC,OAAO,CAAC6D,IAAAA,CAAAA,IAASA,IAAAA,CAAK5D,MAAM,KAAK,CAAA,EAAG;YACtD,MAAM,IAAIV,MAAAA,CAAOW,gBAAgB,CAAC,mBAAA,CAAA;AACpC,QAAA;QAEA,IAAI2D,IAAAA,CAAK5D,MAAM,GAAG,EAAA,EAAI;YACpB,MAAM,IAAIV,MAAAA,CAAOW,gBAAgB,CAAC,qCAAA,CAAA;AACpC,QAAA;;AAGAvC,QAAAA,GAAAA,CAAIoG,OAAO,GAAG,KAAA;QACd,MAAMC,GAAAA,GAAMrG,IAAIqG,GAAG;QACnBA,GAAAA,CAAIC,SAAS,CAAC,GAAA,EAAK;YACjB,cAAA,EAAgB,mBAAA;YAChB,eAAA,EAAiB,UAAA;YACjBC,UAAAA,EAAY;AACd,SAAA,CAAA;QAEA,MAAMC,QAAAA,GAAW,CAACC,KAAAA,EAAe3E,IAAAA,GAAAA;AAC/BuE,YAAAA,GAAAA,CAAIK,KAAK,CAAC,CAAC,OAAO,EAAED,KAAAA,CAAM,QAAQ,EAAEb,IAAAA,CAAKE,SAAS,CAAChE,IAAAA,CAAAA,CAAM,IAAI,CAAC,CAAA;AAChE,QAAA,CAAA;QAEA,MAAM6E,KAAAA,GAAQT,KAAK5D,MAAM;AACzB,QAAA,MAAMsE,eAAkC,EAAE;AAC1C,QAAA,MAAMC,kBAAyB,EAAE;;QAGjC,MAAMC,mBAAAA,GAAsB,MAAMC,GAAAA,CAAIC,OAAO,CAACC,KAAKC,IAAI,CAACC,EAAAA,CAAGC,MAAM,EAAA,EAAI,oBAAA,CAAA,CAAA;QACrE,MAAM,EAAEC,SAAS,EAAE,GAAGzE,OAAO0E,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;QAEhD,IAAI;;YAEF,IAAK,IAAIC,IAAI,CAAA,EAAGA,CAAAA,GAAItB,KAAK5D,MAAM,EAAEkF,KAAK,CAAA,CAAG;gBACvC,MAAMC,GAAAA,GAAMvB,IAAI,CAACsB,CAAAA,CAAE;AAEnBhB,gBAAAA,QAAAA,CAAS,eAAA,EAAiB;AAAEiB,oBAAAA,GAAAA;oBAAKC,KAAAA,EAAOF,CAAAA;AAAGb,oBAAAA;AAAM,iBAAA,CAAA;gBAEjD,IAAI;;oBAEF,MAAM,EAAE3E,IAAI,EAAE,GAAG,MAAMiE,WAAAA,CAAY0B,mBAAmB,CACpDF,GAAAA,EACAX,mBAAAA,EACAO,SAAAA,CAAAA;oBAEF,MAAM9B,QAAAA,GAAWvD,KAAKyC,gBAAgB;AAEtC+B,oBAAAA,QAAAA,CAAS,gBAAA,EAAkB;wBACzB9B,IAAAA,EAAMa,QAAAA;wBACNmC,KAAAA,EAAOF,CAAAA;AACPb,wBAAAA,KAAAA;AACArB,wBAAAA,IAAAA,EAAMtD,KAAKsD;AACb,qBAAA,CAAA;;AAGA,oBAAA,MAAMxE,QAAAA,GAA2B;wBAC/B4D,IAAAA,EAAMa,QAAAA;wBACNC,OAAAA,EAAS,IAAA;wBACTC,eAAAA,EAAiB,IAAA;AACjBC,wBAAAA,MAAAA,EAAQS,QAAAA,IAAY;AACtB,qBAAA;oBAEA,MAAM,EAAE3D,UAAU,EAAEZ,MAAAA,EAAQc,gBAAgB,EAAE,GAAG,MAAMC,oBAAAA,CACrDX,IAAAA,EACA;wBAAElB,QAAAA,EAAU8E,IAAAA,CAAKE,SAAS,CAAChF,QAAAA;qBAAU,EACrC8B,MAAAA,CAAAA;oBAGF,IAAIJ,UAAAA,CAAWF,MAAM,KAAK,CAAA,EAAG;AAC3B,wBAAA,MAAMsF,YAAAA,GAAelF,gBAAgB,CAAC,CAAA,CAAE,EAAEG,OAAAA,IAAW,mBAAA;AACrD+D,wBAAAA,YAAAA,CAAaiB,IAAI,CAAC;4BAAEnD,IAAAA,EAAMa,QAAAA;4BAAU1C,OAAAA,EAAS+E;AAAa,yBAAA,CAAA;AAC1DpB,wBAAAA,QAAAA,CAAS,YAAA,EAAc;4BAAE9B,IAAAA,EAAMa,QAAAA;AAAUkC,4BAAAA,GAAAA;4BAAKC,KAAAA,EAAOF,CAAAA;4BAAG3E,OAAAA,EAAS+E;AAAa,yBAAA,CAAA;oBAChF,CAAA,MAAO;;wBAEL,MAAM9F,IAAAA,GAAO,MAAMC,kBAAAA,CAAmB;AAAEjB,4BAAAA;yBAAS,EAAG,KAAA,CAAA;AACpD,wBAAA,MAAM,CAACiF,YAAAA,CAAa,GAAG,MAAMvF,aAAAA,CAAcsE,MAAM,CAC/C;AAAEhD,4BAAAA,IAAAA;4BAAMI,KAAAA,EAAO;AAACM,gCAAAA,UAAU,CAAC,CAAA;AAAG;yBAAC,EAC/B;AAAErC,4BAAAA;AAAK,yBAAA,CAAA;;AAIT,wBAAA,MAAMmB,UAAAA,GAAa,MAAM2E,WAAAA,CAAY1E,YAAY,CAACwE,YAAAA,CAAAA;AAClDc,wBAAAA,eAAAA,CAAgBgB,IAAI,CAACvG,UAAAA,CAAAA;AAErBkF,wBAAAA,QAAAA,CAAS,eAAA,EAAiB;4BAAE9B,IAAAA,EAAMa,QAAAA;4BAAUmC,KAAAA,EAAOF,CAAAA;4BAAGxF,IAAAA,EAAMV;AAAW,yBAAA,CAAA;AACzE,oBAAA;AACF,gBAAA,CAAA,CAAE,OAAOiC,KAAAA,EAAO;AACd,oBAAA,MAAMqE,eAAerE,KAAAA,YAAiBG,KAAAA,GAAQH,KAAAA,CAAMV,OAAO,GAAGc,MAAAA,CAAOJ,KAAAA,CAAAA;AACrEqD,oBAAAA,YAAAA,CAAaiB,IAAI,CAAC;wBAAEnD,IAAAA,EAAM+C,GAAAA;wBAAK5E,OAAAA,EAAS+E;AAAa,qBAAA,CAAA;AACrDpB,oBAAAA,QAAAA,CAAS,YAAA,EAAc;AAAEiB,wBAAAA,GAAAA;wBAAKC,KAAAA,EAAOF,CAAAA;wBAAG3E,OAAAA,EAAS+E;AAAa,qBAAA,CAAA;AAChE,gBAAA;AACF,YAAA;;YAGA,IAAIf,eAAAA,CAAgB9B,IAAI,CAAC,CAAC/C,OAASA,IAAAA,CAAKiB,IAAI,EAAEC,UAAAA,CAAW,QAAA,CAAA,CAAA,EAAY;gBACnE,MAAMzC,UAAAA,CAAW,SAAA,CAAA,CAAWuE,UAAU,CAAC,gBAAA,CAAA;AACzC,YAAA;;AAGAwB,YAAAA,QAAAA,CAAS,iBAAA,EAAmB;AAC1B1E,gBAAAA,IAAAA,EAAM,MAAMf,EAAAA,CAAGS,cAAc,CAACqF,eAAAA,EAAiB;AAAEpF,oBAAAA,MAAAA,EAAQR,QAAQS;AAAK,iBAAA,CAAA;gBACtEE,MAAAA,EAAQgF;AACV,aAAA,CAAA;QACF,CAAA,QAAU;;YAER,MAAMG,GAAAA,CAAIe,MAAM,CAAChB,mBAAAA,CAAAA;AACnB,QAAA;AAEAT,QAAAA,GAAAA,CAAI0B,GAAG,EAAA;AACT,IAAA,CAAA;;AAGA,IAAA,MAAMjD,QAAO9E,GAAY,EAAA;AACvB,QAAA,MAAM,EACJ2B,KAAAA,EAAO,EAAEd,EAAE,EAAE,EACbT,OAAAA,EAAS,EAAE8B,KAAAA,EAAO,EAAEA,KAAK,EAAE,GAAG,EAAE,EAAE,EACnC,GAAGlC,GAAAA;AAEJ,QAAA,IAAIoF,CAAAA,CAAEC,OAAO,CAACnD,KAAAA,CAAAA,IAAW,CAACE,KAAAA,CAAMC,OAAO,CAACH,KAAAA,CAAAA,IAAUA,KAAAA,CAAMoD,IAAI,KAAK,CAAA,EAAI;AACnE,YAAA,IAAIzE,EAAAA,EAAI;gBACN,OAAO,IAAI,CAACQ,cAAc,CAACrB,GAAAA,CAAAA;AAC7B,YAAA;YAEA,MAAM,IAAI4B,MAAAA,CAAOW,gBAAgB,CAAC,iBAAA,CAAA;AACpC,QAAA;QAEA,MAAO1B,CAAAA,EAAAA,GAAK,IAAI,CAACoB,WAAW,GAAG,IAAI,CAAC2B,WAAU,EAAG5D,GAAAA,CAAAA;AACnD,IAAA;AACF,CAAA;;;;"}
1
+ {"version":3,"file":"admin-upload.mjs","sources":["../../../server/src/controllers/admin-upload.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\nimport fse from 'fs-extra';\nimport _ from 'lodash';\nimport { errors, async } from '@strapi/utils';\n\nimport type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\nimport { validateBulkUpdateBody, validateUploadBody } from './validation/admin/upload';\nimport { findEntityAndCheckPermissions } from './utils/find-entity-and-check-permissions';\nimport { Config, FileInfo } from '../types';\nimport { prepareUploadRequest, type FileUploadError } from '../utils/mime-validation';\nimport type { UploadFileInfo } from '../../../shared/contracts/files';\n\nexport default {\n async bulkUpdateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const { updates } = await validateBulkUpdateBody(body);\n const uploadService = getService('upload');\n\n const results = await async.map(\n updates,\n async ({ id, fileInfo }: { id: number; fileInfo: FileInfo }) => {\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const updated = await uploadService.updateFileInfo(id, fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(updated);\n\n return pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n }\n );\n\n ctx.body = results;\n },\n\n /**\n * `PUT /upload/files/:id`\n *\n * Update the editable metadata (`fileInfo`) of an existing file. Also reached\n * through the `POST /upload` multiplexer, which delegates here with the id in\n * `ctx.query` — hence the dual read below, route params winning.\n */\n async updateFileInfo(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const id = ctx.params?.id ?? ctx.query.id;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n const data = await validateUploadBody(body);\n\n const file = await uploadService.updateFileInfo(id, data.fileInfo as any, { user });\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(file);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n /**\n * `POST /upload/files/:id/replace`\n *\n * Replace the binary content of an existing file. Also reached through the\n * `POST /upload` multiplexer, which delegates here with the id in `ctx.query`\n * — hence the dual read below, route params winning.\n */\n async replaceFile(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files: filesInput } = {} },\n } = ctx;\n\n const id = ctx.params?.id ?? ctx.query.id;\n\n if (typeof id !== 'string') {\n throw new errors.ValidationError('File id is required');\n }\n\n const uploadService = getService('upload');\n const { pm } = await findEntityAndCheckPermissions(\n userAbility,\n ACTIONS.update,\n FILE_MODEL_UID,\n id\n );\n\n if (Array.isArray(filesInput) && filesInput.length > 1) {\n throw new errors.ApplicationError('Cannot replace a file with multiple ones');\n }\n\n const files = Array.isArray(filesInput) ? filesInput[0] : filesInput;\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const data = (await validateUploadBody(filteredBody)) as { fileInfo: FileInfo };\n const replacedFile = await uploadService.replace(id, { data, file: validFiles[0] }, { user });\n\n // Regenerate AI metadata for image replacements so the alt text / caption\n // reflect the new file content. Mirrors the post-upload hook in\n // `uploadFiles`; failure is logged and swallowed to keep the replace flow\n // resilient when the AI provider is unavailable.\n const aiMetadataService = getService('aiMetadata');\n if (replacedFile?.mime?.startsWith('image/') && (await aiMetadataService.isEnabled())) {\n try {\n const metadataResults = await aiMetadataService.processFiles([replacedFile]);\n await aiMetadataService.updateFilesWithAIMetadata([replacedFile], metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed on replace, proceeding without it', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFile = await getService('file').signFileUrls(replacedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n },\n\n async uploadFiles(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n const {\n validFiles,\n filteredBody,\n errors: validationErrors,\n } = await prepareUploadRequest(files, body, strapi);\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0].message);\n }\n\n const isMultipleFiles = validFiles.length > 1;\n const data = await validateUploadBody(filteredBody, isMultipleFiles);\n\n let filesArray = validFiles;\n\n if (\n data.fileInfo &&\n Array.isArray(data.fileInfo) &&\n filesArray.length === data.fileInfo.length\n ) {\n // Reorder filesArray to match data.fileInfo order\n const alignedFilesArray = data.fileInfo\n .map((info) => {\n return filesArray.find((file) => file.originalFilename === info.name);\n })\n .filter(Boolean) as any[];\n\n filesArray = alignedFilesArray;\n }\n\n // Upload files first to get thumbnails\n const uploadedFiles = await uploadService.upload({ data, files: filesArray }, { user });\n if (uploadedFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n const aiMetadataService = getService('aiMetadata');\n\n // AFTER upload - generate AI metadata for images\n if (await aiMetadataService.isEnabled()) {\n try {\n const metadataResults = await aiMetadataService.processFiles(uploadedFiles);\n // Update the uploaded files with AI metadata\n await aiMetadataService.updateFilesWithAIMetadata(uploadedFiles, metadataResults, user);\n } catch (error) {\n strapi.log.warn('AI metadata generation failed, proceeding without AI enhancements', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Sign file urls for private providers\n const signedFiles = await async.map(uploadedFiles, getService('file').signFileUrls);\n\n ctx.body = await pm.sanitizeOutput(signedFiles, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * `POST /upload/files`\n *\n * Upload a single file and return the created File.\n *\n * Accepts one file per request (multipart `files` + `fileInfo`) and returns a\n * single `File` object. Unlike `uploadFiles`, it does **not** run AI metadata\n * generation inline — that responsibility is decoupled and will be handled by a\n * background job. Auth and permission checks mirror `POST /upload`.\n */\n async uploadFile(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body, files: { files } = {} },\n } = ctx;\n\n const uploadService = getService('upload');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n throw new errors.ApplicationError('Files are empty');\n }\n\n // Accept a single file per request; ignore any extras defensively.\n const file = Array.isArray(files) ? files[0] : files;\n const fileName = file.originalFilename || 'unknown';\n\n // Parse the single fileInfo object from the multipart body.\n let fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: null,\n };\n if (body?.fileInfo) {\n const raw = body.fileInfo;\n if (typeof raw === 'string') {\n fileInfo = JSON.parse(raw);\n } else if (Array.isArray(raw)) {\n fileInfo = (typeof raw[0] === 'string' ? JSON.parse(raw[0]) : raw[0]) as UploadFileInfo;\n } else {\n fileInfo = raw as UploadFileInfo;\n }\n }\n\n // Validate this single file using security checks.\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n throw new errors.ValidationError(validationErrors[0]?.message || 'Validation failed');\n }\n\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload({ data, files: [validFiles[0]] }, { user });\n\n if (uploadedFile.mime?.startsWith('image/')) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // No inline AI metadata generation — intentionally decoupled for the async job.\n\n // Sign file url for private providers.\n const signedFile = await getService('file').signFileUrls(uploadedFile);\n\n ctx.body = await pm.sanitizeOutput(signedFile, { action: ACTIONS.read });\n ctx.status = 201;\n },\n\n /**\n * `POST /upload/actions/upload-from-urls`\n *\n * Upload files from URLs with SSE streaming for per-file progress\n *\n * Accepts JSON body with URLs and fetches them server-side.\n * Streams Server-Sent Events as each URL is fetched and uploaded:\n * - file:fetching — when starting to fetch a URL\n * - file:uploading — when upload starts for a fetched file\n * - file:complete — when a file is successfully uploaded\n * - file:error — when a URL fetch or upload fails\n * - stream:complete — final summary with all results\n */\n async uploadFromUrls(ctx: Context) {\n const {\n state: { userAbility, user },\n request: { body },\n } = ctx;\n\n const uploadService = getService('upload');\n const fileService = getService('file');\n const pm = strapi.service('admin::permission').createPermissionsManager({\n ability: userAbility,\n action: ACTIONS.create,\n model: FILE_MODEL_UID,\n });\n\n if (!pm.isAllowed) {\n return ctx.forbidden();\n }\n\n // Parse and validate request body\n const { urls, folderId } = body as { urls?: string[]; folderId?: number | null };\n\n if (!urls || !Array.isArray(urls) || urls.length === 0) {\n throw new errors.ApplicationError('URLs are required');\n }\n\n if (urls.length > 20) {\n throw new errors.ApplicationError('Maximum 20 URLs allowed per request');\n }\n\n // Take manual control of the response for SSE streaming\n ctx.respond = false;\n const res = ctx.res;\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n const writeSSE = (event: string, data: Record<string, unknown>) => {\n res.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`);\n };\n\n const total = urls.length;\n const uploadErrors: FileUploadError[] = [];\n const successfulFiles: any[] = [];\n\n // Create temp directory for fetched files\n const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-url-upload-'));\n const { sizeLimit } = strapi.config.get<Config>('plugin::upload');\n\n try {\n // Process each URL sequentially\n for (let i = 0; i < urls.length; i += 1) {\n const url = urls[i];\n\n writeSSE('file:fetching', { url, index: i, total });\n\n try {\n // Fetch URL to temp file\n const { file } = await fileService.fetchUrlToInputFile(\n url,\n tmpWorkingDirectory,\n sizeLimit\n );\n const fileName = file.originalFilename;\n\n writeSSE('file:uploading', {\n name: fileName,\n index: i,\n total,\n size: file.size,\n });\n\n // Validate using security checks\n const fileInfo: UploadFileInfo = {\n name: fileName,\n caption: null,\n alternativeText: null,\n folder: folderId ?? null,\n };\n\n const { validFiles, errors: validationErrors } = await prepareUploadRequest(\n file,\n { fileInfo: JSON.stringify(fileInfo) },\n strapi\n );\n\n if (validFiles.length === 0) {\n const errorMessage = validationErrors[0]?.message || 'Validation failed';\n uploadErrors.push({ name: fileName, message: errorMessage });\n writeSSE('file:error', { name: fileName, url, index: i, message: errorMessage });\n } else {\n // Upload the file\n const data = await validateUploadBody({ fileInfo }, false);\n const [uploadedFile] = await uploadService.upload(\n { data, files: [validFiles[0]] },\n { user }\n );\n\n // Sign file url\n const signedFile = await fileService.signFileUrls(uploadedFile);\n successfulFiles.push(signedFile);\n\n writeSSE('file:complete', { name: fileName, index: i, file: signedFile });\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n uploadErrors.push({ name: url, message: errorMessage });\n writeSSE('file:error', { url, index: i, message: errorMessage });\n }\n }\n\n // Track image upload metric once if any images were uploaded\n if (successfulFiles.some((file) => file.mime?.startsWith('image/'))) {\n await getService('metrics').trackUsage('didUploadImage');\n }\n\n // Send final stream summary\n writeSSE('stream:complete', {\n data: await pm.sanitizeOutput(successfulFiles, { action: ACTIONS.read }),\n errors: uploadErrors,\n });\n } finally {\n // Clean up temp directory\n await fse.remove(tmpWorkingDirectory);\n }\n\n res.end();\n },\n\n // TODO: split into multiple endpoints\n async upload(ctx: Context) {\n const {\n query: { id },\n request: { files: { files } = {} },\n } = ctx;\n\n if (_.isEmpty(files) || (!Array.isArray(files) && files.size === 0)) {\n if (id) {\n return this.updateFileInfo(ctx);\n }\n\n throw new errors.ApplicationError('Files are empty');\n }\n\n await (id ? this.replaceFile : this.uploadFiles)(ctx);\n },\n};\n"],"names":["bulkUpdateFileInfo","ctx","state","userAbility","user","request","body","updates","validateBulkUpdateBody","uploadService","getService","results","async","map","id","fileInfo","pm","findEntityAndCheckPermissions","ACTIONS","update","FILE_MODEL_UID","updated","updateFileInfo","signedFile","signFileUrls","sanitizeOutput","action","read","params","query","errors","ValidationError","data","validateUploadBody","file","replaceFile","files","filesInput","Array","isArray","length","ApplicationError","validFiles","filteredBody","validationErrors","prepareUploadRequest","strapi","message","replacedFile","replace","aiMetadataService","mime","startsWith","isEnabled","metadataResults","processFiles","updateFilesWithAIMetadata","error","log","warn","Error","String","uploadFiles","service","createPermissionsManager","ability","create","model","isAllowed","forbidden","isMultipleFiles","filesArray","alignedFilesArray","info","find","originalFilename","name","filter","Boolean","uploadedFiles","upload","some","trackUsage","signedFiles","status","uploadFile","_","isEmpty","size","fileName","caption","alternativeText","folder","raw","JSON","parse","stringify","uploadedFile","uploadFromUrls","fileService","urls","folderId","respond","res","writeHead","Connection","writeSSE","event","write","total","uploadErrors","successfulFiles","tmpWorkingDirectory","fse","mkdtemp","path","join","os","tmpdir","sizeLimit","config","get","i","url","index","fetchUrlToInputFile","errorMessage","push","remove","end"],"mappings":"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"}
@@ -51,18 +51,8 @@ const routes = {
51
51
  },
52
52
  {
53
53
  method: 'POST',
54
- path: '/unstable/upload-file',
55
- handler: 'admin-upload.unstable_uploadFile',
56
- config: {
57
- policies: [
58
- 'admin::isAuthenticatedAdmin'
59
- ]
60
- }
61
- },
62
- {
63
- method: 'POST',
64
- path: '/unstable/stream-from-urls',
65
- handler: 'admin-upload.unstable_uploadFromUrls',
54
+ path: '/actions/upload-from-urls',
55
+ handler: 'admin-upload.uploadFromUrls',
66
56
  config: {
67
57
  policies: [
68
58
  'admin::isAuthenticatedAdmin'
@@ -87,6 +77,16 @@ const routes = {
87
77
  ]
88
78
  }
89
79
  },
80
+ {
81
+ method: 'POST',
82
+ path: '/files',
83
+ handler: 'admin-upload.uploadFile',
84
+ config: {
85
+ policies: [
86
+ 'admin::isAuthenticatedAdmin'
87
+ ]
88
+ }
89
+ },
90
90
  {
91
91
  method: 'GET',
92
92
  path: '/files/:id',
@@ -105,6 +105,26 @@ const routes = {
105
105
  ]
106
106
  }
107
107
  },
108
+ {
109
+ method: 'PUT',
110
+ path: '/files/:id',
111
+ handler: 'admin-upload.updateFileInfo',
112
+ config: {
113
+ policies: [
114
+ 'admin::isAuthenticatedAdmin'
115
+ ]
116
+ }
117
+ },
118
+ {
119
+ method: 'POST',
120
+ path: '/files/:id/replace',
121
+ handler: 'admin-upload.replaceFile',
122
+ config: {
123
+ policies: [
124
+ 'admin::isAuthenticatedAdmin'
125
+ ]
126
+ }
127
+ },
108
128
  {
109
129
  method: 'DELETE',
110
130
  path: '/files/:id',
@@ -286,9 +306,9 @@ const routes = {
286
306
  }
287
307
  },
288
308
  {
289
- method: 'GET',
290
- path: '/actions/generate-ai-metadata/count',
291
- handler: 'admin-file.getAIMetadataCount',
309
+ method: 'POST',
310
+ path: '/ai-metadata-jobs',
311
+ handler: 'admin-file.createAIMetadataJob',
292
312
  config: {
293
313
  policies: [
294
314
  'admin::isAuthenticatedAdmin',
@@ -296,7 +316,7 @@ const routes = {
296
316
  name: 'admin::hasPermissions',
297
317
  config: {
298
318
  actions: [
299
- 'plugin::upload.read'
319
+ 'plugin::upload.assets.update'
300
320
  ]
301
321
  }
302
322
  }
@@ -305,7 +325,7 @@ const routes = {
305
325
  },
306
326
  {
307
327
  method: 'GET',
308
- path: '/actions/generate-ai-metadata/latest',
328
+ path: '/ai-metadata-jobs/latest',
309
329
  handler: 'admin-file.getLatestAIMetadataJob',
310
330
  config: {
311
331
  policies: [
@@ -314,9 +334,9 @@ const routes = {
314
334
  }
315
335
  },
316
336
  {
317
- method: 'POST',
318
- path: '/unstable/generate-ai-metadata',
319
- handler: 'admin-file.unstable_generateAIMetadata',
337
+ method: 'GET',
338
+ path: '/ai-metadata-jobs/pending-count',
339
+ handler: 'admin-file.getAIMetadataPendingCount',
320
340
  config: {
321
341
  policies: [
322
342
  'admin::isAuthenticatedAdmin',
@@ -324,7 +344,7 @@ const routes = {
324
344
  name: 'admin::hasPermissions',
325
345
  config: {
326
346
  actions: [
327
- 'plugin::upload.assets.update'
347
+ 'plugin::upload.read'
328
348
  ]
329
349
  }
330
350
  }
@@ -1 +1 @@
1
- {"version":3,"file":"admin.js","sources":["../../../server/src/routes/admin.ts"],"sourcesContent":["export const routes = {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/settings',\n handler: 'admin-settings.getSettings',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.settings.read'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/settings',\n handler: 'admin-settings.updateSettings',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.settings.read'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/',\n handler: 'admin-upload.upload',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'POST',\n path: '/unstable/upload-file',\n handler: 'admin-upload.unstable_uploadFile',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'POST',\n path: '/unstable/stream-from-urls',\n handler: 'admin-upload.unstable_uploadFromUrls',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'GET',\n path: '/files',\n handler: 'admin-file.find',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/files/:id',\n handler: 'admin-file.findOne',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'DELETE',\n path: '/files/:id',\n handler: 'admin-file.destroy',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/folders/:id',\n handler: 'admin-folder.findOne',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/folders',\n handler: 'admin-folder.find',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/folders',\n handler: 'admin-folder.create',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.create'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/folders/:id',\n handler: 'admin-folder.update',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/folder-structure',\n handler: 'admin-folder.getStructure',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/bulk-delete',\n handler: 'admin-folder-file.deleteMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/bulk-move',\n handler: 'admin-folder-file.moveMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/bulk-update',\n handler: 'admin-upload.bulkUpdateFileInfo',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/generate-ai-metadata',\n handler: 'admin-file.generateAIMetadata',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/actions/generate-ai-metadata/count',\n handler: 'admin-file.getAIMetadataCount',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/actions/generate-ai-metadata/latest',\n handler: 'admin-file.getLatestAIMetadataJob',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'POST',\n path: '/unstable/generate-ai-metadata',\n handler: 'admin-file.unstable_generateAIMetadata',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n ],\n};\n"],"names":["routes","type","method","path","handler","config","policies","name","actions"],"mappings":"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"}
1
+ {"version":3,"file":"admin.js","sources":["../../../server/src/routes/admin.ts"],"sourcesContent":["export const routes = {\n type: 'admin',\n routes: [\n {\n method: 'GET',\n path: '/settings',\n handler: 'admin-settings.getSettings',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.settings.read'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/settings',\n handler: 'admin-settings.updateSettings',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.settings.read'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/',\n handler: 'admin-upload.upload',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'POST',\n path: '/actions/upload-from-urls',\n handler: 'admin-upload.uploadFromUrls',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'GET',\n path: '/files',\n handler: 'admin-file.find',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/files',\n handler: 'admin-upload.uploadFile',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'GET',\n path: '/files/:id',\n handler: 'admin-file.findOne',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/files/:id',\n handler: 'admin-upload.updateFileInfo',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'POST',\n path: '/files/:id/replace',\n handler: 'admin-upload.replaceFile',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'DELETE',\n path: '/files/:id',\n handler: 'admin-file.destroy',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/folders/:id',\n handler: 'admin-folder.findOne',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/folders',\n handler: 'admin-folder.find',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/folders',\n handler: 'admin-folder.create',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.create'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/folders/:id',\n handler: 'admin-folder.update',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/folder-structure',\n handler: 'admin-folder.getStructure',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/bulk-delete',\n handler: 'admin-folder-file.deleteMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/bulk-move',\n handler: 'admin-folder-file.moveMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/bulk-update',\n handler: 'admin-upload.bulkUpdateFileInfo',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/actions/generate-ai-metadata',\n handler: 'admin-file.generateAIMetadata',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/ai-metadata-jobs',\n handler: 'admin-file.createAIMetadataJob',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.assets.update'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/ai-metadata-jobs/latest',\n handler: 'admin-file.getLatestAIMetadataJob',\n config: {\n policies: ['admin::isAuthenticatedAdmin'],\n },\n },\n {\n method: 'GET',\n path: '/ai-metadata-jobs/pending-count',\n handler: 'admin-file.getAIMetadataPendingCount',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::upload.read'],\n },\n },\n ],\n },\n },\n ],\n};\n"],"names":["routes","type","method","path","handler","config","policies","name","actions"],"mappings":";;MAAaA,MAAAA,GAAS;IACpBC,IAAAA,EAAM,OAAA;IACND,MAAAA,EAAQ;AACN,QAAA;YACEE,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,WAAA;YACNC,OAAAA,EAAS,4BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,WAAA;YACNC,OAAAA,EAAS,+BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,GAAA;YACNC,OAAAA,EAAS,qBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AAAC,oBAAA;AAA8B;AAC3C;AACF,SAAA;AACA,QAAA;YACEJ,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,2BAAA;YACNC,OAAAA,EAAS,6BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AAAC,oBAAA;AAA8B;AAC3C;AACF,SAAA;AACA,QAAA;YACEJ,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,QAAA;YACNC,OAAAA,EAAS,iBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAAsB;AAClC;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,QAAA;YACNC,OAAAA,EAAS,yBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AAAC,oBAAA;AAA8B;AAC3C;AACF,SAAA;AACA,QAAA;YACEJ,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,YAAA;YACNC,OAAAA,EAAS,oBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAAsB;AAClC;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,YAAA;YACNC,OAAAA,EAAS,6BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AAAC,oBAAA;AAA8B;AAC3C;AACF,SAAA;AACA,QAAA;YACEJ,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,oBAAA;YACNC,OAAAA,EAAS,0BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AAAC,oBAAA;AAA8B;AAC3C;AACF,SAAA;AACA,QAAA;YACEJ,MAAAA,EAAQ,QAAA;YACRC,IAAAA,EAAM,YAAA;YACNC,OAAAA,EAAS,oBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,cAAA;YACNC,OAAAA,EAAS,sBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAAsB;AAClC;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,UAAA;YACNC,OAAAA,EAAS,mBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAAsB;AAClC;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,UAAA;YACNC,OAAAA,EAAS,qBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,cAAA;YACNC,OAAAA,EAAS,qBAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,mBAAA;YACNC,OAAAA,EAAS,2BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAAsB;AAClC;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,sBAAA;YACNC,OAAAA,EAAS,8BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,oBAAA;YACNC,OAAAA,EAAS,4BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,sBAAA;YACNC,OAAAA,EAAS,iCAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,+BAAA;YACNC,OAAAA,EAAS,+BAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,MAAA;YACRC,IAAAA,EAAM,mBAAA;YACNC,OAAAA,EAAS,gCAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAA+B;AAC3C;AACF;AACD;AACH;AACF,SAAA;AACA,QAAA;YACEN,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,0BAAA;YACNC,OAAAA,EAAS,mCAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AAAC,oBAAA;AAA8B;AAC3C;AACF,SAAA;AACA,QAAA;YACEJ,MAAAA,EAAQ,KAAA;YACRC,IAAAA,EAAM,iCAAA;YACNC,OAAAA,EAAS,sCAAA;YACTC,MAAAA,EAAQ;gBACNC,QAAAA,EAAU;AACR,oBAAA,6BAAA;AACA,oBAAA;wBACEC,IAAAA,EAAM,uBAAA;wBACNF,MAAAA,EAAQ;4BACNG,OAAAA,EAAS;AAAC,gCAAA;AAAsB;AAClC;AACF;AACD;AACH;AACF;AACD;AACH;;;;"}