@strapi/upload 5.55.1 → 5.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/hooks/useAIMetadataAvailability.js +14 -0
- package/dist/admin/hooks/useAIMetadataAvailability.js.map +1 -0
- package/dist/admin/hooks/useAIMetadataAvailability.mjs +12 -0
- package/dist/admin/hooks/useAIMetadataAvailability.mjs.map +1 -0
- package/dist/admin/hooks/useAIMetadataEnabled.js +7 -5
- package/dist/admin/hooks/useAIMetadataEnabled.js.map +1 -1
- package/dist/admin/hooks/useAIMetadataEnabled.mjs +7 -5
- package/dist/admin/hooks/useAIMetadataEnabled.mjs.map +1 -1
- package/dist/admin/hooks/useTracking.js +2 -2
- package/dist/admin/hooks/useTracking.js.map +1 -1
- package/dist/admin/hooks/useTracking.mjs +2 -2
- package/dist/admin/hooks/useTracking.mjs.map +1 -1
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.js +14 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.js.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs +12 -0
- package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.js +23 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.js.map +1 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs +21 -0
- package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs.map +1 -0
- package/dist/admin/legacy/hooks/useTracking.js +2 -2
- package/dist/admin/legacy/hooks/useTracking.js.map +1 -1
- package/dist/admin/legacy/hooks/useTracking.mjs +2 -2
- package/dist/admin/legacy/hooks/useTracking.mjs.map +1 -1
- package/dist/admin/legacy/pages/App/MediaLibrary.js +4 -4
- package/dist/admin/legacy/pages/App/MediaLibrary.js.map +1 -1
- package/dist/admin/legacy/pages/App/MediaLibrary.mjs +4 -4
- package/dist/admin/legacy/pages/App/MediaLibrary.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.js +4 -2
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.js.map +1 -1
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs +4 -2
- package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
- package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
- package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
- package/dist/admin/services/api.js.map +1 -1
- package/dist/admin/services/api.mjs.map +1 -1
- package/dist/admin/src/hooks/useAIMetadataAvailability.d.ts +5 -0
- package/dist/admin/src/hooks/useAIMetadataEnabled.d.ts +5 -3
- package/dist/admin/src/legacy/components/EditAssetDialog/PreviewBox/PreviewComponents.d.ts +2 -2
- package/dist/admin/src/legacy/hooks/useAIMetadataAvailability.d.ts +5 -0
- package/dist/admin/src/legacy/hooks/useAIMetadataEnabled.d.ts +12 -0
- package/dist/admin/src/legacy/hooks/useSettings.d.ts +1 -0
- package/dist/admin/src/pages/Assets/components/ActionsMenuContent.d.ts +2 -2
- package/dist/admin/translations/en.json.js +1 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +1 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/bootstrap.js +7 -0
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs +7 -0
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-folder-file.js +51 -4
- package/dist/server/controllers/admin-folder-file.js.map +1 -1
- package/dist/server/controllers/admin-folder-file.mjs +52 -5
- package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
- package/dist/server/controllers/admin-settings.js +2 -1
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +2 -1
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/errors.js +12 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/errors.mjs +10 -0
- package/dist/server/errors.mjs.map +1 -0
- package/dist/server/services/ai-metadata-provider.js +83 -0
- package/dist/server/services/ai-metadata-provider.js.map +1 -0
- package/dist/server/services/ai-metadata-provider.mjs +61 -0
- package/dist/server/services/ai-metadata-provider.mjs.map +1 -0
- package/dist/server/services/ai-metadata-strapi-managed.js +48 -0
- package/dist/server/services/ai-metadata-strapi-managed.js.map +1 -0
- package/dist/server/services/ai-metadata-strapi-managed.mjs +46 -0
- package/dist/server/services/ai-metadata-strapi-managed.mjs.map +1 -0
- package/dist/server/services/ai-metadata.js +5 -58
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +6 -39
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/folder.js +4 -1
- package/dist/server/services/folder.js.map +1 -1
- package/dist/server/services/folder.mjs +4 -1
- package/dist/server/services/folder.mjs.map +1 -1
- package/dist/server/services/index.js +3 -1
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/index.mjs +3 -1
- package/dist/server/services/index.mjs.map +1 -1
- package/dist/server/services/metrics.js +1 -1
- package/dist/server/services/metrics.js.map +1 -1
- package/dist/server/services/metrics.mjs +1 -1
- package/dist/server/services/metrics.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-folder-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/errors.d.ts +6 -0
- package/dist/server/src/errors.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +6 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/mcp/ambient-instance.d.ts +3 -1
- package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata-provider.d.ts +26 -0
- package/dist/server/src/services/ai-metadata-provider.d.ts.map +1 -0
- package/dist/server/src/services/ai-metadata-strapi-managed.d.ts +7 -0
- package/dist/server/src/services/ai-metadata-strapi-managed.d.ts.map +1 -0
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/folder.d.ts +5 -2
- package/dist/server/src/services/folder.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +6 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/utils/images.d.ts +2 -2
- package/dist/server/src/utils/images.d.ts.map +1 -1
- package/dist/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/server/utils/images.js +6 -7
- package/dist/server/utils/images.js.map +1 -1
- package/dist/server/utils/images.mjs +6 -7
- package/dist/server/utils/images.mjs.map +1 -1
- package/dist/server/utils/index.js.map +1 -1
- package/dist/server/utils/index.mjs.map +1 -1
- package/dist/shared/contracts/settings.d.ts +6 -0
- package/package.json +11 -11
- package/dist/admin/legacy/hooks/useAiAvailability.js +0 -22
- package/dist/admin/legacy/hooks/useAiAvailability.js.map +0 -1
- package/dist/admin/legacy/hooks/useAiAvailability.mjs +0 -20
- package/dist/admin/legacy/hooks/useAiAvailability.mjs.map +0 -1
- package/dist/admin/src/legacy/hooks/useAiAvailability.d.ts +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-settings.js","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n const validated = await validateSettings(body);\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {
|
|
1
|
+
{"version":3,"file":"admin-settings.js","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n const validated = await validateSettings(body);\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {\n data: {\n ...data,\n concurrentUploadRequests,\n aiMetadataAvailable: getService('aiMetadataProvider').hasProvider(),\n },\n };\n },\n};\n"],"names":["SETTINGS_KEYS","Object","keys","settingsSchema","fields","updateSettings","ctx","request","body","state","userAbility","cannot","ACTIONS","readSettings","FILE_MODEL_UID","forbidden","validated","validateSettings","source","data","fromEntries","filter","key","map","getService","setSettings","getSettings","concurrentUploadRequests","strapi","config","get","aiMetadataAvailable","hasProvider"],"mappings":";;;;;;AAQA;AACA;AACA;AACA;AACA,MAAMA,aAAAA,GAAgBC,MAAAA,CAAOC,IAAI,CAACC,wBAAeC,MAAM,CAAA;AAEvD,oBAAe;AACb,IAAA,MAAMC,gBAAeC,GAAY,EAAA;QAC/B,MAAM,EACJC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EACjBC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACvB,GAAGJ,GAAAA;AAEJ,QAAA,IAAII,YAAYC,MAAM,CAACC,iBAAAA,CAAQC,YAAY,EAAEC,wBAAAA,CAAAA,EAAiB;AAC5D,YAAA,OAAOR,IAAIS,SAAS,EAAA;AACtB,QAAA;QAEA,MAAMC,SAAAA,GAAY,MAAMC,gBAAAA,CAAiBT,IAAAA,CAAAA;AACzC,QAAA,MAAMU,MAAAA,GAASF,SAAAA;AACf,QAAA,MAAMG,IAAAA,GAAOlB,MAAAA,CAAOmB,WAAW,CAC7BpB,cAAcqB,MAAM,CAAC,CAACC,GAAAA,GAAQA,GAAAA,IAAOJ,MAAAA,CAAAA,CAAQK,GAAG,CAAC,CAACD,GAAAA,GAAQ;AAACA,gBAAAA,GAAAA;AAAKJ,gBAAAA,MAAM,CAACI,GAAAA;AAAK,aAAA,CAAA,CAAA;QAG9E,MAAME,gBAAAA,CAAW,QAAA,CAAA,CAAUC,WAAW,CAACN,IAAAA,CAAAA;AAEvCb,QAAAA,GAAAA,CAAIE,IAAI,GAAG;AAAEW,YAAAA;AAAK,SAAA;AACpB,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAYpB,GAAY,EAAA;;AAE5B,QAAA,MAAMa,IAAAA,GAAO,MAAMK,gBAAAA,CAAW,QAAA,CAAA,CAAUE,WAAW,EAAA;;;;;QAMnD,MAAM,EAAEC,2BAA2B,CAAC,EAAE,GAAGC,MAAAA,CAAOC,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;AAEnExB,QAAAA,GAAAA,CAAIE,IAAI,GAAG;YACTW,IAAAA,EAAM;AACJ,gBAAA,GAAGA,IAAI;AACPQ,gBAAAA,wBAAAA;gBACAI,mBAAAA,EAAqBP,gBAAAA,CAAW,sBAAsBQ,WAAW;AACnE;AACF,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-settings.mjs","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n const validated = await validateSettings(body);\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {
|
|
1
|
+
{"version":3,"file":"admin-settings.mjs","sources":["../../../server/src/controllers/admin-settings.ts"],"sourcesContent":["import type { Context } from 'koa';\n\nimport { getService } from '../utils';\nimport { ACTIONS, FILE_MODEL_UID } from '../constants';\n\nimport type { Config } from '../types';\nimport validateSettings, { settingsSchema } from './validation/admin/settings';\n\n// The stored settings' known keys. `GET /upload/settings` echoes read-only\n// config (e.g. `concurrentUploadRequests`) alongside them, and the legacy\n// Settings page PUTs the whole payload back — narrowing to these keys keeps\n// those echoes out of the store. Derived from the schema so it can't drift.\nconst SETTINGS_KEYS = Object.keys(settingsSchema.fields);\n\nexport default {\n async updateSettings(ctx: Context) {\n const {\n request: { body },\n state: { userAbility },\n } = ctx;\n\n if (userAbility.cannot(ACTIONS.readSettings, FILE_MODEL_UID)) {\n return ctx.forbidden();\n }\n\n const validated = await validateSettings(body);\n const source = validated as Record<string, unknown>;\n const data = Object.fromEntries(\n SETTINGS_KEYS.filter((key) => key in source).map((key) => [key, source[key]])\n ) as typeof validated;\n\n await getService('upload').setSettings(data);\n\n ctx.body = { data };\n },\n\n async getSettings(ctx: Context) {\n // Gated on `plugin::upload.read` by the route policy.\n const data = await getService('upload').getSettings();\n\n // Read-only echo of the app config so the admin knows how many upload\n // requests it may fire in parallel. Distinct from `concurrentUploadSize`,\n // which is the server-side per-request processing ceiling. Deployments that\n // need to bound this override the config.\n const { concurrentUploadRequests = 1 } = strapi.config.get<Config>('plugin::upload');\n\n ctx.body = {\n data: {\n ...data,\n concurrentUploadRequests,\n aiMetadataAvailable: getService('aiMetadataProvider').hasProvider(),\n },\n };\n },\n};\n"],"names":["SETTINGS_KEYS","Object","keys","settingsSchema","fields","updateSettings","ctx","request","body","state","userAbility","cannot","ACTIONS","readSettings","FILE_MODEL_UID","forbidden","validated","validateSettings","source","data","fromEntries","filter","key","map","getService","setSettings","getSettings","concurrentUploadRequests","strapi","config","get","aiMetadataAvailable","hasProvider"],"mappings":";;;;AAQA;AACA;AACA;AACA;AACA,MAAMA,aAAAA,GAAgBC,MAAAA,CAAOC,IAAI,CAACC,eAAeC,MAAM,CAAA;AAEvD,oBAAe;AACb,IAAA,MAAMC,gBAAeC,GAAY,EAAA;QAC/B,MAAM,EACJC,OAAAA,EAAS,EAAEC,IAAI,EAAE,EACjBC,KAAAA,EAAO,EAAEC,WAAW,EAAE,EACvB,GAAGJ,GAAAA;AAEJ,QAAA,IAAII,YAAYC,MAAM,CAACC,OAAAA,CAAQC,YAAY,EAAEC,cAAAA,CAAAA,EAAiB;AAC5D,YAAA,OAAOR,IAAIS,SAAS,EAAA;AACtB,QAAA;QAEA,MAAMC,SAAAA,GAAY,MAAMC,gBAAAA,CAAiBT,IAAAA,CAAAA;AACzC,QAAA,MAAMU,MAAAA,GAASF,SAAAA;AACf,QAAA,MAAMG,IAAAA,GAAOlB,MAAAA,CAAOmB,WAAW,CAC7BpB,cAAcqB,MAAM,CAAC,CAACC,GAAAA,GAAQA,GAAAA,IAAOJ,MAAAA,CAAAA,CAAQK,GAAG,CAAC,CAACD,GAAAA,GAAQ;AAACA,gBAAAA,GAAAA;AAAKJ,gBAAAA,MAAM,CAACI,GAAAA;AAAK,aAAA,CAAA,CAAA;QAG9E,MAAME,UAAAA,CAAW,QAAA,CAAA,CAAUC,WAAW,CAACN,IAAAA,CAAAA;AAEvCb,QAAAA,GAAAA,CAAIE,IAAI,GAAG;AAAEW,YAAAA;AAAK,SAAA;AACpB,IAAA,CAAA;AAEA,IAAA,MAAMO,aAAYpB,GAAY,EAAA;;AAE5B,QAAA,MAAMa,IAAAA,GAAO,MAAMK,UAAAA,CAAW,QAAA,CAAA,CAAUE,WAAW,EAAA;;;;;QAMnD,MAAM,EAAEC,2BAA2B,CAAC,EAAE,GAAGC,MAAAA,CAAOC,MAAM,CAACC,GAAG,CAAS,gBAAA,CAAA;AAEnExB,QAAAA,GAAAA,CAAIE,IAAI,GAAG;YACTW,IAAAA,EAAM;AACJ,gBAAA,GAAGA,IAAI;AACPQ,gBAAAA,wBAAAA;gBACAI,mBAAAA,EAAqBP,UAAAA,CAAW,sBAAsBQ,WAAW;AACnE;AACF,SAAA;AACF,IAAA;AACF,CAAA;;;;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var utils = require('@strapi/utils');
|
|
4
|
+
|
|
5
|
+
class FolderContainsUnauthorizedAssetsError extends utils.errors.PolicyError {
|
|
6
|
+
constructor(){
|
|
7
|
+
super('FolderContainsUnauthorizedAssetsError');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
exports.FolderContainsUnauthorizedAssetsError = FolderContainsUnauthorizedAssetsError;
|
|
12
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sources":["../../server/src/errors.ts"],"sourcesContent":["import { errors } from '@strapi/utils';\n\nclass FolderContainsUnauthorizedAssetsError extends errors.PolicyError<'FolderContainsUnauthorizedAssetsError'> {\n constructor() {\n super('FolderContainsUnauthorizedAssetsError');\n }\n}\n\nexport { FolderContainsUnauthorizedAssetsError };\n"],"names":["FolderContainsUnauthorizedAssetsError","errors","PolicyError"],"mappings":";;;;AAEA,MAAMA,qCAAAA,SAA8CC,aAAOC,WAAW,CAAA;IACpE,WAAA,EAAc;AACZ,QAAA,KAAK,CAAC,uCAAA,CAAA;AACR,IAAA;AACF;;;;"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { errors } from '@strapi/utils';
|
|
2
|
+
|
|
3
|
+
class FolderContainsUnauthorizedAssetsError extends errors.PolicyError {
|
|
4
|
+
constructor(){
|
|
5
|
+
super('FolderContainsUnauthorizedAssetsError');
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { FolderContainsUnauthorizedAssetsError };
|
|
10
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","sources":["../../server/src/errors.ts"],"sourcesContent":["import { errors } from '@strapi/utils';\n\nclass FolderContainsUnauthorizedAssetsError extends errors.PolicyError<'FolderContainsUnauthorizedAssetsError'> {\n constructor() {\n super('FolderContainsUnauthorizedAssetsError');\n }\n}\n\nexport { FolderContainsUnauthorizedAssetsError };\n"],"names":["FolderContainsUnauthorizedAssetsError","errors","PolicyError"],"mappings":";;AAEA,MAAMA,qCAAAA,SAA8CC,OAAOC,WAAW,CAAA;IACpE,WAAA,EAAc;AACZ,QAAA,KAAK,CAAC,uCAAA,CAAA;AACR,IAAA;AACF;;;;"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var z = require('zod/v4');
|
|
4
|
+
var aiMetadataStrapiManaged = require('./ai-metadata-strapi-managed.js');
|
|
5
|
+
|
|
6
|
+
function _interopNamespace(e) {
|
|
7
|
+
if (e && e.__esModule) return e;
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var z__namespace = /*#__PURE__*/_interopNamespace(z);
|
|
25
|
+
|
|
26
|
+
const resultSchema = z__namespace.object({
|
|
27
|
+
results: z__namespace.array(z__namespace.object({
|
|
28
|
+
altText: z__namespace.string(),
|
|
29
|
+
caption: z__namespace.string()
|
|
30
|
+
}))
|
|
31
|
+
});
|
|
32
|
+
// This service mirrors i18n's ai-translations service on purpose. Keep the two in sync,
|
|
33
|
+
// and only extract the shared logic once a third feature needs it.
|
|
34
|
+
const createAIMetadataProviderService = ({ strapi })=>{
|
|
35
|
+
let registeredProvider = null;
|
|
36
|
+
const resolveProvider = ()=>{
|
|
37
|
+
if (!strapi.ai.admin.isAvailable()) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return registeredProvider;
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
hasProvider () {
|
|
44
|
+
return resolveProvider() !== null;
|
|
45
|
+
},
|
|
46
|
+
registerProvider ({ provider }) {
|
|
47
|
+
if (!strapi.ai.admin.authorizeCustomProvider()) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (registeredProvider !== null) {
|
|
51
|
+
throw new Error(`The AI metadata provider "${registeredProvider.name}" is already registered, "${provider.name}" cannot replace it.`);
|
|
52
|
+
}
|
|
53
|
+
registeredProvider = provider;
|
|
54
|
+
},
|
|
55
|
+
registerStrapiManagedProvider () {
|
|
56
|
+
if (!strapi.ai.admin.isStrapiManagedAiEnabled()) {
|
|
57
|
+
strapi.log.warn('The Strapi-managed AI metadata provider was ignored: the Strapi license does not include the "cms-ai" feature.');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const provider = aiMetadataStrapiManaged.createStrapiManagedAiMetadataProvider({
|
|
61
|
+
strapi
|
|
62
|
+
});
|
|
63
|
+
if (registeredProvider !== null) {
|
|
64
|
+
throw new Error(`The AI metadata provider "${registeredProvider.name}" is already registered, "${provider.name}" cannot replace it.`);
|
|
65
|
+
}
|
|
66
|
+
registeredProvider = provider;
|
|
67
|
+
},
|
|
68
|
+
async generateMetadata (params) {
|
|
69
|
+
const provider = resolveProvider();
|
|
70
|
+
if (provider === null) {
|
|
71
|
+
throw new Error('No AI metadata provider is registered.');
|
|
72
|
+
}
|
|
73
|
+
const raw = await provider.generateMetadata(params);
|
|
74
|
+
const result = resultSchema.parse(raw);
|
|
75
|
+
const fileCount = result.results.length;
|
|
76
|
+
strapi.log.http(`AI generated metadata successfully for ${fileCount} file${fileCount === 1 ? '' : 's'}`);
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
exports.createAIMetadataProviderService = createAIMetadataProviderService;
|
|
83
|
+
//# sourceMappingURL=ai-metadata-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-metadata-provider.js","sources":["../../../server/src/services/ai-metadata-provider.ts"],"sourcesContent":["import type { Core, Modules } from '@strapi/types';\nimport * as z from 'zod/v4';\n\nimport { createStrapiManagedAiMetadataProvider } from './ai-metadata-strapi-managed';\n\nexport type GenerateMetadataParams = { images: Blob[] };\n\nexport type GenerateMetadataResult = {\n results: Array<{ altText: string; caption: string }>;\n};\n\nexport type AiMetadataProvider = Modules.AI.AiProvider & {\n generateMetadata: (params: GenerateMetadataParams) => Promise<GenerateMetadataResult>;\n};\n\nexport interface AiMetadataProviderService {\n hasProvider(): boolean;\n registerProvider(params: { provider: AiMetadataProvider }): void;\n registerStrapiManagedProvider(): void;\n generateMetadata(params: GenerateMetadataParams): Promise<GenerateMetadataResult>;\n}\n\nconst resultSchema = z.object({\n results: z.array(\n z.object({\n altText: z.string(),\n caption: z.string(),\n })\n ),\n});\n\n// This service mirrors i18n's ai-translations service on purpose. Keep the two in sync,\n// and only extract the shared logic once a third feature needs it.\nconst createAIMetadataProviderService = ({\n strapi,\n}: {\n strapi: Core.Strapi;\n}): AiMetadataProviderService => {\n let registeredProvider: AiMetadataProvider | null = null;\n\n const resolveProvider = (): AiMetadataProvider | null => {\n if (!strapi.ai.admin.isAvailable()) {\n return null;\n }\n\n return registeredProvider;\n };\n\n return {\n hasProvider(): boolean {\n return resolveProvider() !== null;\n },\n\n registerProvider({ provider }: { provider: AiMetadataProvider }) {\n if (!strapi.ai.admin.authorizeCustomProvider()) {\n return;\n }\n\n if (registeredProvider !== null) {\n throw new Error(\n `The AI metadata provider \"${registeredProvider.name}\" is already registered, \"${provider.name}\" cannot replace it.`\n );\n }\n\n registeredProvider = provider;\n },\n\n registerStrapiManagedProvider() {\n if (!strapi.ai.admin.isStrapiManagedAiEnabled()) {\n strapi.log.warn(\n 'The Strapi-managed AI metadata provider was ignored: the Strapi license does not include the \"cms-ai\" feature.'\n );\n return;\n }\n\n const provider = createStrapiManagedAiMetadataProvider({ strapi });\n\n if (registeredProvider !== null) {\n throw new Error(\n `The AI metadata provider \"${registeredProvider.name}\" is already registered, \"${provider.name}\" cannot replace it.`\n );\n }\n\n registeredProvider = provider;\n },\n\n async generateMetadata(params: GenerateMetadataParams): Promise<GenerateMetadataResult> {\n const provider = resolveProvider();\n\n if (provider === null) {\n throw new Error('No AI metadata provider is registered.');\n }\n\n const raw = await provider.generateMetadata(params);\n const result = resultSchema.parse(raw);\n\n const fileCount = result.results.length;\n\n strapi.log.http(\n `AI generated metadata successfully for ${fileCount} file${fileCount === 1 ? '' : 's'}`\n );\n\n return result;\n },\n };\n};\n\nexport { createAIMetadataProviderService };\n"],"names":["resultSchema","z","object","results","array","altText","string","caption","createAIMetadataProviderService","strapi","registeredProvider","resolveProvider","ai","admin","isAvailable","hasProvider","registerProvider","provider","authorizeCustomProvider","Error","name","registerStrapiManagedProvider","isStrapiManagedAiEnabled","log","warn","createStrapiManagedAiMetadataProvider","generateMetadata","params","raw","result","parse","fileCount","length","http"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAMA,YAAAA,GAAeC,YAAAA,CAAEC,MAAM,CAAC;AAC5BC,IAAAA,OAAAA,EAASF,YAAAA,CAAEG,KAAK,CACdH,YAAAA,CAAEC,MAAM,CAAC;AACPG,QAAAA,OAAAA,EAASJ,aAAEK,MAAM,EAAA;AACjBC,QAAAA,OAAAA,EAASN,aAAEK,MAAM;AACnB,KAAA,CAAA;AAEJ,CAAA,CAAA;AAEA;AACA;AACA,MAAME,+BAAAA,GAAkC,CAAC,EACvCC,MAAM,EAGP,GAAA;AACC,IAAA,IAAIC,kBAAAA,GAAgD,IAAA;AAEpD,IAAA,MAAMC,eAAAA,GAAkB,IAAA;AACtB,QAAA,IAAI,CAACF,MAAAA,CAAOG,EAAE,CAACC,KAAK,CAACC,WAAW,EAAA,EAAI;YAClC,OAAO,IAAA;AACT,QAAA;QAEA,OAAOJ,kBAAAA;AACT,IAAA,CAAA;IAEA,OAAO;AACLK,QAAAA,WAAAA,CAAAA,GAAAA;AACE,YAAA,OAAOJ,eAAAA,EAAAA,KAAsB,IAAA;AAC/B,QAAA,CAAA;QAEAK,gBAAAA,CAAAA,CAAiB,EAAEC,QAAQ,EAAoC,EAAA;AAC7D,YAAA,IAAI,CAACR,MAAAA,CAAOG,EAAE,CAACC,KAAK,CAACK,uBAAuB,EAAA,EAAI;AAC9C,gBAAA;AACF,YAAA;AAEA,YAAA,IAAIR,uBAAuB,IAAA,EAAM;AAC/B,gBAAA,MAAM,IAAIS,KAAAA,CACR,CAAC,0BAA0B,EAAET,kBAAAA,CAAmBU,IAAI,CAAC,0BAA0B,EAAEH,QAAAA,CAASG,IAAI,CAAC,oBAAoB,CAAC,CAAA;AAExH,YAAA;YAEAV,kBAAAA,GAAqBO,QAAAA;AACvB,QAAA,CAAA;AAEAI,QAAAA,6BAAAA,CAAAA,GAAAA;AACE,YAAA,IAAI,CAACZ,MAAAA,CAAOG,EAAE,CAACC,KAAK,CAACS,wBAAwB,EAAA,EAAI;gBAC/Cb,MAAAA,CAAOc,GAAG,CAACC,IAAI,CACb,gHAAA,CAAA;AAEF,gBAAA;AACF,YAAA;AAEA,YAAA,MAAMP,WAAWQ,6DAAAA,CAAsC;AAAEhB,gBAAAA;AAAO,aAAA,CAAA;AAEhE,YAAA,IAAIC,uBAAuB,IAAA,EAAM;AAC/B,gBAAA,MAAM,IAAIS,KAAAA,CACR,CAAC,0BAA0B,EAAET,kBAAAA,CAAmBU,IAAI,CAAC,0BAA0B,EAAEH,QAAAA,CAASG,IAAI,CAAC,oBAAoB,CAAC,CAAA;AAExH,YAAA;YAEAV,kBAAAA,GAAqBO,QAAAA;AACvB,QAAA,CAAA;AAEA,QAAA,MAAMS,kBAAiBC,MAA8B,EAAA;AACnD,YAAA,MAAMV,QAAAA,GAAWN,eAAAA,EAAAA;AAEjB,YAAA,IAAIM,aAAa,IAAA,EAAM;AACrB,gBAAA,MAAM,IAAIE,KAAAA,CAAM,wCAAA,CAAA;AAClB,YAAA;AAEA,YAAA,MAAMS,GAAAA,GAAM,MAAMX,QAAAA,CAASS,gBAAgB,CAACC,MAAAA,CAAAA;YAC5C,MAAME,MAAAA,GAAS7B,YAAAA,CAAa8B,KAAK,CAACF,GAAAA,CAAAA;AAElC,YAAA,MAAMG,SAAAA,GAAYF,MAAAA,CAAO1B,OAAO,CAAC6B,MAAM;AAEvCvB,YAAAA,MAAAA,CAAOc,GAAG,CAACU,IAAI,CACb,CAAC,uCAAuC,EAAEF,SAAAA,CAAU,KAAK,EAAEA,SAAAA,KAAc,CAAA,GAAI,KAAK,GAAA,CAAA,CAAK,CAAA;YAGzF,OAAOF,MAAAA;AACT,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
import { createStrapiManagedAiMetadataProvider } from './ai-metadata-strapi-managed.mjs';
|
|
3
|
+
|
|
4
|
+
const resultSchema = z.object({
|
|
5
|
+
results: z.array(z.object({
|
|
6
|
+
altText: z.string(),
|
|
7
|
+
caption: z.string()
|
|
8
|
+
}))
|
|
9
|
+
});
|
|
10
|
+
// This service mirrors i18n's ai-translations service on purpose. Keep the two in sync,
|
|
11
|
+
// and only extract the shared logic once a third feature needs it.
|
|
12
|
+
const createAIMetadataProviderService = ({ strapi })=>{
|
|
13
|
+
let registeredProvider = null;
|
|
14
|
+
const resolveProvider = ()=>{
|
|
15
|
+
if (!strapi.ai.admin.isAvailable()) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return registeredProvider;
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
hasProvider () {
|
|
22
|
+
return resolveProvider() !== null;
|
|
23
|
+
},
|
|
24
|
+
registerProvider ({ provider }) {
|
|
25
|
+
if (!strapi.ai.admin.authorizeCustomProvider()) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (registeredProvider !== null) {
|
|
29
|
+
throw new Error(`The AI metadata provider "${registeredProvider.name}" is already registered, "${provider.name}" cannot replace it.`);
|
|
30
|
+
}
|
|
31
|
+
registeredProvider = provider;
|
|
32
|
+
},
|
|
33
|
+
registerStrapiManagedProvider () {
|
|
34
|
+
if (!strapi.ai.admin.isStrapiManagedAiEnabled()) {
|
|
35
|
+
strapi.log.warn('The Strapi-managed AI metadata provider was ignored: the Strapi license does not include the "cms-ai" feature.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const provider = createStrapiManagedAiMetadataProvider({
|
|
39
|
+
strapi
|
|
40
|
+
});
|
|
41
|
+
if (registeredProvider !== null) {
|
|
42
|
+
throw new Error(`The AI metadata provider "${registeredProvider.name}" is already registered, "${provider.name}" cannot replace it.`);
|
|
43
|
+
}
|
|
44
|
+
registeredProvider = provider;
|
|
45
|
+
},
|
|
46
|
+
async generateMetadata (params) {
|
|
47
|
+
const provider = resolveProvider();
|
|
48
|
+
if (provider === null) {
|
|
49
|
+
throw new Error('No AI metadata provider is registered.');
|
|
50
|
+
}
|
|
51
|
+
const raw = await provider.generateMetadata(params);
|
|
52
|
+
const result = resultSchema.parse(raw);
|
|
53
|
+
const fileCount = result.results.length;
|
|
54
|
+
strapi.log.http(`AI generated metadata successfully for ${fileCount} file${fileCount === 1 ? '' : 's'}`);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { createAIMetadataProviderService };
|
|
61
|
+
//# sourceMappingURL=ai-metadata-provider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-metadata-provider.mjs","sources":["../../../server/src/services/ai-metadata-provider.ts"],"sourcesContent":["import type { Core, Modules } from '@strapi/types';\nimport * as z from 'zod/v4';\n\nimport { createStrapiManagedAiMetadataProvider } from './ai-metadata-strapi-managed';\n\nexport type GenerateMetadataParams = { images: Blob[] };\n\nexport type GenerateMetadataResult = {\n results: Array<{ altText: string; caption: string }>;\n};\n\nexport type AiMetadataProvider = Modules.AI.AiProvider & {\n generateMetadata: (params: GenerateMetadataParams) => Promise<GenerateMetadataResult>;\n};\n\nexport interface AiMetadataProviderService {\n hasProvider(): boolean;\n registerProvider(params: { provider: AiMetadataProvider }): void;\n registerStrapiManagedProvider(): void;\n generateMetadata(params: GenerateMetadataParams): Promise<GenerateMetadataResult>;\n}\n\nconst resultSchema = z.object({\n results: z.array(\n z.object({\n altText: z.string(),\n caption: z.string(),\n })\n ),\n});\n\n// This service mirrors i18n's ai-translations service on purpose. Keep the two in sync,\n// and only extract the shared logic once a third feature needs it.\nconst createAIMetadataProviderService = ({\n strapi,\n}: {\n strapi: Core.Strapi;\n}): AiMetadataProviderService => {\n let registeredProvider: AiMetadataProvider | null = null;\n\n const resolveProvider = (): AiMetadataProvider | null => {\n if (!strapi.ai.admin.isAvailable()) {\n return null;\n }\n\n return registeredProvider;\n };\n\n return {\n hasProvider(): boolean {\n return resolveProvider() !== null;\n },\n\n registerProvider({ provider }: { provider: AiMetadataProvider }) {\n if (!strapi.ai.admin.authorizeCustomProvider()) {\n return;\n }\n\n if (registeredProvider !== null) {\n throw new Error(\n `The AI metadata provider \"${registeredProvider.name}\" is already registered, \"${provider.name}\" cannot replace it.`\n );\n }\n\n registeredProvider = provider;\n },\n\n registerStrapiManagedProvider() {\n if (!strapi.ai.admin.isStrapiManagedAiEnabled()) {\n strapi.log.warn(\n 'The Strapi-managed AI metadata provider was ignored: the Strapi license does not include the \"cms-ai\" feature.'\n );\n return;\n }\n\n const provider = createStrapiManagedAiMetadataProvider({ strapi });\n\n if (registeredProvider !== null) {\n throw new Error(\n `The AI metadata provider \"${registeredProvider.name}\" is already registered, \"${provider.name}\" cannot replace it.`\n );\n }\n\n registeredProvider = provider;\n },\n\n async generateMetadata(params: GenerateMetadataParams): Promise<GenerateMetadataResult> {\n const provider = resolveProvider();\n\n if (provider === null) {\n throw new Error('No AI metadata provider is registered.');\n }\n\n const raw = await provider.generateMetadata(params);\n const result = resultSchema.parse(raw);\n\n const fileCount = result.results.length;\n\n strapi.log.http(\n `AI generated metadata successfully for ${fileCount} file${fileCount === 1 ? '' : 's'}`\n );\n\n return result;\n },\n };\n};\n\nexport { createAIMetadataProviderService };\n"],"names":["resultSchema","z","object","results","array","altText","string","caption","createAIMetadataProviderService","strapi","registeredProvider","resolveProvider","ai","admin","isAvailable","hasProvider","registerProvider","provider","authorizeCustomProvider","Error","name","registerStrapiManagedProvider","isStrapiManagedAiEnabled","log","warn","createStrapiManagedAiMetadataProvider","generateMetadata","params","raw","result","parse","fileCount","length","http"],"mappings":";;;AAsBA,MAAMA,YAAAA,GAAeC,CAAAA,CAAEC,MAAM,CAAC;AAC5BC,IAAAA,OAAAA,EAASF,CAAAA,CAAEG,KAAK,CACdH,CAAAA,CAAEC,MAAM,CAAC;AACPG,QAAAA,OAAAA,EAASJ,EAAEK,MAAM,EAAA;AACjBC,QAAAA,OAAAA,EAASN,EAAEK,MAAM;AACnB,KAAA,CAAA;AAEJ,CAAA,CAAA;AAEA;AACA;AACA,MAAME,+BAAAA,GAAkC,CAAC,EACvCC,MAAM,EAGP,GAAA;AACC,IAAA,IAAIC,kBAAAA,GAAgD,IAAA;AAEpD,IAAA,MAAMC,eAAAA,GAAkB,IAAA;AACtB,QAAA,IAAI,CAACF,MAAAA,CAAOG,EAAE,CAACC,KAAK,CAACC,WAAW,EAAA,EAAI;YAClC,OAAO,IAAA;AACT,QAAA;QAEA,OAAOJ,kBAAAA;AACT,IAAA,CAAA;IAEA,OAAO;AACLK,QAAAA,WAAAA,CAAAA,GAAAA;AACE,YAAA,OAAOJ,eAAAA,EAAAA,KAAsB,IAAA;AAC/B,QAAA,CAAA;QAEAK,gBAAAA,CAAAA,CAAiB,EAAEC,QAAQ,EAAoC,EAAA;AAC7D,YAAA,IAAI,CAACR,MAAAA,CAAOG,EAAE,CAACC,KAAK,CAACK,uBAAuB,EAAA,EAAI;AAC9C,gBAAA;AACF,YAAA;AAEA,YAAA,IAAIR,uBAAuB,IAAA,EAAM;AAC/B,gBAAA,MAAM,IAAIS,KAAAA,CACR,CAAC,0BAA0B,EAAET,kBAAAA,CAAmBU,IAAI,CAAC,0BAA0B,EAAEH,QAAAA,CAASG,IAAI,CAAC,oBAAoB,CAAC,CAAA;AAExH,YAAA;YAEAV,kBAAAA,GAAqBO,QAAAA;AACvB,QAAA,CAAA;AAEAI,QAAAA,6BAAAA,CAAAA,GAAAA;AACE,YAAA,IAAI,CAACZ,MAAAA,CAAOG,EAAE,CAACC,KAAK,CAACS,wBAAwB,EAAA,EAAI;gBAC/Cb,MAAAA,CAAOc,GAAG,CAACC,IAAI,CACb,gHAAA,CAAA;AAEF,gBAAA;AACF,YAAA;AAEA,YAAA,MAAMP,WAAWQ,qCAAAA,CAAsC;AAAEhB,gBAAAA;AAAO,aAAA,CAAA;AAEhE,YAAA,IAAIC,uBAAuB,IAAA,EAAM;AAC/B,gBAAA,MAAM,IAAIS,KAAAA,CACR,CAAC,0BAA0B,EAAET,kBAAAA,CAAmBU,IAAI,CAAC,0BAA0B,EAAEH,QAAAA,CAASG,IAAI,CAAC,oBAAoB,CAAC,CAAA;AAExH,YAAA;YAEAV,kBAAAA,GAAqBO,QAAAA;AACvB,QAAA,CAAA;AAEA,QAAA,MAAMS,kBAAiBC,MAA8B,EAAA;AACnD,YAAA,MAAMV,QAAAA,GAAWN,eAAAA,EAAAA;AAEjB,YAAA,IAAIM,aAAa,IAAA,EAAM;AACrB,gBAAA,MAAM,IAAIE,KAAAA,CAAM,wCAAA,CAAA;AAClB,YAAA;AAEA,YAAA,MAAMS,GAAAA,GAAM,MAAMX,QAAAA,CAASS,gBAAgB,CAACC,MAAAA,CAAAA;YAC5C,MAAME,MAAAA,GAAS7B,YAAAA,CAAa8B,KAAK,CAACF,GAAAA,CAAAA;AAElC,YAAA,MAAMG,SAAAA,GAAYF,MAAAA,CAAO1B,OAAO,CAAC6B,MAAM;AAEvCvB,YAAAA,MAAAA,CAAOc,GAAG,CAACU,IAAI,CACb,CAAC,uCAAuC,EAAEF,SAAAA,CAAU,KAAK,EAAEA,SAAAA,KAAc,CAAA,GAAI,KAAK,GAAA,CAAA,CAAK,CAAA;YAGzF,OAAOF,MAAAA;AACT,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const createStrapiManagedAiMetadataProvider = ({ strapi })=>{
|
|
4
|
+
// TODO(ai): add a helper function to get the AI server URL
|
|
5
|
+
const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';
|
|
6
|
+
const getAiToken = async ()=>{
|
|
7
|
+
try {
|
|
8
|
+
const { token } = await strapi.ai.admin.getAiToken();
|
|
9
|
+
return token;
|
|
10
|
+
} catch (error) {
|
|
11
|
+
throw new Error('Failed to retrieve AI token', {
|
|
12
|
+
cause: error instanceof Error ? error : undefined
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
name: 'strapi-managed',
|
|
18
|
+
async generateMetadata ({ images }) {
|
|
19
|
+
const formData = new FormData();
|
|
20
|
+
for (const image of images){
|
|
21
|
+
formData.append('files', image);
|
|
22
|
+
}
|
|
23
|
+
const token = await getAiToken();
|
|
24
|
+
strapi.log.http('Contacting AI Server for media metadata generation', {
|
|
25
|
+
aiServerUrl,
|
|
26
|
+
imageCount: images.length
|
|
27
|
+
});
|
|
28
|
+
const res = await fetch(`${aiServerUrl}/media-library/generate-metadata`, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
body: formData,
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: `Bearer ${token}`
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const errorText = await res.text();
|
|
37
|
+
strapi.log.error(`AI metadata generation request failed: ${res.status} ${res.statusText}`);
|
|
38
|
+
throw Error(`AI metadata generation failed`, {
|
|
39
|
+
cause: errorText
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return await res.json();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
exports.createStrapiManagedAiMetadataProvider = createStrapiManagedAiMetadataProvider;
|
|
48
|
+
//# sourceMappingURL=ai-metadata-strapi-managed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-metadata-strapi-managed.js","sources":["../../../server/src/services/ai-metadata-strapi-managed.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport type { AiMetadataProvider } from './ai-metadata-provider';\n\nconst createStrapiManagedAiMetadataProvider = ({\n strapi,\n}: {\n strapi: Core.Strapi;\n}): AiMetadataProvider => {\n // TODO(ai): add a helper function to get the AI server URL\n const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';\n\n const getAiToken = async () => {\n try {\n const { token } = await strapi.ai.admin.getAiToken();\n return token;\n } catch (error) {\n throw new Error('Failed to retrieve AI token', {\n cause: error instanceof Error ? error : undefined,\n });\n }\n };\n\n return {\n name: 'strapi-managed',\n\n async generateMetadata({ images }) {\n const formData = new FormData();\n\n for (const image of images) {\n formData.append('files', image);\n }\n\n const token = await getAiToken();\n\n strapi.log.http('Contacting AI Server for media metadata generation', {\n aiServerUrl,\n imageCount: images.length,\n });\n\n const res = await fetch(`${aiServerUrl}/media-library/generate-metadata`, {\n method: 'POST',\n body: formData,\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) {\n const errorText = await res.text();\n strapi.log.error(`AI metadata generation request failed: ${res.status} ${res.statusText}`);\n\n throw Error(`AI metadata generation failed`, { cause: errorText });\n }\n\n return (await res.json()) as Awaited<ReturnType<AiMetadataProvider['generateMetadata']>>;\n },\n };\n};\n\nexport { createStrapiManagedAiMetadataProvider };\n"],"names":["createStrapiManagedAiMetadataProvider","strapi","aiServerUrl","process","env","STRAPI_AI_URL","getAiToken","token","ai","admin","error","Error","cause","undefined","name","generateMetadata","images","formData","FormData","image","append","log","http","imageCount","length","res","fetch","method","body","headers","Authorization","ok","errorText","text","status","statusText","json"],"mappings":";;AAIA,MAAMA,qCAAAA,GAAwC,CAAC,EAC7CC,MAAM,EAGP,GAAA;;AAEC,IAAA,MAAMC,WAAAA,GAAcC,OAAAA,CAAQC,GAAG,CAACC,aAAa,IAAI,kCAAA;AAEjD,IAAA,MAAMC,UAAAA,GAAa,UAAA;QACjB,IAAI;YACF,MAAM,EAAEC,KAAK,EAAE,GAAG,MAAMN,OAAOO,EAAE,CAACC,KAAK,CAACH,UAAU,EAAA;YAClD,OAAOC,KAAAA;AACT,QAAA,CAAA,CAAE,OAAOG,KAAAA,EAAO;YACd,MAAM,IAAIC,MAAM,6BAAA,EAA+B;gBAC7CC,KAAAA,EAAOF,KAAAA,YAAiBC,QAAQD,KAAAA,GAAQG;AAC1C,aAAA,CAAA;AACF,QAAA;AACF,IAAA,CAAA;IAEA,OAAO;QACLC,IAAAA,EAAM,gBAAA;QAEN,MAAMC,gBAAAA,CAAAA,CAAiB,EAAEC,MAAM,EAAE,EAAA;AAC/B,YAAA,MAAMC,WAAW,IAAIC,QAAAA,EAAAA;YAErB,KAAK,MAAMC,SAASH,MAAAA,CAAQ;gBAC1BC,QAAAA,CAASG,MAAM,CAAC,OAAA,EAASD,KAAAA,CAAAA;AAC3B,YAAA;AAEA,YAAA,MAAMZ,QAAQ,MAAMD,UAAAA,EAAAA;AAEpBL,YAAAA,MAAAA,CAAOoB,GAAG,CAACC,IAAI,CAAC,oDAAA,EAAsD;AACpEpB,gBAAAA,WAAAA;AACAqB,gBAAAA,UAAAA,EAAYP,OAAOQ;AACrB,aAAA,CAAA;AAEA,YAAA,MAAMC,MAAM,MAAMC,KAAAA,CAAM,GAAGxB,WAAAA,CAAY,gCAAgC,CAAC,EAAE;gBACxEyB,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAMX,QAAAA;gBACNY,OAAAA,EAAS;oBACPC,aAAAA,EAAe,CAAC,OAAO,EAAEvB,KAAAA,CAAAA;AAC3B;AACF,aAAA,CAAA;YAEA,IAAI,CAACkB,GAAAA,CAAIM,EAAE,EAAE;gBACX,MAAMC,SAAAA,GAAY,MAAMP,GAAAA,CAAIQ,IAAI,EAAA;AAChChC,gBAAAA,MAAAA,CAAOoB,GAAG,CAACX,KAAK,CAAC,CAAC,uCAAuC,EAAEe,GAAAA,CAAIS,MAAM,CAAC,CAAC,EAAET,GAAAA,CAAIU,UAAU,CAAA,CAAE,CAAA;AAEzF,gBAAA,MAAMxB,KAAAA,CAAM,CAAC,6BAA6B,CAAC,EAAE;oBAAEC,KAAAA,EAAOoB;AAAU,iBAAA,CAAA;AAClE,YAAA;YAEA,OAAQ,MAAMP,IAAIW,IAAI,EAAA;AACxB,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const createStrapiManagedAiMetadataProvider = ({ strapi })=>{
|
|
2
|
+
// TODO(ai): add a helper function to get the AI server URL
|
|
3
|
+
const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';
|
|
4
|
+
const getAiToken = async ()=>{
|
|
5
|
+
try {
|
|
6
|
+
const { token } = await strapi.ai.admin.getAiToken();
|
|
7
|
+
return token;
|
|
8
|
+
} catch (error) {
|
|
9
|
+
throw new Error('Failed to retrieve AI token', {
|
|
10
|
+
cause: error instanceof Error ? error : undefined
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
name: 'strapi-managed',
|
|
16
|
+
async generateMetadata ({ images }) {
|
|
17
|
+
const formData = new FormData();
|
|
18
|
+
for (const image of images){
|
|
19
|
+
formData.append('files', image);
|
|
20
|
+
}
|
|
21
|
+
const token = await getAiToken();
|
|
22
|
+
strapi.log.http('Contacting AI Server for media metadata generation', {
|
|
23
|
+
aiServerUrl,
|
|
24
|
+
imageCount: images.length
|
|
25
|
+
});
|
|
26
|
+
const res = await fetch(`${aiServerUrl}/media-library/generate-metadata`, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
body: formData,
|
|
29
|
+
headers: {
|
|
30
|
+
Authorization: `Bearer ${token}`
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
const errorText = await res.text();
|
|
35
|
+
strapi.log.error(`AI metadata generation request failed: ${res.status} ${res.statusText}`);
|
|
36
|
+
throw Error(`AI metadata generation failed`, {
|
|
37
|
+
cause: errorText
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return await res.json();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export { createStrapiManagedAiMetadataProvider };
|
|
46
|
+
//# sourceMappingURL=ai-metadata-strapi-managed.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-metadata-strapi-managed.mjs","sources":["../../../server/src/services/ai-metadata-strapi-managed.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport type { AiMetadataProvider } from './ai-metadata-provider';\n\nconst createStrapiManagedAiMetadataProvider = ({\n strapi,\n}: {\n strapi: Core.Strapi;\n}): AiMetadataProvider => {\n // TODO(ai): add a helper function to get the AI server URL\n const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';\n\n const getAiToken = async () => {\n try {\n const { token } = await strapi.ai.admin.getAiToken();\n return token;\n } catch (error) {\n throw new Error('Failed to retrieve AI token', {\n cause: error instanceof Error ? error : undefined,\n });\n }\n };\n\n return {\n name: 'strapi-managed',\n\n async generateMetadata({ images }) {\n const formData = new FormData();\n\n for (const image of images) {\n formData.append('files', image);\n }\n\n const token = await getAiToken();\n\n strapi.log.http('Contacting AI Server for media metadata generation', {\n aiServerUrl,\n imageCount: images.length,\n });\n\n const res = await fetch(`${aiServerUrl}/media-library/generate-metadata`, {\n method: 'POST',\n body: formData,\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) {\n const errorText = await res.text();\n strapi.log.error(`AI metadata generation request failed: ${res.status} ${res.statusText}`);\n\n throw Error(`AI metadata generation failed`, { cause: errorText });\n }\n\n return (await res.json()) as Awaited<ReturnType<AiMetadataProvider['generateMetadata']>>;\n },\n };\n};\n\nexport { createStrapiManagedAiMetadataProvider };\n"],"names":["createStrapiManagedAiMetadataProvider","strapi","aiServerUrl","process","env","STRAPI_AI_URL","getAiToken","token","ai","admin","error","Error","cause","undefined","name","generateMetadata","images","formData","FormData","image","append","log","http","imageCount","length","res","fetch","method","body","headers","Authorization","ok","errorText","text","status","statusText","json"],"mappings":"AAIA,MAAMA,qCAAAA,GAAwC,CAAC,EAC7CC,MAAM,EAGP,GAAA;;AAEC,IAAA,MAAMC,WAAAA,GAAcC,OAAAA,CAAQC,GAAG,CAACC,aAAa,IAAI,kCAAA;AAEjD,IAAA,MAAMC,UAAAA,GAAa,UAAA;QACjB,IAAI;YACF,MAAM,EAAEC,KAAK,EAAE,GAAG,MAAMN,OAAOO,EAAE,CAACC,KAAK,CAACH,UAAU,EAAA;YAClD,OAAOC,KAAAA;AACT,QAAA,CAAA,CAAE,OAAOG,KAAAA,EAAO;YACd,MAAM,IAAIC,MAAM,6BAAA,EAA+B;gBAC7CC,KAAAA,EAAOF,KAAAA,YAAiBC,QAAQD,KAAAA,GAAQG;AAC1C,aAAA,CAAA;AACF,QAAA;AACF,IAAA,CAAA;IAEA,OAAO;QACLC,IAAAA,EAAM,gBAAA;QAEN,MAAMC,gBAAAA,CAAAA,CAAiB,EAAEC,MAAM,EAAE,EAAA;AAC/B,YAAA,MAAMC,WAAW,IAAIC,QAAAA,EAAAA;YAErB,KAAK,MAAMC,SAASH,MAAAA,CAAQ;gBAC1BC,QAAAA,CAASG,MAAM,CAAC,OAAA,EAASD,KAAAA,CAAAA;AAC3B,YAAA;AAEA,YAAA,MAAMZ,QAAQ,MAAMD,UAAAA,EAAAA;AAEpBL,YAAAA,MAAAA,CAAOoB,GAAG,CAACC,IAAI,CAAC,oDAAA,EAAsD;AACpEpB,gBAAAA,WAAAA;AACAqB,gBAAAA,UAAAA,EAAYP,OAAOQ;AACrB,aAAA,CAAA;AAEA,YAAA,MAAMC,MAAM,MAAMC,KAAAA,CAAM,GAAGxB,WAAAA,CAAY,gCAAgC,CAAC,EAAE;gBACxEyB,MAAAA,EAAQ,MAAA;gBACRC,IAAAA,EAAMX,QAAAA;gBACNY,OAAAA,EAAS;oBACPC,aAAAA,EAAe,CAAC,OAAO,EAAEvB,KAAAA,CAAAA;AAC3B;AACF,aAAA,CAAA;YAEA,IAAI,CAACkB,GAAAA,CAAIM,EAAE,EAAE;gBACX,MAAMC,SAAAA,GAAY,MAAMP,GAAAA,CAAIQ,IAAI,EAAA;AAChChC,gBAAAA,MAAAA,CAAOoB,GAAG,CAACX,KAAK,CAAC,CAAC,uCAAuC,EAAEe,GAAAA,CAAIS,MAAM,CAAC,CAAC,EAAET,GAAAA,CAAIU,UAAU,CAAA,CAAE,CAAA;AAEzF,gBAAA,MAAMxB,KAAAA,CAAM,CAAC,6BAA6B,CAAC,EAAE;oBAAEC,KAAAA,EAAOoB;AAAU,iBAAA,CAAA;AAClE,YAAA;YAEA,OAAQ,MAAMP,IAAIW,IAAI,EAAA;AACxB,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var z = require('zod/v4');
|
|
4
3
|
var index = require('../utils/index.js');
|
|
5
4
|
var images = require('../utils/images.js');
|
|
6
5
|
var constants = require('../shared/constants.js');
|
|
7
6
|
|
|
8
|
-
function _interopNamespace(e) {
|
|
9
|
-
if (e && e.__esModule) return e;
|
|
10
|
-
var n = Object.create(null);
|
|
11
|
-
if (e) {
|
|
12
|
-
Object.keys(e).forEach(function (k) {
|
|
13
|
-
if (k !== 'default') {
|
|
14
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
-
enumerable: true,
|
|
17
|
-
get: function () { return e[k]; }
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
n.default = e;
|
|
23
|
-
return Object.freeze(n);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
var z__namespace = /*#__PURE__*/_interopNamespace(z);
|
|
27
|
-
|
|
28
7
|
/**
|
|
29
8
|
* Supported image types for AI metadata generation. Lives in `shared/` so the
|
|
30
9
|
* admin panel gates the bulk action on exactly what the provider accepts.
|
|
@@ -38,10 +17,9 @@ const chunk = (items, size)=>{
|
|
|
38
17
|
return chunks;
|
|
39
18
|
};
|
|
40
19
|
const createAIMetadataService = ({ strapi })=>{
|
|
41
|
-
const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';
|
|
42
20
|
return {
|
|
43
21
|
async isEnabled () {
|
|
44
|
-
if (
|
|
22
|
+
if (!index.getService('aiMetadataProvider').hasProvider()) {
|
|
45
23
|
return false;
|
|
46
24
|
}
|
|
47
25
|
const settings = await strapi.plugin('upload').service('upload').getSettings();
|
|
@@ -276,7 +254,7 @@ const createAIMetadataService = ({ strapi })=>{
|
|
|
276
254
|
/**
|
|
277
255
|
* Processes provided files for AI metadata generation
|
|
278
256
|
*/ async processFiles (files) {
|
|
279
|
-
if (!await this.isEnabled()
|
|
257
|
+
if (!await this.isEnabled()) {
|
|
280
258
|
throw new Error('AI Metadata service is not enabled');
|
|
281
259
|
}
|
|
282
260
|
// Filter for image files only and track their original positions
|
|
@@ -304,41 +282,10 @@ const createAIMetadataService = ({ strapi })=>{
|
|
|
304
282
|
if (imageFiles.length === 0) {
|
|
305
283
|
return createEmptyMetadataResults();
|
|
306
284
|
}
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const tokenData = await strapi.ai.admin.getAiToken();
|
|
311
|
-
token = tokenData.token;
|
|
312
|
-
} catch (error) {
|
|
313
|
-
throw new Error('Failed to retrieve AI token', {
|
|
314
|
-
cause: error instanceof Error ? error : undefined
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
strapi.log.http('Contacting AI Server for media metadata generation', {
|
|
318
|
-
aiServerUrl,
|
|
319
|
-
imageCount: imageFiles.length
|
|
320
|
-
});
|
|
321
|
-
const res = await fetch(`${aiServerUrl}/media-library/generate-metadata`, {
|
|
322
|
-
method: 'POST',
|
|
323
|
-
body: formData,
|
|
324
|
-
headers: {
|
|
325
|
-
Authorization: `Bearer ${token}`
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
if (!res.ok) {
|
|
329
|
-
const errorText = await res.text();
|
|
330
|
-
throw Error(`AI metadata generation failed`, {
|
|
331
|
-
cause: errorText
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
const responseSchema = z__namespace.object({
|
|
335
|
-
results: z__namespace.array(z__namespace.object({
|
|
336
|
-
altText: z__namespace.string(),
|
|
337
|
-
caption: z__namespace.string()
|
|
338
|
-
}))
|
|
285
|
+
const images$1 = await images.fetchImagesAsBlobs(imageInputFiles, strapi.config.get('server.absoluteUrl'), strapi.log);
|
|
286
|
+
const { results } = await index.getService('aiMetadataProvider').generateMetadata({
|
|
287
|
+
images: images$1
|
|
339
288
|
});
|
|
340
|
-
const { results } = responseSchema.parse(await res.json());
|
|
341
|
-
strapi.log.http(`AI generated metadata successfully for ${results.length} files`);
|
|
342
289
|
// Create sparse array with results at original indices
|
|
343
290
|
// Example: files=[img1, pdf, img2] -> imageFiles=[{img1, index:0}, {img2, index:2}]
|
|
344
291
|
// AI results=[meta1, meta2] -> sparse=[meta1, null, meta2]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-metadata.js","sources":["../../../server/src/services/ai-metadata.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\nimport * as z from 'zod/v4';\nimport { InputFile, File } from '../types';\nimport { Settings } from '../controllers/validation/admin/settings';\nimport { getService } from '../utils';\nimport { buildFormDataFromFiles } from '../utils/images';\nimport { AI_METADATA_CHUNK_SIZE, AI_METADATA_SUPPORTED_IMAGE_TYPES } from '../constants';\n\nimport { isAIMetadataSupportedMime } from '../../../shared/constants';\n\nimport type { GenerateAIMetadata } from '../../../shared/contracts/files';\n\nexport type AIMetadataFileResult = GenerateAIMetadata.FileResult;\n\n/**\n * Supported image types for AI metadata generation. Lives in `shared/` so the\n * admin panel gates the bulk action on exactly what the provider accepts.\n */\nconst SUPPORTED_IMAGE_TYPES = AI_METADATA_SUPPORTED_IMAGE_TYPES;\n\nconst isSupportedImage = (file: File): boolean => isAIMetadataSupportedMime(file.mime);\n\nconst chunk = <T>(items: T[], size: number): T[][] => {\n const chunks: T[][] = [];\n\n for (let i = 0; i < items.length; i += size) {\n chunks.push(items.slice(i, i + size));\n }\n\n return chunks;\n};\n\nconst createAIMetadataService = ({ strapi }: { strapi: Core.Strapi }) => {\n const aiServerUrl = process.env.STRAPI_AI_URL || 'https://strapi-ai.apps.strapi.io';\n\n return {\n async isEnabled() {\n if (strapi.ai.admin.isStrapiManagedAiEnabled() === false) {\n return false;\n }\n const settings: Settings = await strapi.plugin('upload').service('upload').getSettings();\n return settings.aiMetadata ?? true;\n },\n\n async countImagesWithoutMetadata() {\n const imagesWithoutMetadataCountPromise = strapi.db.query('plugin::upload.file').count({\n where: {\n mime: {\n $in: SUPPORTED_IMAGE_TYPES,\n },\n $or: [\n { alternativeText: { $null: true } },\n { alternativeText: '' },\n { caption: { $null: true } },\n { caption: '' },\n ],\n },\n });\n\n const totalImagesPromise = strapi.db.query('plugin::upload.file').count({\n where: {\n mime: {\n $in: SUPPORTED_IMAGE_TYPES,\n },\n },\n });\n\n const [imagesWithoutMetadataCount, totalImages] = await Promise.all([\n imagesWithoutMetadataCountPromise,\n totalImagesPromise,\n ]);\n\n return { imagesWithoutMetadataCount, totalImages };\n },\n\n /**\n * Update files with AI-generated metadata\n * Shared logic used by both upload flow and retroactive processing\n */\n async updateFilesWithAIMetadata(\n files: File[],\n metadataResults: Array<{ altText: string; caption: string } | null>,\n user: { id: string | number }\n ) {\n const uploadService = strapi.plugin('upload').service('upload');\n\n await Promise.all(\n files.map(async (file, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n // Only update fields that are missing (null or empty string)\n const updateData: { alternativeText?: string; caption?: string } = {};\n\n if (!file.alternativeText || file.alternativeText === '') {\n updateData.alternativeText = aiMetadata.altText;\n }\n\n if (!file.caption || file.caption === '') {\n updateData.caption = aiMetadata.caption;\n }\n\n // Only update if there are fields to update\n if (Object.keys(updateData).length > 0) {\n await uploadService.updateFileInfo(file.id, updateData, { user });\n\n // Update in-memory file object (needed for upload flow response)\n if (updateData.alternativeText !== undefined) {\n file.alternativeText = updateData.alternativeText;\n }\n if (updateData.caption !== undefined) {\n file.caption = updateData.caption;\n }\n }\n }\n })\n );\n },\n\n /**\n * Process existing files with job tracking for progress updates\n */\n async processExistingFiles(jobId: number, user: { id: string | number }): Promise<void> {\n const jobService = getService('aiMetadataJobs');\n\n try {\n // Mark as processing\n await jobService.updateJob(jobId, { status: 'processing' });\n\n // Query all images without metadata\n const files: File[] = await strapi.db.query('plugin::upload.file').findMany({\n where: {\n mime: {\n $in: SUPPORTED_IMAGE_TYPES,\n },\n $or: [\n { alternativeText: { $null: true } },\n { alternativeText: '' },\n { caption: { $null: true } },\n { caption: '' },\n ],\n },\n });\n\n if (files.length === 0) {\n await jobService.updateJob(jobId, {\n status: 'completed',\n completedAt: new Date(),\n });\n return;\n }\n\n // Process all files at once\n const metadataResults = await this.processFiles(files);\n await this.updateFilesWithAIMetadata(files, metadataResults, user);\n\n // Mark as completed\n await jobService.updateJob(jobId, {\n status: 'completed',\n completedAt: new Date(),\n });\n } catch (error) {\n strapi.log.error('AI metadata job failed', {\n jobId,\n error: error instanceof Error ? error.message : String(error),\n });\n\n await jobService.updateJob(jobId, {\n status: 'failed',\n completedAt: new Date(),\n });\n }\n },\n\n /**\n * Generate AI metadata for an explicit selection of files, synchronously.\n *\n * Unlike `processExistingFiles`, this does not create a job and does not\n * filter on missing metadata — the selection is what the user asked for.\n * Existing alt text / captions are still preserved by\n * `updateFilesWithAIMetadata`, so a file that already has both is reported\n * as `success` without being modified.\n *\n * Images are processed in sequential chunks so one failing chunk only\n * affects its own files; every other chunk still runs.\n *\n * @experimental\n */\n async generateForFiles(\n fileIds: Array<number | string>,\n user: { id: string | number }\n ): Promise<AIMetadataFileResult[]> {\n // `yup.strapiID()` accepts both numbers and numeric strings without\n // coercing, so normalise here — otherwise a request sending `[\"1\"]`\n // would never match the numeric ids coming back from the database.\n const normalisedIds = fileIds.map(Number);\n const uniqueIds = [...new Set(normalisedIds)];\n\n const files: File[] = await strapi.db.query('plugin::upload.file').findMany({\n where: { id: { $in: uniqueIds } },\n });\n\n const filesById = new Map(files.map((file) => [file.id, file]));\n\n // Keyed by input id so the response order matches the request order.\n const resultsById = new Map<number, AIMetadataFileResult>();\n const images: File[] = [];\n\n uniqueIds.forEach((id) => {\n const file = filesById.get(id);\n\n if (!file) {\n resultsById.set(id, { id, status: 'error', error: 'File not found' });\n return;\n }\n\n // Only the formats the AI provider understands are sent; anything else\n // (non-images, but also exotic image formats like SVG or TIFF) is\n // reported as skipped rather than failed.\n if (!isSupportedImage(file)) {\n resultsById.set(id, { id, status: 'skipped' });\n return;\n }\n\n images.push(file);\n });\n\n for (const imageChunk of chunk(images, AI_METADATA_CHUNK_SIZE)) {\n try {\n const metadataResults = await this.processFiles(imageChunk);\n await this.updateFilesWithAIMetadata(imageChunk, metadataResults, user);\n\n imageChunk.forEach((file, index) => {\n resultsById.set(file.id, {\n id: file.id,\n status: metadataResults[index] ? 'success' : 'error',\n ...(metadataResults[index]\n ? {}\n : { error: 'AI metadata generation returned no result' }),\n });\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'AI metadata generation failed';\n\n strapi.log.error('AI metadata generation failed for a chunk of files', {\n fileIds: imageChunk.map((file) => file.id),\n message,\n });\n\n imageChunk.forEach((file) => {\n resultsById.set(file.id, { id: file.id, status: 'error', error: message });\n });\n }\n }\n\n return normalisedIds.map(\n (id) => resultsById.get(id) ?? { id, status: 'error', error: 'File not processed' }\n );\n },\n\n /**\n * Processes provided files for AI metadata generation\n */\n async processFiles(files: File[]): Promise<Array<{ altText: string; caption: string } | null>> {\n if (!(await this.isEnabled()) || !aiServerUrl) {\n throw new Error('AI Metadata service is not enabled');\n }\n\n // Filter for image files only and track their original positions\n // We need to maintain the original indices so we can map AI results back correctly\n const imageFiles = files\n .map((file, index) => ({ file, originalIndex: index }))\n .filter(({ file }) => file.mime?.startsWith('image/'));\n\n // Convert filtered image files to InputFile format (uses thumbnails when available)\n const imageInputFiles = imageFiles.map(({ file }) => {\n const thumbnail = (file.formats as any)?.thumbnail;\n return {\n filepath: thumbnail?.url || file.url || '',\n mimetype: file.mime,\n originalFilename: file.name,\n size: thumbnail?.size || file.size,\n provider: file.provider,\n } as InputFile;\n });\n const createEmptyMetadataResults = (): Array<{ altText: string; caption: string } | null> =>\n Array.from({ length: files.length }, () => null);\n\n // If no image files, return sparse array with all nulls to avoid calling the AI server\n // This maintains the same array length as input files for proper index alignment\n if (imageFiles.length === 0) {\n return createEmptyMetadataResults();\n }\n\n const formData = await buildFormDataFromFiles(\n imageInputFiles,\n strapi.config.get('server.absoluteUrl'),\n strapi.log\n );\n\n let token: string;\n try {\n const tokenData = await strapi.ai.admin.getAiToken();\n token = tokenData.token;\n } catch (error) {\n throw new Error('Failed to retrieve AI token', {\n cause: error instanceof Error ? error : undefined,\n });\n }\n\n strapi.log.http('Contacting AI Server for media metadata generation', {\n aiServerUrl,\n imageCount: imageFiles.length,\n });\n\n const res = await fetch(`${aiServerUrl}/media-library/generate-metadata`, {\n method: 'POST',\n body: formData,\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!res.ok) {\n const errorText = await res.text();\n throw Error(`AI metadata generation failed`, { cause: errorText });\n }\n\n const responseSchema = z.object({\n results: z.array(\n z.object({\n altText: z.string(),\n caption: z.string(),\n })\n ),\n });\n\n const { results } = responseSchema.parse(await res.json());\n strapi.log.http(`AI generated metadata successfully for ${results.length} files`);\n\n // Create sparse array with results at original indices\n // Example: files=[img1, pdf, img2] -> imageFiles=[{img1, index:0}, {img2, index:2}]\n // AI results=[meta1, meta2] -> sparse=[meta1, null, meta2]\n // This ensures metadata[i] corresponds to files[i], with null for non-images\n return imageFiles.reduce((sparseResults, { originalIndex }, resultIndex) => {\n sparseResults[originalIndex] = results[resultIndex];\n return sparseResults;\n }, createEmptyMetadataResults());\n },\n };\n};\n\nexport { createAIMetadataService };\n"],"names":["SUPPORTED_IMAGE_TYPES","AI_METADATA_SUPPORTED_IMAGE_TYPES","isSupportedImage","file","isAIMetadataSupportedMime","mime","chunk","items","size","chunks","i","length","push","slice","createAIMetadataService","strapi","aiServerUrl","process","env","STRAPI_AI_URL","isEnabled","ai","admin","isStrapiManagedAiEnabled","settings","plugin","service","getSettings","aiMetadata","countImagesWithoutMetadata","imagesWithoutMetadataCountPromise","db","query","count","where","$in","$or","alternativeText","$null","caption","totalImagesPromise","imagesWithoutMetadataCount","totalImages","Promise","all","updateFilesWithAIMetadata","files","metadataResults","user","uploadService","map","index","updateData","altText","Object","keys","updateFileInfo","id","undefined","processExistingFiles","jobId","jobService","getService","updateJob","status","findMany","completedAt","Date","processFiles","error","log","Error","message","String","generateForFiles","fileIds","normalisedIds","Number","uniqueIds","Set","filesById","Map","resultsById","images","forEach","get","set","imageChunk","AI_METADATA_CHUNK_SIZE","imageFiles","originalIndex","filter","startsWith","imageInputFiles","thumbnail","formats","filepath","url","mimetype","originalFilename","name","provider","createEmptyMetadataResults","Array","from","formData","buildFormDataFromFiles","config","token","tokenData","getAiToken","cause","http","imageCount","res","fetch","method","body","headers","Authorization","ok","errorText","text","responseSchema","z","object","results","array","string","parse","json","reduce","sparseResults","resultIndex"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-metadata.js","sources":["../../../server/src/services/ai-metadata.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\nimport { InputFile, File } from '../types';\nimport { Settings } from '../controllers/validation/admin/settings';\nimport { getService } from '../utils';\nimport { fetchImagesAsBlobs } from '../utils/images';\nimport { AI_METADATA_CHUNK_SIZE, AI_METADATA_SUPPORTED_IMAGE_TYPES } from '../constants';\n\nimport { isAIMetadataSupportedMime } from '../../../shared/constants';\n\nimport type { GenerateAIMetadata } from '../../../shared/contracts/files';\n\nexport type AIMetadataFileResult = GenerateAIMetadata.FileResult;\n\n/**\n * Supported image types for AI metadata generation. Lives in `shared/` so the\n * admin panel gates the bulk action on exactly what the provider accepts.\n */\nconst SUPPORTED_IMAGE_TYPES = AI_METADATA_SUPPORTED_IMAGE_TYPES;\n\nconst isSupportedImage = (file: File): boolean => isAIMetadataSupportedMime(file.mime);\n\nconst chunk = <T>(items: T[], size: number): T[][] => {\n const chunks: T[][] = [];\n\n for (let i = 0; i < items.length; i += size) {\n chunks.push(items.slice(i, i + size));\n }\n\n return chunks;\n};\n\nconst createAIMetadataService = ({ strapi }: { strapi: Core.Strapi }) => {\n return {\n async isEnabled() {\n if (!getService('aiMetadataProvider').hasProvider()) {\n return false;\n }\n const settings: Settings = await strapi.plugin('upload').service('upload').getSettings();\n return settings.aiMetadata ?? true;\n },\n\n async countImagesWithoutMetadata() {\n const imagesWithoutMetadataCountPromise = strapi.db.query('plugin::upload.file').count({\n where: {\n mime: {\n $in: SUPPORTED_IMAGE_TYPES,\n },\n $or: [\n { alternativeText: { $null: true } },\n { alternativeText: '' },\n { caption: { $null: true } },\n { caption: '' },\n ],\n },\n });\n\n const totalImagesPromise = strapi.db.query('plugin::upload.file').count({\n where: {\n mime: {\n $in: SUPPORTED_IMAGE_TYPES,\n },\n },\n });\n\n const [imagesWithoutMetadataCount, totalImages] = await Promise.all([\n imagesWithoutMetadataCountPromise,\n totalImagesPromise,\n ]);\n\n return { imagesWithoutMetadataCount, totalImages };\n },\n\n /**\n * Update files with AI-generated metadata\n * Shared logic used by both upload flow and retroactive processing\n */\n async updateFilesWithAIMetadata(\n files: File[],\n metadataResults: Array<{ altText: string; caption: string } | null>,\n user: { id: string | number }\n ) {\n const uploadService = strapi.plugin('upload').service('upload');\n\n await Promise.all(\n files.map(async (file, index) => {\n const aiMetadata = metadataResults[index];\n if (aiMetadata) {\n // Only update fields that are missing (null or empty string)\n const updateData: { alternativeText?: string; caption?: string } = {};\n\n if (!file.alternativeText || file.alternativeText === '') {\n updateData.alternativeText = aiMetadata.altText;\n }\n\n if (!file.caption || file.caption === '') {\n updateData.caption = aiMetadata.caption;\n }\n\n // Only update if there are fields to update\n if (Object.keys(updateData).length > 0) {\n await uploadService.updateFileInfo(file.id, updateData, { user });\n\n // Update in-memory file object (needed for upload flow response)\n if (updateData.alternativeText !== undefined) {\n file.alternativeText = updateData.alternativeText;\n }\n if (updateData.caption !== undefined) {\n file.caption = updateData.caption;\n }\n }\n }\n })\n );\n },\n\n /**\n * Process existing files with job tracking for progress updates\n */\n async processExistingFiles(jobId: number, user: { id: string | number }): Promise<void> {\n const jobService = getService('aiMetadataJobs');\n\n try {\n // Mark as processing\n await jobService.updateJob(jobId, { status: 'processing' });\n\n // Query all images without metadata\n const files: File[] = await strapi.db.query('plugin::upload.file').findMany({\n where: {\n mime: {\n $in: SUPPORTED_IMAGE_TYPES,\n },\n $or: [\n { alternativeText: { $null: true } },\n { alternativeText: '' },\n { caption: { $null: true } },\n { caption: '' },\n ],\n },\n });\n\n if (files.length === 0) {\n await jobService.updateJob(jobId, {\n status: 'completed',\n completedAt: new Date(),\n });\n return;\n }\n\n // Process all files at once\n const metadataResults = await this.processFiles(files);\n await this.updateFilesWithAIMetadata(files, metadataResults, user);\n\n // Mark as completed\n await jobService.updateJob(jobId, {\n status: 'completed',\n completedAt: new Date(),\n });\n } catch (error) {\n strapi.log.error('AI metadata job failed', {\n jobId,\n error: error instanceof Error ? error.message : String(error),\n });\n\n await jobService.updateJob(jobId, {\n status: 'failed',\n completedAt: new Date(),\n });\n }\n },\n\n /**\n * Generate AI metadata for an explicit selection of files, synchronously.\n *\n * Unlike `processExistingFiles`, this does not create a job and does not\n * filter on missing metadata — the selection is what the user asked for.\n * Existing alt text / captions are still preserved by\n * `updateFilesWithAIMetadata`, so a file that already has both is reported\n * as `success` without being modified.\n *\n * Images are processed in sequential chunks so one failing chunk only\n * affects its own files; every other chunk still runs.\n *\n * @experimental\n */\n async generateForFiles(\n fileIds: Array<number | string>,\n user: { id: string | number }\n ): Promise<AIMetadataFileResult[]> {\n // `yup.strapiID()` accepts both numbers and numeric strings without\n // coercing, so normalise here — otherwise a request sending `[\"1\"]`\n // would never match the numeric ids coming back from the database.\n const normalisedIds = fileIds.map(Number);\n const uniqueIds = [...new Set(normalisedIds)];\n\n const files: File[] = await strapi.db.query('plugin::upload.file').findMany({\n where: { id: { $in: uniqueIds } },\n });\n\n const filesById = new Map(files.map((file) => [file.id, file]));\n\n // Keyed by input id so the response order matches the request order.\n const resultsById = new Map<number, AIMetadataFileResult>();\n const images: File[] = [];\n\n uniqueIds.forEach((id) => {\n const file = filesById.get(id);\n\n if (!file) {\n resultsById.set(id, { id, status: 'error', error: 'File not found' });\n return;\n }\n\n // Only the formats the AI provider understands are sent; anything else\n // (non-images, but also exotic image formats like SVG or TIFF) is\n // reported as skipped rather than failed.\n if (!isSupportedImage(file)) {\n resultsById.set(id, { id, status: 'skipped' });\n return;\n }\n\n images.push(file);\n });\n\n for (const imageChunk of chunk(images, AI_METADATA_CHUNK_SIZE)) {\n try {\n const metadataResults = await this.processFiles(imageChunk);\n await this.updateFilesWithAIMetadata(imageChunk, metadataResults, user);\n\n imageChunk.forEach((file, index) => {\n resultsById.set(file.id, {\n id: file.id,\n status: metadataResults[index] ? 'success' : 'error',\n ...(metadataResults[index]\n ? {}\n : { error: 'AI metadata generation returned no result' }),\n });\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'AI metadata generation failed';\n\n strapi.log.error('AI metadata generation failed for a chunk of files', {\n fileIds: imageChunk.map((file) => file.id),\n message,\n });\n\n imageChunk.forEach((file) => {\n resultsById.set(file.id, { id: file.id, status: 'error', error: message });\n });\n }\n }\n\n return normalisedIds.map(\n (id) => resultsById.get(id) ?? { id, status: 'error', error: 'File not processed' }\n );\n },\n\n /**\n * Processes provided files for AI metadata generation\n */\n async processFiles(files: File[]): Promise<Array<{ altText: string; caption: string } | null>> {\n if (!(await this.isEnabled())) {\n throw new Error('AI Metadata service is not enabled');\n }\n\n // Filter for image files only and track their original positions\n // We need to maintain the original indices so we can map AI results back correctly\n const imageFiles = files\n .map((file, index) => ({ file, originalIndex: index }))\n .filter(({ file }) => file.mime?.startsWith('image/'));\n\n // Convert filtered image files to InputFile format (uses thumbnails when available)\n const imageInputFiles = imageFiles.map(({ file }) => {\n const thumbnail = (file.formats as any)?.thumbnail;\n return {\n filepath: thumbnail?.url || file.url || '',\n mimetype: file.mime,\n originalFilename: file.name,\n size: thumbnail?.size || file.size,\n provider: file.provider,\n } as InputFile;\n });\n const createEmptyMetadataResults = (): Array<{ altText: string; caption: string } | null> =>\n Array.from({ length: files.length }, () => null);\n\n // If no image files, return sparse array with all nulls to avoid calling the AI server\n // This maintains the same array length as input files for proper index alignment\n if (imageFiles.length === 0) {\n return createEmptyMetadataResults();\n }\n\n const images = await fetchImagesAsBlobs(\n imageInputFiles,\n strapi.config.get('server.absoluteUrl'),\n strapi.log\n );\n\n const { results } = await getService('aiMetadataProvider').generateMetadata({ images });\n\n // Create sparse array with results at original indices\n // Example: files=[img1, pdf, img2] -> imageFiles=[{img1, index:0}, {img2, index:2}]\n // AI results=[meta1, meta2] -> sparse=[meta1, null, meta2]\n // This ensures metadata[i] corresponds to files[i], with null for non-images\n return imageFiles.reduce((sparseResults, { originalIndex }, resultIndex) => {\n sparseResults[originalIndex] = results[resultIndex];\n return sparseResults;\n }, createEmptyMetadataResults());\n },\n };\n};\n\nexport { createAIMetadataService };\n"],"names":["SUPPORTED_IMAGE_TYPES","AI_METADATA_SUPPORTED_IMAGE_TYPES","isSupportedImage","file","isAIMetadataSupportedMime","mime","chunk","items","size","chunks","i","length","push","slice","createAIMetadataService","strapi","isEnabled","getService","hasProvider","settings","plugin","service","getSettings","aiMetadata","countImagesWithoutMetadata","imagesWithoutMetadataCountPromise","db","query","count","where","$in","$or","alternativeText","$null","caption","totalImagesPromise","imagesWithoutMetadataCount","totalImages","Promise","all","updateFilesWithAIMetadata","files","metadataResults","user","uploadService","map","index","updateData","altText","Object","keys","updateFileInfo","id","undefined","processExistingFiles","jobId","jobService","updateJob","status","findMany","completedAt","Date","processFiles","error","log","Error","message","String","generateForFiles","fileIds","normalisedIds","Number","uniqueIds","Set","filesById","Map","resultsById","images","forEach","get","set","imageChunk","AI_METADATA_CHUNK_SIZE","imageFiles","originalIndex","filter","startsWith","imageInputFiles","thumbnail","formats","filepath","url","mimetype","originalFilename","name","provider","createEmptyMetadataResults","Array","from","fetchImagesAsBlobs","config","results","generateMetadata","reduce","sparseResults","resultIndex"],"mappings":"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"}
|