@webiny/api-file-manager-s3 6.4.11 → 6.6.0-alpha.1

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 (52) hide show
  1. package/FileManagerS3Feature.d.ts +9 -0
  2. package/FileManagerS3Feature.js +27 -0
  3. package/FileManagerS3Feature.js.map +1 -0
  4. package/assetDelivery/abstractions.d.ts +2 -0
  5. package/assetDelivery/abstractions.js.map +1 -1
  6. package/assetDelivery/assetDeliveryConfig.d.ts +1 -1
  7. package/assetDelivery/assetDeliveryConfig.js +0 -2
  8. package/assetDelivery/assetDeliveryConfig.js.map +1 -1
  9. package/assetDelivery/feature.js +3 -2
  10. package/assetDelivery/feature.js.map +1 -1
  11. package/assetDelivery/s3/LazySharpTransform.d.ts +34 -0
  12. package/assetDelivery/s3/LazySharpTransform.js +34 -0
  13. package/assetDelivery/s3/LazySharpTransform.js.map +1 -0
  14. package/assetDelivery/s3/SharpTransform.d.ts +15 -7
  15. package/assetDelivery/s3/SharpTransform.js +112 -101
  16. package/assetDelivery/s3/SharpTransform.js.map +1 -1
  17. package/assetDelivery/threatDetection/createThreatDetectionEventHandler.d.ts +21 -1
  18. package/assetDelivery/threatDetection/createThreatDetectionEventHandler.js +43 -27
  19. package/assetDelivery/threatDetection/createThreatDetectionEventHandler.js.map +1 -1
  20. package/assetDelivery/threatDetection/processThreatScanResult.js +9 -7
  21. package/assetDelivery/threatDetection/processThreatScanResult.js.map +1 -1
  22. package/assetDelivery/types.d.ts +3 -0
  23. package/enterprise/ApplyThreatScanning/CreateFileWithThreatScanDecorator.d.ts +3 -1
  24. package/enterprise/ApplyThreatScanning/CreateFileWithThreatScanDecorator.js +7 -2
  25. package/enterprise/ApplyThreatScanning/CreateFileWithThreatScanDecorator.js.map +1 -1
  26. package/features/DeleteFileFromBucket/DeleteS3FolderTask.d.ts +1 -1
  27. package/features/ExtractMetadata/ExtractMetadataTask.d.ts +1 -1
  28. package/features/FlushCache/FlushCacheOnFileDeleteHandler.js +1 -1
  29. package/features/FlushCache/FlushCacheOnFileDeleteHandler.js.map +1 -1
  30. package/features/FlushCache/FlushCacheOnFileUpdateHandler.js +1 -1
  31. package/features/FlushCache/FlushCacheOnFileUpdateHandler.js.map +1 -1
  32. package/features/FlushCache/InvalidateCacheTask.d.ts +2 -2
  33. package/features/FlushCache/InvalidateCacheTask.js +2 -2
  34. package/features/FlushCache/InvalidateCacheTask.js.map +1 -1
  35. package/features/WriteFileMetadata/MetadataWriter.js +3 -1
  36. package/features/WriteFileMetadata/MetadataWriter.js.map +1 -1
  37. package/graphql/S3GraphQLSchema.d.ts +9 -0
  38. package/graphql/S3GraphQLSchema.js +229 -0
  39. package/graphql/S3GraphQLSchema.js.map +1 -0
  40. package/index.d.ts +2 -2
  41. package/index.js +1 -29
  42. package/package.json +21 -19
  43. package/utils/FileUploadModifier.d.ts +8 -2
  44. package/utils/FileUploadModifier.js +5 -3
  45. package/utils/FileUploadModifier.js.map +1 -1
  46. package/utils/createFileNormalizerFromContext.js +2 -2
  47. package/utils/createFileNormalizerFromContext.js.map +1 -1
  48. package/utils/uploadFileToS3.d.ts +1 -1
  49. package/graphql/schema.d.ts +0 -1
  50. package/graphql/schema.js +0 -203
  51. package/graphql/schema.js.map +0 -1
  52. package/index.js.map +0 -1
@@ -1,37 +1,53 @@
1
- import { createEventBridgeEventHandler } from "@webiny/handler-aws";
2
- import { createHandlerOnRequest } from "@webiny/handler";
1
+ import { EventBridgeEventHandler } from "@webiny/event-handler-aws/abstractions/handlers/EventBridgeEventHandler.js";
2
+ import { RequestContainer } from "@webiny/event-handler-core";
3
+ import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
4
+ import { GetTenantByIdUseCase } from "@webiny/api-core/features/tenancy/GetTenantById/index.js";
5
+ import { FeatureFlags } from "@webiny/api-core/features/featureFlags/abstractions.js";
3
6
  import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
4
7
  import { processThreatScanResult } from "./processThreatScanResult.js";
5
8
  import { ObjectKey } from "./ObjectKey.js";
6
- const detailType = "GuardDuty Malware Protection Object Scan Result";
7
- const createThreatDetectionEventHandler = ()=>{
8
- const handlerOnRequest = createHandlerOnRequest(async (request, _, context)=>{
9
- const payload = request.body;
10
- if (payload["detail-type"] !== detailType) return;
9
+ const DETAIL_TYPE = "GuardDuty Malware Protection Object Scan Result";
10
+ class ThreatDetectionEventBridgeLambdaHandlerImpl {
11
+ constructor(container, tenantCtx, getTenantById){
12
+ this.container = container;
13
+ this.tenantCtx = tenantCtx;
14
+ this.getTenantById = getTenantById;
15
+ }
16
+ async execute(eventCtx, _next) {
17
+ const payload = eventCtx.event;
18
+ if (payload["detail-type"] !== DETAIL_TYPE) return {
19
+ success: true
20
+ };
21
+ if (!this.container.resolve(FeatureFlags).get().isEnabled("fileManager.threatDetection")) return {
22
+ success: true
23
+ };
11
24
  const objectKey = payload.detail.s3ObjectDetails.objectKey;
12
- const keyValueStore = context.container.resolve(GlobalKeyValueStore);
25
+ const keyValueStore = this.container.resolve(GlobalKeyValueStore);
13
26
  try {
14
27
  const fileId = ObjectKey.from(objectKey).id();
15
28
  const result = await keyValueStore.get(`FileManager/File/${fileId}/Metadata`);
16
- if (result.isFail()) return;
17
- request.headers = {
18
- ...request.headers,
19
- "x-tenant": result.value.tenant
20
- };
29
+ if (result.isOk()) {
30
+ const tenantResult = await this.getTenantById.execute(result.value.tenant);
31
+ if (tenantResult.isOk()) this.tenantCtx.setTenant(tenantResult.value);
32
+ }
21
33
  } catch {}
22
- });
23
- const threatScanEventHandler = createEventBridgeEventHandler(async ({ payload, next, ...rest })=>{
24
- const context = rest.context;
25
- const threatDetectionEnabled = context.wcp.canUseFileManagerThreatDetection();
26
- if (!threatDetectionEnabled || payload["detail-type"] !== detailType) return next();
27
- await processThreatScanResult(context, payload.detail);
28
- });
29
- threatScanEventHandler.name = threatScanEventHandler.type + ".threatDetectionEventHandler";
30
- return [
31
- handlerOnRequest,
32
- threatScanEventHandler
33
- ];
34
- };
35
- export { createThreatDetectionEventHandler };
34
+ await processThreatScanResult({
35
+ container: this.container
36
+ }, payload.detail);
37
+ return {
38
+ success: true
39
+ };
40
+ }
41
+ }
42
+ const ThreatDetectionEventBridgeLambdaHandler = EventBridgeEventHandler.createImplementation({
43
+ implementation: ThreatDetectionEventBridgeLambdaHandlerImpl,
44
+ dependencies: [
45
+ RequestContainer,
46
+ TenantContext,
47
+ GetTenantByIdUseCase
48
+ ]
49
+ });
50
+ const createThreatDetectionEventHandler = ()=>[];
51
+ export { ThreatDetectionEventBridgeLambdaHandler, createThreatDetectionEventHandler };
36
52
 
37
53
  //# sourceMappingURL=createThreatDetectionEventHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assetDelivery/threatDetection/createThreatDetectionEventHandler.js","sources":["../../../src/assetDelivery/threatDetection/createThreatDetectionEventHandler.ts"],"sourcesContent":["import { createEventBridgeEventHandler } from \"@webiny/handler-aws\";\nimport { createHandlerOnRequest } from \"@webiny/handler\";\nimport type { EventBridgeEvent } from \"@webiny/aws-sdk/types/index.js\";\nimport type { ApiCoreContext } from \"@webiny/api-core/types/core.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport type { GuardDutyEvent } from \"./types.js\";\nimport { processThreatScanResult } from \"./processThreatScanResult.js\";\nimport { ObjectKey } from \"./ObjectKey.js\";\n\nconst detailType = \"GuardDuty Malware Protection Object Scan Result\";\n\nexport const createThreatDetectionEventHandler = () => {\n const handlerOnRequest = createHandlerOnRequest(async (request, _, context) => {\n const payload = request.body as EventBridgeEvent<string, GuardDutyEvent>;\n\n if (payload[\"detail-type\"] !== detailType) {\n return;\n }\n\n const objectKey = payload.detail.s3ObjectDetails.objectKey;\n const keyValueStore = context.container.resolve(GlobalKeyValueStore);\n\n try {\n // Extract file id from the absolute S3 object key\n const fileId = ObjectKey.from(objectKey).id();\n const result = await keyValueStore.get<{ tenant: string }>(\n `FileManager/File/${fileId}/Metadata`\n );\n\n if (result.isFail()) {\n return;\n }\n\n request.headers = {\n ...request.headers,\n \"x-tenant\": result.value.tenant\n };\n } catch {\n // If metadata can't be loaded, we ignore the file.\n // Most likely it's because the file is a rendition of the original file,\n // so we don't need to do anything with it.\n }\n });\n // Guard Duty event handler.\n const threatScanEventHandler = createEventBridgeEventHandler<typeof detailType, GuardDutyEvent>(\n async ({ payload, next, ...rest }) => {\n const context = rest.context as ApiCoreContext;\n\n const threatDetectionEnabled = context.wcp.canUseFileManagerThreatDetection();\n\n if (!threatDetectionEnabled || payload[\"detail-type\"] !== detailType) {\n return next();\n }\n\n await processThreatScanResult(context, payload.detail);\n }\n );\n\n // Assign a human-readable name for easier debugging.\n threatScanEventHandler.name = threatScanEventHandler.type + \".threatDetectionEventHandler\";\n\n return [handlerOnRequest, threatScanEventHandler];\n};\n"],"names":["detailType","createThreatDetectionEventHandler","handlerOnRequest","createHandlerOnRequest","request","_","context","payload","objectKey","keyValueStore","GlobalKeyValueStore","fileId","ObjectKey","result","threatScanEventHandler","createEventBridgeEventHandler","next","rest","threatDetectionEnabled","processThreatScanResult"],"mappings":";;;;;AASA,MAAMA,aAAa;AAEZ,MAAMC,oCAAoC;IAC7C,MAAMC,mBAAmBC,uBAAuB,OAAOC,SAASC,GAAGC;QAC/D,MAAMC,UAAUH,QAAQ,IAAI;QAE5B,IAAIG,OAAO,CAAC,cAAc,KAAKP,YAC3B;QAGJ,MAAMQ,YAAYD,QAAQ,MAAM,CAAC,eAAe,CAAC,SAAS;QAC1D,MAAME,gBAAgBH,QAAQ,SAAS,CAAC,OAAO,CAACI;QAEhD,IAAI;YAEA,MAAMC,SAASC,UAAU,IAAI,CAACJ,WAAW,EAAE;YAC3C,MAAMK,SAAS,MAAMJ,cAAc,GAAG,CAClC,CAAC,iBAAiB,EAAEE,OAAO,SAAS,CAAC;YAGzC,IAAIE,OAAO,MAAM,IACb;YAGJT,QAAQ,OAAO,GAAG;gBACd,GAAGA,QAAQ,OAAO;gBAClB,YAAYS,OAAO,KAAK,CAAC,MAAM;YACnC;QACJ,EAAE,OAAM,CAIR;IACJ;IAEA,MAAMC,yBAAyBC,8BAC3B,OAAO,EAAER,OAAO,EAAES,IAAI,EAAE,GAAGC,MAAM;QAC7B,MAAMX,UAAUW,KAAK,OAAO;QAE5B,MAAMC,yBAAyBZ,QAAQ,GAAG,CAAC,gCAAgC;QAE3E,IAAI,CAACY,0BAA0BX,OAAO,CAAC,cAAc,KAAKP,YACtD,OAAOgB;QAGX,MAAMG,wBAAwBb,SAASC,QAAQ,MAAM;IACzD;IAIJO,uBAAuB,IAAI,GAAGA,uBAAuB,IAAI,GAAG;IAE5D,OAAO;QAACZ;QAAkBY;KAAuB;AACrD"}
1
+ {"version":3,"file":"assetDelivery/threatDetection/createThreatDetectionEventHandler.js","sources":["../../../src/assetDelivery/threatDetection/createThreatDetectionEventHandler.ts"],"sourcesContent":["import type { Container } from \"@webiny/feature/api\";\nimport type { EventBridgeEvent } from \"@webiny/aws-sdk/types/index.js\";\nimport {\n EventBridgeEventHandler,\n type EventBridgeResult\n} from \"@webiny/event-handler-aws/abstractions/handlers/EventBridgeEventHandler.js\";\nimport { RequestContainer } from \"@webiny/event-handler-core\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { GetTenantByIdUseCase } from \"@webiny/api-core/features/tenancy/GetTenantById/index.js\";\nimport type { ITenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/abstractions.js\";\nimport type { IGetTenantByIdUseCase } from \"@webiny/api-core/features/tenancy/GetTenantById/abstractions.js\";\nimport type { EventContext, NextFunction } from \"@webiny/event-handler-core\";\nimport type { ApiCoreContext } from \"@webiny/api-core/types/core.js\";\nimport { FeatureFlags } from \"@webiny/api-core/features/featureFlags/abstractions.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { processThreatScanResult } from \"./processThreatScanResult.js\";\nimport { ObjectKey } from \"./ObjectKey.js\";\nimport type { GuardDutyEvent } from \"./types.js\";\n\nconst DETAIL_TYPE = \"GuardDuty Malware Protection Object Scan Result\";\n\nclass ThreatDetectionEventBridgeLambdaHandlerImpl implements EventBridgeEventHandler.Interface {\n constructor(\n private container: Container,\n private tenantCtx: ITenantContext,\n private getTenantById: IGetTenantByIdUseCase\n ) {}\n\n async execute(\n eventCtx: EventContext<EventBridgeEvent<string, GuardDutyEvent>>,\n _next: NextFunction\n ): Promise<EventBridgeResult> {\n const payload = eventCtx.event;\n if (payload[\"detail-type\"] !== DETAIL_TYPE) {\n return { success: true };\n }\n\n if (!this.container.resolve(FeatureFlags).get().isEnabled(\"fileManager.threatDetection\")) {\n return { success: true };\n }\n\n const objectKey = payload.detail.s3ObjectDetails.objectKey;\n const keyValueStore = this.container.resolve(GlobalKeyValueStore);\n\n try {\n const fileId = ObjectKey.from(objectKey).id();\n const result = await keyValueStore.get<{ tenant: string }>(\n `FileManager/File/${fileId}/Metadata`\n );\n\n if (result.isOk()) {\n const tenantResult = await this.getTenantById.execute(result.value.tenant);\n if (tenantResult.isOk()) {\n this.tenantCtx.setTenant(tenantResult.value);\n }\n }\n } catch {\n // If metadata can't be loaded, ignore — likely a rendition file.\n }\n\n await processThreatScanResult(\n { container: this.container } as unknown as ApiCoreContext,\n payload.detail\n );\n return { success: true };\n }\n}\n\nexport const ThreatDetectionEventBridgeLambdaHandler = EventBridgeEventHandler.createImplementation(\n {\n implementation: ThreatDetectionEventBridgeLambdaHandlerImpl,\n dependencies: [RequestContainer, TenantContext, GetTenantByIdUseCase]\n }\n);\n\n/**\n * @deprecated Use ThreatDetectionEventBridgeLambdaHandler instead.\n */\nexport const createThreatDetectionEventHandler = () => {\n return [];\n};\n"],"names":["DETAIL_TYPE","ThreatDetectionEventBridgeLambdaHandlerImpl","container","tenantCtx","getTenantById","eventCtx","_next","payload","FeatureFlags","objectKey","keyValueStore","GlobalKeyValueStore","fileId","ObjectKey","result","tenantResult","processThreatScanResult","ThreatDetectionEventBridgeLambdaHandler","EventBridgeEventHandler","RequestContainer","TenantContext","GetTenantByIdUseCase","createThreatDetectionEventHandler"],"mappings":";;;;;;;;AAmBA,MAAMA,cAAc;AAEpB,MAAMC;IACF,YACYC,SAAoB,EACpBC,SAAyB,EACzBC,aAAoC,CAC9C;aAHUF,SAAS,GAATA;aACAC,SAAS,GAATA;aACAC,aAAa,GAAbA;IACT;IAEH,MAAM,QACFC,QAAgE,EAChEC,KAAmB,EACO;QAC1B,MAAMC,UAAUF,SAAS,KAAK;QAC9B,IAAIE,OAAO,CAAC,cAAc,KAAKP,aAC3B,OAAO;YAAE,SAAS;QAAK;QAG3B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAACQ,cAAc,GAAG,GAAG,SAAS,CAAC,gCACtD,OAAO;YAAE,SAAS;QAAK;QAG3B,MAAMC,YAAYF,QAAQ,MAAM,CAAC,eAAe,CAAC,SAAS;QAC1D,MAAMG,gBAAgB,IAAI,CAAC,SAAS,CAAC,OAAO,CAACC;QAE7C,IAAI;YACA,MAAMC,SAASC,UAAU,IAAI,CAACJ,WAAW,EAAE;YAC3C,MAAMK,SAAS,MAAMJ,cAAc,GAAG,CAClC,CAAC,iBAAiB,EAAEE,OAAO,SAAS,CAAC;YAGzC,IAAIE,OAAO,IAAI,IAAI;gBACf,MAAMC,eAAe,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAACD,OAAO,KAAK,CAAC,MAAM;gBACzE,IAAIC,aAAa,IAAI,IACjB,IAAI,CAAC,SAAS,CAAC,SAAS,CAACA,aAAa,KAAK;YAEnD;QACJ,EAAE,OAAM,CAER;QAEA,MAAMC,wBACF;YAAE,WAAW,IAAI,CAAC,SAAS;QAAC,GAC5BT,QAAQ,MAAM;QAElB,OAAO;YAAE,SAAS;QAAK;IAC3B;AACJ;AAEO,MAAMU,0CAA0CC,wBAAwB,oBAAoB,CAC/F;IACI,gBAAgBjB;IAChB,cAAc;QAACkB;QAAkBC;QAAeC;KAAqB;AACzE;AAMG,MAAMC,oCAAoC,IACtC,EAAE"}
@@ -1,14 +1,16 @@
1
1
  import { UpdateFileUseCase } from "@webiny/api-file-manager/features/file/UpdateFile/index.js";
2
2
  import { DeleteFileUseCase } from "@webiny/api-file-manager/features/file/DeleteFile/index.js";
3
- import { WebsocketService } from "@webiny/api-websockets/features/WebsocketService/index.js";
3
+ import { WebsocketsListConnectionsUseCase, WebsocketsSendToConnectionsUseCase } from "@webiny/api-websockets/exports/api.js";
4
4
  import { ObjectKey } from "./ObjectKey.js";
5
5
  import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
6
+ import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/abstractions.js";
6
7
  const processThreatScanResult = async (context, eventDetail)=>{
7
- const websocketService = context.container.resolve(WebsocketService);
8
+ const listConnections = context.container.resolve(WebsocketsListConnectionsUseCase);
9
+ const sendToConnections = context.container.resolve(WebsocketsSendToConnectionsUseCase);
8
10
  const getFile = context.container.resolve(GetFileUseCase);
9
11
  const updateFile = context.container.resolve(UpdateFileUseCase);
10
12
  const deleteFile = context.container.resolve(DeleteFileUseCase);
11
- await context.security.withoutAuthorization(async ()=>{
13
+ await context.container.resolve(IdentityContext).withoutAuthorization(async ()=>{
12
14
  const scanStatus = eventDetail.scanResultDetails.scanResultStatus;
13
15
  const s3Object = eventDetail.s3ObjectDetails;
14
16
  const fileId = ObjectKey.from(s3Object.objectKey).id();
@@ -16,7 +18,7 @@ const processThreatScanResult = async (context, eventDetail)=>{
16
18
  if (fileResult.isFail()) return;
17
19
  const file = fileResult.value;
18
20
  let allConnections = [];
19
- const connectionsResult = await websocketService.listConnections();
21
+ const connectionsResult = await listConnections.execute();
20
22
  if (connectionsResult.isOk()) allConnections = connectionsResult.value;
21
23
  if ("NO_THREATS_FOUND" === scanStatus) {
22
24
  const newTags = file.tags.filter((tag)=>"threatScanInProgress" !== tag);
@@ -24,7 +26,7 @@ const processThreatScanResult = async (context, eventDetail)=>{
24
26
  id: file.id,
25
27
  tags: newTags
26
28
  });
27
- await websocketService.sendToConnections(allConnections, {
29
+ await sendToConnections.execute(allConnections, {
28
30
  action: "fm.threatScan.noThreatFound",
29
31
  data: {
30
32
  id: file.id,
@@ -35,7 +37,7 @@ const processThreatScanResult = async (context, eventDetail)=>{
35
37
  }
36
38
  if ("THREATS_FOUND" === scanStatus) {
37
39
  await deleteFile.execute(file.id);
38
- await websocketService.sendToConnections(allConnections, {
40
+ await sendToConnections.execute(allConnections, {
39
41
  action: "fm.threatScan.threatDetected",
40
42
  data: {
41
43
  id: file.id,
@@ -45,7 +47,7 @@ const processThreatScanResult = async (context, eventDetail)=>{
45
47
  return;
46
48
  }
47
49
  await deleteFile.execute(file.id);
48
- await websocketService.sendToConnections(allConnections, {
50
+ await sendToConnections.execute(allConnections, {
49
51
  action: "fm.threatScan.unsupported",
50
52
  data: {
51
53
  id: file.id,
@@ -1 +1 @@
1
- {"version":3,"file":"assetDelivery/threatDetection/processThreatScanResult.js","sources":["../../../src/assetDelivery/threatDetection/processThreatScanResult.ts"],"sourcesContent":["import type { ApiCoreContext } from \"@webiny/api-core/types/core.js\";\nimport { UpdateFileUseCase } from \"@webiny/api-file-manager/features/file/UpdateFile/index.js\";\nimport { DeleteFileUseCase } from \"@webiny/api-file-manager/features/file/DeleteFile/index.js\";\nimport { WebsocketService } from \"@webiny/api-websockets/features/WebsocketService/index.js\";\nimport type { GuardDutyEvent } from \"./types.js\";\nimport { ObjectKey } from \"./ObjectKey.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\n\nexport const processThreatScanResult = async (\n context: ApiCoreContext,\n eventDetail: GuardDutyEvent\n) => {\n const websocketService = context.container.resolve(WebsocketService);\n const getFile = context.container.resolve(GetFileUseCase);\n const updateFile = context.container.resolve(UpdateFileUseCase);\n const deleteFile = context.container.resolve(DeleteFileUseCase);\n\n await context.security.withoutAuthorization(async () => {\n const scanStatus = eventDetail.scanResultDetails.scanResultStatus;\n const s3Object = eventDetail.s3ObjectDetails;\n\n const fileId = ObjectKey.from(s3Object.objectKey).id();\n const fileResult = await getFile.execute(fileId);\n\n if (fileResult.isFail()) {\n return;\n }\n\n const file = fileResult.value;\n\n let allConnections: WebsocketService.Connection[] = [];\n const connectionsResult = await websocketService.listConnections();\n if (connectionsResult.isOk()) {\n allConnections = connectionsResult.value;\n }\n\n if (scanStatus === \"NO_THREATS_FOUND\") {\n const newTags = file.tags.filter(tag => tag !== \"threatScanInProgress\");\n await updateFile.execute({\n id: file.id,\n tags: newTags\n });\n\n await websocketService.sendToConnections(allConnections, {\n action: \"fm.threatScan.noThreatFound\",\n data: {\n id: file.id,\n tags: newTags\n }\n });\n\n return;\n }\n\n if (scanStatus === \"THREATS_FOUND\") {\n // Delete the infected file.\n await deleteFile.execute(file.id);\n\n await websocketService.sendToConnections(allConnections, {\n action: \"fm.threatScan.threatDetected\",\n data: {\n id: file.id,\n name: file.name\n }\n });\n\n return;\n }\n\n // For all other outcomes, we delete the file, until better logic is implemented.\n await deleteFile.execute(file.id);\n\n await websocketService.sendToConnections(allConnections, {\n action: \"fm.threatScan.unsupported\",\n data: {\n id: file.id,\n name: file.name\n }\n });\n });\n};\n"],"names":["processThreatScanResult","context","eventDetail","websocketService","WebsocketService","getFile","GetFileUseCase","updateFile","UpdateFileUseCase","deleteFile","DeleteFileUseCase","scanStatus","s3Object","fileId","ObjectKey","fileResult","file","allConnections","connectionsResult","newTags","tag"],"mappings":";;;;;AAQO,MAAMA,0BAA0B,OACnCC,SACAC;IAEA,MAAMC,mBAAmBF,QAAQ,SAAS,CAAC,OAAO,CAACG;IACnD,MAAMC,UAAUJ,QAAQ,SAAS,CAAC,OAAO,CAACK;IAC1C,MAAMC,aAAaN,QAAQ,SAAS,CAAC,OAAO,CAACO;IAC7C,MAAMC,aAAaR,QAAQ,SAAS,CAAC,OAAO,CAACS;IAE7C,MAAMT,QAAQ,QAAQ,CAAC,oBAAoB,CAAC;QACxC,MAAMU,aAAaT,YAAY,iBAAiB,CAAC,gBAAgB;QACjE,MAAMU,WAAWV,YAAY,eAAe;QAE5C,MAAMW,SAASC,UAAU,IAAI,CAACF,SAAS,SAAS,EAAE,EAAE;QACpD,MAAMG,aAAa,MAAMV,QAAQ,OAAO,CAACQ;QAEzC,IAAIE,WAAW,MAAM,IACjB;QAGJ,MAAMC,OAAOD,WAAW,KAAK;QAE7B,IAAIE,iBAAgD,EAAE;QACtD,MAAMC,oBAAoB,MAAMf,iBAAiB,eAAe;QAChE,IAAIe,kBAAkB,IAAI,IACtBD,iBAAiBC,kBAAkB,KAAK;QAG5C,IAAIP,AAAe,uBAAfA,YAAmC;YACnC,MAAMQ,UAAUH,KAAK,IAAI,CAAC,MAAM,CAACI,CAAAA,MAAOA,AAAQ,2BAARA;YACxC,MAAMb,WAAW,OAAO,CAAC;gBACrB,IAAIS,KAAK,EAAE;gBACX,MAAMG;YACV;YAEA,MAAMhB,iBAAiB,iBAAiB,CAACc,gBAAgB;gBACrD,QAAQ;gBACR,MAAM;oBACF,IAAID,KAAK,EAAE;oBACX,MAAMG;gBACV;YACJ;YAEA;QACJ;QAEA,IAAIR,AAAe,oBAAfA,YAAgC;YAEhC,MAAMF,WAAW,OAAO,CAACO,KAAK,EAAE;YAEhC,MAAMb,iBAAiB,iBAAiB,CAACc,gBAAgB;gBACrD,QAAQ;gBACR,MAAM;oBACF,IAAID,KAAK,EAAE;oBACX,MAAMA,KAAK,IAAI;gBACnB;YACJ;YAEA;QACJ;QAGA,MAAMP,WAAW,OAAO,CAACO,KAAK,EAAE;QAEhC,MAAMb,iBAAiB,iBAAiB,CAACc,gBAAgB;YACrD,QAAQ;YACR,MAAM;gBACF,IAAID,KAAK,EAAE;gBACX,MAAMA,KAAK,IAAI;YACnB;QACJ;IACJ;AACJ"}
1
+ {"version":3,"file":"assetDelivery/threatDetection/processThreatScanResult.js","sources":["../../../src/assetDelivery/threatDetection/processThreatScanResult.ts"],"sourcesContent":["import type { ApiCoreContext } from \"@webiny/api-core/types/core.js\";\nimport { UpdateFileUseCase } from \"@webiny/api-file-manager/features/file/UpdateFile/index.js\";\nimport { DeleteFileUseCase } from \"@webiny/api-file-manager/features/file/DeleteFile/index.js\";\nimport {\n WebsocketsListConnectionsUseCase,\n WebsocketsSendToConnectionsUseCase,\n ConnectionRegistry\n} from \"@webiny/api-websockets/exports/api.js\";\nimport type { GuardDutyEvent } from \"./types.js\";\nimport { ObjectKey } from \"./ObjectKey.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport { IdentityContext } from \"@webiny/api-core/features/security/IdentityContext/abstractions.js\";\n\nexport const processThreatScanResult = async (\n context: ApiCoreContext,\n eventDetail: GuardDutyEvent\n) => {\n const listConnections = context.container.resolve(WebsocketsListConnectionsUseCase);\n const sendToConnections = context.container.resolve(WebsocketsSendToConnectionsUseCase);\n const getFile = context.container.resolve(GetFileUseCase);\n const updateFile = context.container.resolve(UpdateFileUseCase);\n const deleteFile = context.container.resolve(DeleteFileUseCase);\n\n await context.container.resolve(IdentityContext).withoutAuthorization(async () => {\n const scanStatus = eventDetail.scanResultDetails.scanResultStatus;\n const s3Object = eventDetail.s3ObjectDetails;\n\n const fileId = ObjectKey.from(s3Object.objectKey).id();\n const fileResult = await getFile.execute(fileId);\n\n if (fileResult.isFail()) {\n return;\n }\n\n const file = fileResult.value;\n\n let allConnections: ConnectionRegistry.Data[] = [];\n const connectionsResult = await listConnections.execute();\n if (connectionsResult.isOk()) {\n allConnections = connectionsResult.value;\n }\n\n if (scanStatus === \"NO_THREATS_FOUND\") {\n const newTags = file.tags.filter(tag => tag !== \"threatScanInProgress\");\n await updateFile.execute({\n id: file.id,\n tags: newTags\n });\n\n await sendToConnections.execute(allConnections, {\n action: \"fm.threatScan.noThreatFound\",\n data: {\n id: file.id,\n tags: newTags\n }\n });\n\n return;\n }\n\n if (scanStatus === \"THREATS_FOUND\") {\n // Delete the infected file.\n await deleteFile.execute(file.id);\n\n await sendToConnections.execute(allConnections, {\n action: \"fm.threatScan.threatDetected\",\n data: {\n id: file.id,\n name: file.name\n }\n });\n\n return;\n }\n\n // For all other outcomes, we delete the file, until better logic is implemented.\n await deleteFile.execute(file.id);\n\n await sendToConnections.execute(allConnections, {\n action: \"fm.threatScan.unsupported\",\n data: {\n id: file.id,\n name: file.name\n }\n });\n });\n};\n"],"names":["processThreatScanResult","context","eventDetail","listConnections","WebsocketsListConnectionsUseCase","sendToConnections","WebsocketsSendToConnectionsUseCase","getFile","GetFileUseCase","updateFile","UpdateFileUseCase","deleteFile","DeleteFileUseCase","IdentityContext","scanStatus","s3Object","fileId","ObjectKey","fileResult","file","allConnections","connectionsResult","newTags","tag"],"mappings":";;;;;;AAaO,MAAMA,0BAA0B,OACnCC,SACAC;IAEA,MAAMC,kBAAkBF,QAAQ,SAAS,CAAC,OAAO,CAACG;IAClD,MAAMC,oBAAoBJ,QAAQ,SAAS,CAAC,OAAO,CAACK;IACpD,MAAMC,UAAUN,QAAQ,SAAS,CAAC,OAAO,CAACO;IAC1C,MAAMC,aAAaR,QAAQ,SAAS,CAAC,OAAO,CAACS;IAC7C,MAAMC,aAAaV,QAAQ,SAAS,CAAC,OAAO,CAACW;IAE7C,MAAMX,QAAQ,SAAS,CAAC,OAAO,CAACY,iBAAiB,oBAAoB,CAAC;QAClE,MAAMC,aAAaZ,YAAY,iBAAiB,CAAC,gBAAgB;QACjE,MAAMa,WAAWb,YAAY,eAAe;QAE5C,MAAMc,SAASC,UAAU,IAAI,CAACF,SAAS,SAAS,EAAE,EAAE;QACpD,MAAMG,aAAa,MAAMX,QAAQ,OAAO,CAACS;QAEzC,IAAIE,WAAW,MAAM,IACjB;QAGJ,MAAMC,OAAOD,WAAW,KAAK;QAE7B,IAAIE,iBAA4C,EAAE;QAClD,MAAMC,oBAAoB,MAAMlB,gBAAgB,OAAO;QACvD,IAAIkB,kBAAkB,IAAI,IACtBD,iBAAiBC,kBAAkB,KAAK;QAG5C,IAAIP,AAAe,uBAAfA,YAAmC;YACnC,MAAMQ,UAAUH,KAAK,IAAI,CAAC,MAAM,CAACI,CAAAA,MAAOA,AAAQ,2BAARA;YACxC,MAAMd,WAAW,OAAO,CAAC;gBACrB,IAAIU,KAAK,EAAE;gBACX,MAAMG;YACV;YAEA,MAAMjB,kBAAkB,OAAO,CAACe,gBAAgB;gBAC5C,QAAQ;gBACR,MAAM;oBACF,IAAID,KAAK,EAAE;oBACX,MAAMG;gBACV;YACJ;YAEA;QACJ;QAEA,IAAIR,AAAe,oBAAfA,YAAgC;YAEhC,MAAMH,WAAW,OAAO,CAACQ,KAAK,EAAE;YAEhC,MAAMd,kBAAkB,OAAO,CAACe,gBAAgB;gBAC5C,QAAQ;gBACR,MAAM;oBACF,IAAID,KAAK,EAAE;oBACX,MAAMA,KAAK,IAAI;gBACnB;YACJ;YAEA;QACJ;QAGA,MAAMR,WAAW,OAAO,CAACQ,KAAK,EAAE;QAEhC,MAAMd,kBAAkB,OAAO,CAACe,gBAAgB;YAC5C,QAAQ;YACR,MAAM;gBACF,IAAID,KAAK,EAAE;gBACX,MAAMA,KAAK,IAAI;YACnB;QACJ;IACJ;AACJ"}
@@ -1,5 +1,8 @@
1
+ import type { ImageFormat } from "@webiny/api-file-manager/features/assetDelivery/transformation/index.js";
1
2
  export type AssetDeliveryParams = {
2
3
  imageResizeWidths?: number[];
4
+ /** Per-format encoder quality (1-100). Merged over the built-in defaults. */
5
+ imageQuality?: Partial<Record<ImageFormat, number>>;
3
6
  /**
4
7
  * BE CAREFUL!
5
8
  * Setting this to more than 1 hour may cause your URLs to still expire before the desired expiration time.
@@ -1,8 +1,10 @@
1
1
  import { CreateFileUseCase } from "@webiny/api-file-manager/features/file/CreateFile/abstractions.js";
2
2
  import type { CreateFileInput } from "@webiny/api-file-manager/features/file/CreateFile/abstractions.js";
3
+ import { FeatureFlags } from "@webiny/api-core/features/featureFlags/abstractions.js";
3
4
  declare class CreateFileWithThreatScanDecoratorImpl implements CreateFileUseCase.Interface {
5
+ private featureFlags;
4
6
  private decoratee;
5
- constructor(decoratee: CreateFileUseCase.Interface);
7
+ constructor(featureFlags: FeatureFlags.Interface, decoratee: CreateFileUseCase.Interface);
6
8
  execute(input: CreateFileInput, meta?: Record<string, any>): ReturnType<CreateFileUseCase.Interface["execute"]>;
7
9
  }
8
10
  export declare const CreateFileWithThreatScanDecorator: typeof CreateFileWithThreatScanDecoratorImpl & {
@@ -1,9 +1,12 @@
1
1
  import { CreateFileUseCase } from "@webiny/api-file-manager/features/file/CreateFile/abstractions.js";
2
+ import { FeatureFlags } from "@webiny/api-core/features/featureFlags/abstractions.js";
2
3
  class CreateFileWithThreatScanDecoratorImpl {
3
- constructor(decoratee){
4
+ constructor(featureFlags, decoratee){
5
+ this.featureFlags = featureFlags;
4
6
  this.decoratee = decoratee;
5
7
  }
6
8
  async execute(input, meta) {
9
+ if (!this.featureFlags.get().isEnabled("fileManager.threatDetection")) return this.decoratee.execute(input, meta);
7
10
  const modifiedInput = {
8
11
  ...input,
9
12
  tags: [
@@ -16,7 +19,9 @@ class CreateFileWithThreatScanDecoratorImpl {
16
19
  }
17
20
  const CreateFileWithThreatScanDecorator = CreateFileUseCase.createDecorator({
18
21
  decorator: CreateFileWithThreatScanDecoratorImpl,
19
- dependencies: []
22
+ dependencies: [
23
+ FeatureFlags
24
+ ]
20
25
  });
21
26
  export { CreateFileWithThreatScanDecorator };
22
27
 
@@ -1 +1 @@
1
- {"version":3,"file":"enterprise/ApplyThreatScanning/CreateFileWithThreatScanDecorator.js","sources":["../../../src/enterprise/ApplyThreatScanning/CreateFileWithThreatScanDecorator.ts"],"sourcesContent":["import { CreateFileUseCase } from \"@webiny/api-file-manager/features/file/CreateFile/abstractions.js\";\nimport type { CreateFileInput } from \"@webiny/api-file-manager/features/file/CreateFile/abstractions.js\";\n\nclass CreateFileWithThreatScanDecoratorImpl implements CreateFileUseCase.Interface {\n constructor(private decoratee: CreateFileUseCase.Interface) {}\n\n async execute(\n input: CreateFileInput,\n meta?: Record<string, any>\n ): ReturnType<CreateFileUseCase.Interface[\"execute\"]> {\n const modifiedInput: CreateFileInput = {\n ...input,\n tags: [...(input.tags || []), \"threatScanInProgress\"]\n };\n\n return this.decoratee.execute(modifiedInput, meta);\n }\n}\n\nexport const CreateFileWithThreatScanDecorator = CreateFileUseCase.createDecorator({\n decorator: CreateFileWithThreatScanDecoratorImpl,\n dependencies: []\n});\n"],"names":["CreateFileWithThreatScanDecoratorImpl","decoratee","input","meta","modifiedInput","CreateFileWithThreatScanDecorator","CreateFileUseCase"],"mappings":";AAGA,MAAMA;IACF,YAAoBC,SAAsC,CAAE;aAAxCA,SAAS,GAATA;IAAyC;IAE7D,MAAM,QACFC,KAAsB,EACtBC,IAA0B,EACwB;QAClD,MAAMC,gBAAiC;YACnC,GAAGF,KAAK;YACR,MAAM;mBAAKA,MAAM,IAAI,IAAI,EAAE;gBAAG;aAAuB;QACzD;QAEA,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAACE,eAAeD;IACjD;AACJ;AAEO,MAAME,oCAAoCC,kBAAkB,eAAe,CAAC;IAC/E,WAAWN;IACX,cAAc,EAAE;AACpB"}
1
+ {"version":3,"file":"enterprise/ApplyThreatScanning/CreateFileWithThreatScanDecorator.js","sources":["../../../src/enterprise/ApplyThreatScanning/CreateFileWithThreatScanDecorator.ts"],"sourcesContent":["import { CreateFileUseCase } from \"@webiny/api-file-manager/features/file/CreateFile/abstractions.js\";\nimport type { CreateFileInput } from \"@webiny/api-file-manager/features/file/CreateFile/abstractions.js\";\nimport { FeatureFlags } from \"@webiny/api-core/features/featureFlags/abstractions.js\";\n\nclass CreateFileWithThreatScanDecoratorImpl implements CreateFileUseCase.Interface {\n constructor(\n private featureFlags: FeatureFlags.Interface,\n private decoratee: CreateFileUseCase.Interface\n ) {}\n\n async execute(\n input: CreateFileInput,\n meta?: Record<string, any>\n ): ReturnType<CreateFileUseCase.Interface[\"execute\"]> {\n if (!this.featureFlags.get().isEnabled(\"fileManager.threatDetection\")) {\n return this.decoratee.execute(input, meta);\n }\n\n const modifiedInput: CreateFileInput = {\n ...input,\n tags: [...(input.tags || []), \"threatScanInProgress\"]\n };\n\n return this.decoratee.execute(modifiedInput, meta);\n }\n}\n\nexport const CreateFileWithThreatScanDecorator = CreateFileUseCase.createDecorator({\n decorator: CreateFileWithThreatScanDecoratorImpl,\n dependencies: [FeatureFlags]\n});\n"],"names":["CreateFileWithThreatScanDecoratorImpl","featureFlags","decoratee","input","meta","modifiedInput","CreateFileWithThreatScanDecorator","CreateFileUseCase","FeatureFlags"],"mappings":";;AAIA,MAAMA;IACF,YACYC,YAAoC,EACpCC,SAAsC,CAChD;aAFUD,YAAY,GAAZA;aACAC,SAAS,GAATA;IACT;IAEH,MAAM,QACFC,KAAsB,EACtBC,IAA0B,EACwB;QAClD,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,GAAG,SAAS,CAAC,gCACnC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAACD,OAAOC;QAGzC,MAAMC,gBAAiC;YACnC,GAAGF,KAAK;YACR,MAAM;mBAAKA,MAAM,IAAI,IAAI,EAAE;gBAAG;aAAuB;QACzD;QAEA,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAACE,eAAeD;IACjD;AACJ;AAEO,MAAME,oCAAoCC,kBAAkB,eAAe,CAAC;IAC/E,WAAWP;IACX,cAAc;QAACQ;KAAa;AAChC"}
@@ -23,7 +23,7 @@ declare class DeleteS3FolderTask implements TaskDefinition.Interface<DeleteS3Fol
23
23
  description: string;
24
24
  maxIterations: number;
25
25
  isPrivate: boolean;
26
- readonly selfCleanup: ("onSuccess" | "onAbort")[];
26
+ readonly selfCleanup: ("onAbort" | "onSuccess")[];
27
27
  run({ input, controller }: TaskDefinition.RunParams<DeleteS3FolderInput>): Promise<TaskDefinition.Result<DeleteS3FolderInput>>;
28
28
  }
29
29
  export declare const DeleteS3FolderTaskDefinition: typeof DeleteS3FolderTask & {
@@ -13,7 +13,7 @@ declare class ExtractMetadataTask implements TaskDefinition.Interface<ExtractMet
13
13
  maxIterations: number;
14
14
  isPrivate: boolean;
15
15
  databaseLogs: boolean;
16
- selfCleanup: ("onSuccess" | "onAbort")[];
16
+ selfCleanup: ("onAbort" | "onSuccess")[];
17
17
  constructor(keyValueStore: GlobalKeyValueStore.Interface, updateFileUseCase: UpdateFileUseCase.Interface);
18
18
  run({ input, controller }: TaskDefinition.RunParams<ExtractMetadataInput>): Promise<TaskDefinition.Result<ExtractMetadataInput>>;
19
19
  private cleanValues;
@@ -9,7 +9,7 @@ class FlushCacheOnFileDeleteHandlerImpl {
9
9
  async handle(event) {
10
10
  const { file } = event.payload;
11
11
  await this.taskService.trigger({
12
- definition: "cloudfrontInvalidateCache",
12
+ definition: "invalidateAssetCache",
13
13
  input: {
14
14
  caller: "fm-before-delete",
15
15
  paths: this.pathsGenerator.generate(file.id)
@@ -1 +1 @@
1
- {"version":3,"file":"features/FlushCache/FlushCacheOnFileDeleteHandler.js","sources":["../../../src/features/FlushCache/FlushCacheOnFileDeleteHandler.ts"],"sourcesContent":["import { FileAfterDeleteEventHandler } from \"@webiny/api-file-manager/features/file/DeleteFile/events.js\";\nimport { TaskService } from \"@webiny/api-core/features/task/TaskService/index.js\";\nimport { CdnPathsGenerator } from \"~/utils/CdnPathsGenerator.js\";\n\nclass FlushCacheOnFileDeleteHandlerImpl implements FileAfterDeleteEventHandler.Interface {\n private readonly pathsGenerator: CdnPathsGenerator;\n\n constructor(private taskService: TaskService.Interface) {\n this.pathsGenerator = new CdnPathsGenerator();\n }\n\n async handle(event: FileAfterDeleteEventHandler.Event): Promise<void> {\n const { file } = event.payload;\n\n await this.taskService.trigger({\n definition: \"cloudfrontInvalidateCache\",\n input: {\n caller: \"fm-before-delete\",\n paths: this.pathsGenerator.generate(file.id)\n }\n });\n }\n}\n\nexport const FlushCacheOnFileDeleteHandler = FileAfterDeleteEventHandler.createImplementation({\n implementation: FlushCacheOnFileDeleteHandlerImpl,\n dependencies: [TaskService]\n});\n"],"names":["FlushCacheOnFileDeleteHandlerImpl","taskService","CdnPathsGenerator","event","file","FlushCacheOnFileDeleteHandler","FileAfterDeleteEventHandler","TaskService"],"mappings":";;;AAIA,MAAMA;IAGF,YAAoBC,WAAkC,CAAE;aAApCA,WAAW,GAAXA;QAChB,IAAI,CAAC,cAAc,GAAG,IAAIC;IAC9B;IAEA,MAAM,OAAOC,KAAwC,EAAiB;QAClE,MAAM,EAAEC,IAAI,EAAE,GAAGD,MAAM,OAAO;QAE9B,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;YAC3B,YAAY;YACZ,OAAO;gBACH,QAAQ;gBACR,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACC,KAAK,EAAE;YAC/C;QACJ;IACJ;AACJ;AAEO,MAAMC,gCAAgCC,4BAA4B,oBAAoB,CAAC;IAC1F,gBAAgBN;IAChB,cAAc;QAACO;KAAY;AAC/B"}
1
+ {"version":3,"file":"features/FlushCache/FlushCacheOnFileDeleteHandler.js","sources":["../../../src/features/FlushCache/FlushCacheOnFileDeleteHandler.ts"],"sourcesContent":["import { FileAfterDeleteEventHandler } from \"@webiny/api-file-manager/features/file/DeleteFile/events.js\";\nimport { TaskService } from \"@webiny/api-core/features/task/TaskService/index.js\";\nimport { CdnPathsGenerator } from \"~/utils/CdnPathsGenerator.js\";\n\nclass FlushCacheOnFileDeleteHandlerImpl implements FileAfterDeleteEventHandler.Interface {\n private readonly pathsGenerator: CdnPathsGenerator;\n\n constructor(private taskService: TaskService.Interface) {\n this.pathsGenerator = new CdnPathsGenerator();\n }\n\n async handle(event: FileAfterDeleteEventHandler.Event): Promise<void> {\n const { file } = event.payload;\n\n await this.taskService.trigger({\n definition: \"invalidateAssetCache\",\n input: {\n caller: \"fm-before-delete\",\n paths: this.pathsGenerator.generate(file.id)\n }\n });\n }\n}\n\nexport const FlushCacheOnFileDeleteHandler = FileAfterDeleteEventHandler.createImplementation({\n implementation: FlushCacheOnFileDeleteHandlerImpl,\n dependencies: [TaskService]\n});\n"],"names":["FlushCacheOnFileDeleteHandlerImpl","taskService","CdnPathsGenerator","event","file","FlushCacheOnFileDeleteHandler","FileAfterDeleteEventHandler","TaskService"],"mappings":";;;AAIA,MAAMA;IAGF,YAAoBC,WAAkC,CAAE;aAApCA,WAAW,GAAXA;QAChB,IAAI,CAAC,cAAc,GAAG,IAAIC;IAC9B;IAEA,MAAM,OAAOC,KAAwC,EAAiB;QAClE,MAAM,EAAEC,IAAI,EAAE,GAAGD,MAAM,OAAO;QAE9B,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;YAC3B,YAAY;YACZ,OAAO;gBACH,QAAQ;gBACR,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACC,KAAK,EAAE;YAC/C;QACJ;IACJ;AACJ;AAEO,MAAMC,gCAAgCC,4BAA4B,oBAAoB,CAAC;IAC1F,gBAAgBN;IAChB,cAAc;QAACO;KAAY;AAC/B"}
@@ -12,7 +12,7 @@ class FlushCacheOnFileUpdateHandlerImpl {
12
12
  const newAccessControl = file.accessControl;
13
13
  if (prevAccessControl?.type === newAccessControl?.type) return;
14
14
  await this.taskService.trigger({
15
- definition: "cloudfrontInvalidateCache",
15
+ definition: "invalidateAssetCache",
16
16
  input: {
17
17
  caller: "fm-before-update",
18
18
  paths: this.pathsGenerator.generate(file.id)
@@ -1 +1 @@
1
- {"version":3,"file":"features/FlushCache/FlushCacheOnFileUpdateHandler.js","sources":["../../../src/features/FlushCache/FlushCacheOnFileUpdateHandler.ts"],"sourcesContent":["import { FileBeforeUpdateEventHandler } from \"@webiny/api-file-manager/features/file/UpdateFile/events.js\";\nimport { TaskService } from \"@webiny/api-core/features/task/TaskService/index.js\";\nimport { CdnPathsGenerator } from \"~/utils/CdnPathsGenerator.js\";\n\nclass FlushCacheOnFileUpdateHandlerImpl implements FileBeforeUpdateEventHandler.Interface {\n private readonly pathsGenerator: CdnPathsGenerator;\n\n constructor(private taskService: TaskService.Interface) {\n this.pathsGenerator = new CdnPathsGenerator();\n }\n\n async handle(event: FileBeforeUpdateEventHandler.Event): Promise<void> {\n const { file, original } = event.payload;\n\n const prevAccessControl = original.accessControl;\n const newAccessControl = file.accessControl;\n\n // Only trigger cache flush if access control type has changed\n if (prevAccessControl?.type === newAccessControl?.type) {\n return;\n }\n\n await this.taskService.trigger({\n definition: \"cloudfrontInvalidateCache\",\n input: {\n caller: \"fm-before-update\",\n paths: this.pathsGenerator.generate(file.id)\n }\n });\n }\n}\n\nexport const FlushCacheOnFileUpdateHandler = FileBeforeUpdateEventHandler.createImplementation({\n implementation: FlushCacheOnFileUpdateHandlerImpl,\n dependencies: [TaskService]\n});\n"],"names":["FlushCacheOnFileUpdateHandlerImpl","taskService","CdnPathsGenerator","event","file","original","prevAccessControl","newAccessControl","FlushCacheOnFileUpdateHandler","FileBeforeUpdateEventHandler","TaskService"],"mappings":";;;AAIA,MAAMA;IAGF,YAAoBC,WAAkC,CAAE;aAApCA,WAAW,GAAXA;QAChB,IAAI,CAAC,cAAc,GAAG,IAAIC;IAC9B;IAEA,MAAM,OAAOC,KAAyC,EAAiB;QACnE,MAAM,EAAEC,IAAI,EAAEC,QAAQ,EAAE,GAAGF,MAAM,OAAO;QAExC,MAAMG,oBAAoBD,SAAS,aAAa;QAChD,MAAME,mBAAmBH,KAAK,aAAa;QAG3C,IAAIE,mBAAmB,SAASC,kBAAkB,MAC9C;QAGJ,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;YAC3B,YAAY;YACZ,OAAO;gBACH,QAAQ;gBACR,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACH,KAAK,EAAE;YAC/C;QACJ;IACJ;AACJ;AAEO,MAAMI,gCAAgCC,6BAA6B,oBAAoB,CAAC;IAC3F,gBAAgBT;IAChB,cAAc;QAACU;KAAY;AAC/B"}
1
+ {"version":3,"file":"features/FlushCache/FlushCacheOnFileUpdateHandler.js","sources":["../../../src/features/FlushCache/FlushCacheOnFileUpdateHandler.ts"],"sourcesContent":["import { FileBeforeUpdateEventHandler } from \"@webiny/api-file-manager/features/file/UpdateFile/events.js\";\nimport { TaskService } from \"@webiny/api-core/features/task/TaskService/index.js\";\nimport { CdnPathsGenerator } from \"~/utils/CdnPathsGenerator.js\";\n\nclass FlushCacheOnFileUpdateHandlerImpl implements FileBeforeUpdateEventHandler.Interface {\n private readonly pathsGenerator: CdnPathsGenerator;\n\n constructor(private taskService: TaskService.Interface) {\n this.pathsGenerator = new CdnPathsGenerator();\n }\n\n async handle(event: FileBeforeUpdateEventHandler.Event): Promise<void> {\n const { file, original } = event.payload;\n\n const prevAccessControl = original.accessControl;\n const newAccessControl = file.accessControl;\n\n // Only trigger cache flush if access control type has changed\n if (prevAccessControl?.type === newAccessControl?.type) {\n return;\n }\n\n await this.taskService.trigger({\n definition: \"invalidateAssetCache\",\n input: {\n caller: \"fm-before-update\",\n paths: this.pathsGenerator.generate(file.id)\n }\n });\n }\n}\n\nexport const FlushCacheOnFileUpdateHandler = FileBeforeUpdateEventHandler.createImplementation({\n implementation: FlushCacheOnFileUpdateHandlerImpl,\n dependencies: [TaskService]\n});\n"],"names":["FlushCacheOnFileUpdateHandlerImpl","taskService","CdnPathsGenerator","event","file","original","prevAccessControl","newAccessControl","FlushCacheOnFileUpdateHandler","FileBeforeUpdateEventHandler","TaskService"],"mappings":";;;AAIA,MAAMA;IAGF,YAAoBC,WAAkC,CAAE;aAApCA,WAAW,GAAXA;QAChB,IAAI,CAAC,cAAc,GAAG,IAAIC;IAC9B;IAEA,MAAM,OAAOC,KAAyC,EAAiB;QACnE,MAAM,EAAEC,IAAI,EAAEC,QAAQ,EAAE,GAAGF,MAAM,OAAO;QAExC,MAAMG,oBAAoBD,SAAS,aAAa;QAChD,MAAME,mBAAmBH,KAAK,aAAa;QAG3C,IAAIE,mBAAmB,SAASC,kBAAkB,MAC9C;QAGJ,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;YAC3B,YAAY;YACZ,OAAO;gBACH,QAAQ;gBACR,OAAO,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACH,KAAK,EAAE;YAC/C;QACJ;IACJ;AACJ;AAEO,MAAMI,gCAAgCC,6BAA6B,oBAAoB,CAAC;IAC3F,gBAAgBT;IAChB,cAAc;QAACU;KAAY;AAC/B"}
@@ -15,9 +15,9 @@ declare class InvalidateCloudfrontCacheTask implements TaskDefinition.Interface<
15
15
  description: string;
16
16
  maxIterations: number;
17
17
  isPrivate: boolean;
18
- selfCleanup: ("onSuccess" | "onAbort")[];
18
+ selfCleanup: ("onAbort" | "onSuccess")[];
19
19
  private continueIfCode;
20
- run({ input, controller }: TaskDefinition.RunParams<InvalidateCacheInput>): Promise<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultError | import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultAborted | TaskDefinition.ResultDone<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput> | TaskDefinition.ResultContinue<InvalidateCacheInput>>;
20
+ run({ input, controller }: TaskDefinition.RunParams<InvalidateCacheInput>): Promise<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultAborted | import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskResultError | TaskDefinition.ResultContinue<InvalidateCacheInput> | TaskDefinition.ResultDone<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
21
21
  private invalidateCache;
22
22
  }
23
23
  export declare const InvalidateCloudfrontCacheTaskDefinition: typeof InvalidateCloudfrontCacheTask & {
@@ -1,4 +1,4 @@
1
- import { ServiceDiscovery } from "@webiny/api";
1
+ import { ServiceDiscovery } from "@webiny/api-core/features/serviceDiscovery/index.js";
2
2
  import { CloudFront } from "@webiny/aws-sdk/client-cloudfront/index.js";
3
3
  import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
4
4
  import { executeWithRetry } from "@webiny/utils";
@@ -51,7 +51,7 @@ class InvalidateCloudfrontCacheTask {
51
51
  });
52
52
  }
53
53
  constructor(){
54
- this.id = "cloudfrontInvalidateCache";
54
+ this.id = "invalidateAssetCache";
55
55
  this.title = "Invalidate CloudFront Cache";
56
56
  this.description = "A task to invalidate Cloudfront cache by given paths.";
57
57
  this.maxIterations = 100;
@@ -1 +1 @@
1
- {"version":3,"file":"features/FlushCache/InvalidateCacheTask.js","sources":["../../../src/features/FlushCache/InvalidateCacheTask.ts"],"sourcesContent":["import { ServiceDiscovery } from \"@webiny/api\";\nimport { CloudFront } from \"@webiny/aws-sdk/client-cloudfront/index.js\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { executeWithRetry } from \"@webiny/utils\";\n\nclass ReturnContinue extends Error {}\n\nexport interface InvalidateCacheInput {\n /**\n * Caller of the task (e.g., `fm-before-update`, `fm-after-delete`).\n */\n caller: string;\n /**\n * Cache paths to invalidate.\n */\n paths: string[];\n}\n\nclass InvalidateCloudfrontCacheTask implements TaskDefinition.Interface<InvalidateCacheInput> {\n id = \"cloudfrontInvalidateCache\";\n title = \"Invalidate CloudFront Cache\";\n description = \"A task to invalidate Cloudfront cache by given paths.\";\n maxIterations = 100;\n isPrivate = true;\n\n selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n private continueIfCode = [\"TooManyInvalidationsInProgress\", \"Throttling\"];\n\n public async run({ input, controller }: TaskDefinition.RunParams<InvalidateCacheInput>) {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const manifest = await ServiceDiscovery.load();\n\n if (!manifest) {\n return controller.response.error({\n message: `Unable to invalidate cache due to a missing service manifest.`,\n code: \"MISSING_SERVICE_MANIFEST\",\n data: {\n manifest: \"api\"\n }\n });\n }\n\n const { distributionId } = manifest.api.cloudfront;\n\n const invalidateCache = () => {\n return this.invalidateCache(input.caller, distributionId as string, input.paths);\n };\n\n try {\n await executeWithRetry(invalidateCache, {\n minTimeout: 2000,\n // instead of forever: true\n retries: 10000,\n onFailedAttempt: ({ error }) => {\n if (this.continueIfCode.includes(error.name)) {\n throw new ReturnContinue();\n }\n\n if (error.message.includes(\"not authorized to perform\")) {\n throw error;\n }\n\n if (controller.runtime.isCloseToTimeout()) {\n throw new ReturnContinue();\n }\n }\n });\n } catch (error) {\n if (error instanceof ReturnContinue) {\n return controller.response.continue(input);\n }\n\n return controller.response.error({\n message: error.message,\n code: \"EXECUTE_WITH_RETRY_FAILED\",\n data: input.paths\n });\n }\n\n return controller.response.done();\n }\n\n private async invalidateCache(\n caller: string,\n distributionId: string,\n paths: string[]\n ): Promise<void> {\n const cloudfront = new CloudFront();\n await cloudfront.createInvalidation({\n DistributionId: distributionId,\n InvalidationBatch: {\n CallerReference: `${new Date().getTime()}-${caller}`,\n Paths: {\n Quantity: paths.length,\n Items: paths\n }\n }\n });\n }\n}\n\nexport const InvalidateCloudfrontCacheTaskDefinition = TaskDefinition.createImplementation({\n implementation: InvalidateCloudfrontCacheTask,\n dependencies: []\n});\n"],"names":["ReturnContinue","Error","InvalidateCloudfrontCacheTask","input","controller","manifest","ServiceDiscovery","distributionId","invalidateCache","executeWithRetry","error","caller","paths","cloudfront","CloudFront","Date","InvalidateCloudfrontCacheTaskDefinition","TaskDefinition"],"mappings":";;;;AAKA,MAAMA,uBAAuBC;AAAO;AAapC,MAAMC;IAWF,MAAa,IAAI,EAAEC,KAAK,EAAEC,UAAU,EAAkD,EAAE;QACpF,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,MAAMC,WAAW,MAAMC,iBAAiB,IAAI;QAE5C,IAAI,CAACD,UACD,OAAOD,WAAW,QAAQ,CAAC,KAAK,CAAC;YAC7B,SAAS;YACT,MAAM;YACN,MAAM;gBACF,UAAU;YACd;QACJ;QAGJ,MAAM,EAAEG,cAAc,EAAE,GAAGF,SAAS,GAAG,CAAC,UAAU;QAElD,MAAMG,kBAAkB,IACb,IAAI,CAAC,eAAe,CAACL,MAAM,MAAM,EAAEI,gBAA0BJ,MAAM,KAAK;QAGnF,IAAI;YACA,MAAMM,iBAAiBD,iBAAiB;gBACpC,YAAY;gBAEZ,SAAS;gBACT,iBAAiB,CAAC,EAAEE,KAAK,EAAE;oBACvB,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACA,MAAM,IAAI,GACvC,MAAM,IAAIV;oBAGd,IAAIU,MAAM,OAAO,CAAC,QAAQ,CAAC,8BACvB,MAAMA;oBAGV,IAAIN,WAAW,OAAO,CAAC,gBAAgB,IACnC,MAAM,IAAIJ;gBAElB;YACJ;QACJ,EAAE,OAAOU,OAAO;YACZ,IAAIA,iBAAiBV,gBACjB,OAAOI,WAAW,QAAQ,CAAC,QAAQ,CAACD;YAGxC,OAAOC,WAAW,QAAQ,CAAC,KAAK,CAAC;gBAC7B,SAASM,MAAM,OAAO;gBACtB,MAAM;gBACN,MAAMP,MAAM,KAAK;YACrB;QACJ;QAEA,OAAOC,WAAW,QAAQ,CAAC,IAAI;IACnC;IAEA,MAAc,gBACVO,MAAc,EACdJ,cAAsB,EACtBK,KAAe,EACF;QACb,MAAMC,aAAa,IAAIC;QACvB,MAAMD,WAAW,kBAAkB,CAAC;YAChC,gBAAgBN;YAChB,mBAAmB;gBACf,iBAAiB,GAAG,IAAIQ,OAAO,OAAO,GAAG,CAAC,EAAEJ,QAAQ;gBACpD,OAAO;oBACH,UAAUC,MAAM,MAAM;oBACtB,OAAOA;gBACX;YACJ;QACJ;IACJ;;aAnFA,EAAE,GAAG;aACL,KAAK,GAAG;aACR,WAAW,GAAG;aACd,aAAa,GAAG;aAChB,SAAS,GAAG;aAEZ,WAAW,GAAG;YAAC;YAAsB;SAAmB;aAEhD,cAAc,GAAG;YAAC;YAAkC;SAAa;;AA4E7E;AAEO,MAAMI,0CAA0CC,eAAe,oBAAoB,CAAC;IACvF,gBAAgBf;IAChB,cAAc,EAAE;AACpB"}
1
+ {"version":3,"file":"features/FlushCache/InvalidateCacheTask.js","sources":["../../../src/features/FlushCache/InvalidateCacheTask.ts"],"sourcesContent":["import { ServiceDiscovery } from \"@webiny/api-core/features/serviceDiscovery/index.js\";\nimport { CloudFront } from \"@webiny/aws-sdk/client-cloudfront/index.js\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { executeWithRetry } from \"@webiny/utils\";\n\nclass ReturnContinue extends Error {}\n\nexport interface InvalidateCacheInput {\n /**\n * Caller of the task (e.g., `fm-before-update`, `fm-after-delete`).\n */\n caller: string;\n /**\n * Cache paths to invalidate.\n */\n paths: string[];\n}\n\nclass InvalidateCloudfrontCacheTask implements TaskDefinition.Interface<InvalidateCacheInput> {\n id = \"invalidateAssetCache\";\n title = \"Invalidate CloudFront Cache\";\n description = \"A task to invalidate Cloudfront cache by given paths.\";\n maxIterations = 100;\n isPrivate = true;\n\n selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n private continueIfCode = [\"TooManyInvalidationsInProgress\", \"Throttling\"];\n\n public async run({ input, controller }: TaskDefinition.RunParams<InvalidateCacheInput>) {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const manifest = await ServiceDiscovery.load();\n\n if (!manifest) {\n return controller.response.error({\n message: `Unable to invalidate cache due to a missing service manifest.`,\n code: \"MISSING_SERVICE_MANIFEST\",\n data: {\n manifest: \"api\"\n }\n });\n }\n\n const { distributionId } = manifest.api.cloudfront;\n\n const invalidateCache = () => {\n return this.invalidateCache(input.caller, distributionId as string, input.paths);\n };\n\n try {\n await executeWithRetry(invalidateCache, {\n minTimeout: 2000,\n // instead of forever: true\n retries: 10000,\n onFailedAttempt: ({ error }) => {\n if (this.continueIfCode.includes(error.name)) {\n throw new ReturnContinue();\n }\n\n if (error.message.includes(\"not authorized to perform\")) {\n throw error;\n }\n\n if (controller.runtime.isCloseToTimeout()) {\n throw new ReturnContinue();\n }\n }\n });\n } catch (error) {\n if (error instanceof ReturnContinue) {\n return controller.response.continue(input);\n }\n\n return controller.response.error({\n message: error.message,\n code: \"EXECUTE_WITH_RETRY_FAILED\",\n data: input.paths\n });\n }\n\n return controller.response.done();\n }\n\n private async invalidateCache(\n caller: string,\n distributionId: string,\n paths: string[]\n ): Promise<void> {\n const cloudfront = new CloudFront();\n await cloudfront.createInvalidation({\n DistributionId: distributionId,\n InvalidationBatch: {\n CallerReference: `${new Date().getTime()}-${caller}`,\n Paths: {\n Quantity: paths.length,\n Items: paths\n }\n }\n });\n }\n}\n\nexport const InvalidateCloudfrontCacheTaskDefinition = TaskDefinition.createImplementation({\n implementation: InvalidateCloudfrontCacheTask,\n dependencies: []\n});\n"],"names":["ReturnContinue","Error","InvalidateCloudfrontCacheTask","input","controller","manifest","ServiceDiscovery","distributionId","invalidateCache","executeWithRetry","error","caller","paths","cloudfront","CloudFront","Date","InvalidateCloudfrontCacheTaskDefinition","TaskDefinition"],"mappings":";;;;AAKA,MAAMA,uBAAuBC;AAAO;AAapC,MAAMC;IAWF,MAAa,IAAI,EAAEC,KAAK,EAAEC,UAAU,EAAkD,EAAE;QACpF,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,MAAMC,WAAW,MAAMC,iBAAiB,IAAI;QAE5C,IAAI,CAACD,UACD,OAAOD,WAAW,QAAQ,CAAC,KAAK,CAAC;YAC7B,SAAS;YACT,MAAM;YACN,MAAM;gBACF,UAAU;YACd;QACJ;QAGJ,MAAM,EAAEG,cAAc,EAAE,GAAGF,SAAS,GAAG,CAAC,UAAU;QAElD,MAAMG,kBAAkB,IACb,IAAI,CAAC,eAAe,CAACL,MAAM,MAAM,EAAEI,gBAA0BJ,MAAM,KAAK;QAGnF,IAAI;YACA,MAAMM,iBAAiBD,iBAAiB;gBACpC,YAAY;gBAEZ,SAAS;gBACT,iBAAiB,CAAC,EAAEE,KAAK,EAAE;oBACvB,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAACA,MAAM,IAAI,GACvC,MAAM,IAAIV;oBAGd,IAAIU,MAAM,OAAO,CAAC,QAAQ,CAAC,8BACvB,MAAMA;oBAGV,IAAIN,WAAW,OAAO,CAAC,gBAAgB,IACnC,MAAM,IAAIJ;gBAElB;YACJ;QACJ,EAAE,OAAOU,OAAO;YACZ,IAAIA,iBAAiBV,gBACjB,OAAOI,WAAW,QAAQ,CAAC,QAAQ,CAACD;YAGxC,OAAOC,WAAW,QAAQ,CAAC,KAAK,CAAC;gBAC7B,SAASM,MAAM,OAAO;gBACtB,MAAM;gBACN,MAAMP,MAAM,KAAK;YACrB;QACJ;QAEA,OAAOC,WAAW,QAAQ,CAAC,IAAI;IACnC;IAEA,MAAc,gBACVO,MAAc,EACdJ,cAAsB,EACtBK,KAAe,EACF;QACb,MAAMC,aAAa,IAAIC;QACvB,MAAMD,WAAW,kBAAkB,CAAC;YAChC,gBAAgBN;YAChB,mBAAmB;gBACf,iBAAiB,GAAG,IAAIQ,OAAO,OAAO,GAAG,CAAC,EAAEJ,QAAQ;gBACpD,OAAO;oBACH,UAAUC,MAAM,MAAM;oBACtB,OAAOA;gBACX;YACJ;QACJ;IACJ;;aAnFA,EAAE,GAAG;aACL,KAAK,GAAG;aACR,WAAW,GAAG;aACd,aAAa,GAAG;aAChB,SAAS,GAAG;aAEZ,WAAW,GAAG;YAAC;YAAsB;SAAmB;aAEhD,cAAc,GAAG;YAAC;YAAkC;SAAa;;AA4E7E;AAEO,MAAMI,0CAA0CC,eAAe,oBAAoB,CAAC;IACvF,gBAAgBf;IAChB,cAAc,EAAE;AACpB"}
@@ -6,7 +6,9 @@ class MetadataWriter {
6
6
  async write(files) {
7
7
  const writers = files.map(async (file)=>{
8
8
  const metadata = this.getMetadata(file);
9
- await this.keyValueStore.set(`FileManager/File/${file.id}/Metadata`, metadata);
9
+ const key = `FileManager/File/${file.id}/Metadata`;
10
+ const result = await this.keyValueStore.set(key, metadata);
11
+ if (result.isFail()) console.error(`[FileManagerS3] Failed to write delivery metadata "${key}":`, result.error);
10
12
  });
11
13
  await Promise.all(writers);
12
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"features/WriteFileMetadata/MetadataWriter.js","sources":["../../../src/features/WriteFileMetadata/MetadataWriter.ts"],"sourcesContent":["import { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport type { File } from \"@webiny/api-file-manager/domain/file/types.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\n\nexport class MetadataWriter {\n constructor(\n private tenantContext: TenantContext.Interface,\n private keyValueStore: GlobalKeyValueStore.Interface\n ) {}\n\n async write(files: File[]) {\n /**\n * We need to write each file with retry.\n */\n const writers = files.map(async file => {\n const metadata = this.getMetadata(file);\n await this.keyValueStore.set(`FileManager/File/${file.id}/Metadata`, metadata);\n });\n\n await Promise.all(writers);\n }\n\n private getMetadata(file: File) {\n const tenant = this.tenantContext.getTenant();\n return {\n id: file.id,\n bucketKey: `tenants/${tenant.id}/files/${file.key}`,\n tenant: tenant.id,\n size: file.size,\n contentType: file.type\n };\n }\n}\n"],"names":["MetadataWriter","tenantContext","keyValueStore","files","writers","file","metadata","Promise","tenant"],"mappings":"AAIO,MAAMA;IACT,YACYC,aAAsC,EACtCC,aAA4C,CACtD;aAFUD,aAAa,GAAbA;aACAC,aAAa,GAAbA;IACT;IAEH,MAAM,MAAMC,KAAa,EAAE;QAIvB,MAAMC,UAAUD,MAAM,GAAG,CAAC,OAAME;YAC5B,MAAMC,WAAW,IAAI,CAAC,WAAW,CAACD;YAClC,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,iBAAiB,EAAEA,KAAK,EAAE,CAAC,SAAS,CAAC,EAAEC;QACzE;QAEA,MAAMC,QAAQ,GAAG,CAACH;IACtB;IAEQ,YAAYC,IAAU,EAAE;QAC5B,MAAMG,SAAS,IAAI,CAAC,aAAa,CAAC,SAAS;QAC3C,OAAO;YACH,IAAIH,KAAK,EAAE;YACX,WAAW,CAAC,QAAQ,EAAEG,OAAO,EAAE,CAAC,OAAO,EAAEH,KAAK,GAAG,EAAE;YACnD,QAAQG,OAAO,EAAE;YACjB,MAAMH,KAAK,IAAI;YACf,aAAaA,KAAK,IAAI;QAC1B;IACJ;AACJ"}
1
+ {"version":3,"file":"features/WriteFileMetadata/MetadataWriter.js","sources":["../../../src/features/WriteFileMetadata/MetadataWriter.ts"],"sourcesContent":["import { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport type { File } from \"@webiny/api-file-manager/domain/file/types.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\n\nexport class MetadataWriter {\n constructor(\n private tenantContext: TenantContext.Interface,\n private keyValueStore: GlobalKeyValueStore.Interface\n ) {}\n\n async write(files: File[]) {\n /**\n * We need to write each file with retry.\n */\n const writers = files.map(async file => {\n const metadata = this.getMetadata(file);\n const key = `FileManager/File/${file.id}/Metadata`;\n // Check the result: the KV store returns a failed Result (it does not throw), so an\n // ignored failure here would silently break asset delivery (missing metadata → 404).\n const result = await this.keyValueStore.set(key, metadata);\n if (result.isFail()) {\n console.error(\n `[FileManagerS3] Failed to write delivery metadata \"${key}\":`,\n result.error\n );\n }\n });\n\n await Promise.all(writers);\n }\n\n private getMetadata(file: File) {\n const tenant = this.tenantContext.getTenant();\n return {\n id: file.id,\n bucketKey: `tenants/${tenant.id}/files/${file.key}`,\n tenant: tenant.id,\n size: file.size,\n contentType: file.type\n };\n }\n}\n"],"names":["MetadataWriter","tenantContext","keyValueStore","files","writers","file","metadata","key","result","console","Promise","tenant"],"mappings":"AAIO,MAAMA;IACT,YACYC,aAAsC,EACtCC,aAA4C,CACtD;aAFUD,aAAa,GAAbA;aACAC,aAAa,GAAbA;IACT;IAEH,MAAM,MAAMC,KAAa,EAAE;QAIvB,MAAMC,UAAUD,MAAM,GAAG,CAAC,OAAME;YAC5B,MAAMC,WAAW,IAAI,CAAC,WAAW,CAACD;YAClC,MAAME,MAAM,CAAC,iBAAiB,EAAEF,KAAK,EAAE,CAAC,SAAS,CAAC;YAGlD,MAAMG,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAACD,KAAKD;YACjD,IAAIE,OAAO,MAAM,IACbC,QAAQ,KAAK,CACT,CAAC,mDAAmD,EAAEF,IAAI,EAAE,CAAC,EAC7DC,OAAO,KAAK;QAGxB;QAEA,MAAME,QAAQ,GAAG,CAACN;IACtB;IAEQ,YAAYC,IAAU,EAAE;QAC5B,MAAMM,SAAS,IAAI,CAAC,aAAa,CAAC,SAAS;QAC3C,OAAO;YACH,IAAIN,KAAK,EAAE;YACX,WAAW,CAAC,QAAQ,EAAEM,OAAO,EAAE,CAAC,OAAO,EAAEN,KAAK,GAAG,EAAE;YACnD,QAAQM,OAAO,EAAE;YACjB,MAAMN,KAAK,IAAI;YACf,aAAaA,KAAK,IAAI;QAC1B;IACJ;AACJ"}
@@ -0,0 +1,9 @@
1
+ import { CoreGraphQLSchemaFactory } from "@webiny/api-graphql/graphql/abstractions.js";
2
+ import { GraphQLSchemaBuilder } from "@webiny/api-graphql/features/GraphQLSchemaBuilder/abstractions.js";
3
+ declare class S3GraphQLSchemaImpl implements CoreGraphQLSchemaFactory.Interface {
4
+ execute(builder: GraphQLSchemaBuilder.Interface): CoreGraphQLSchemaFactory.Return;
5
+ }
6
+ export declare const S3GraphQLSchema: typeof S3GraphQLSchemaImpl & {
7
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-graphql/graphql/abstractions.core.js").ICoreGraphQLSchemaFactory>;
8
+ };
9
+ export {};