@webiny/api-file-manager-s3 6.6.0-alpha.0 → 6.6.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,4 @@
1
1
  import { createFeature } from "@webiny/feature/api";
2
- import { RequestContextInitializer } from "@webiny/event-handler-core";
3
- import { WcpContext } from "@webiny/api-core/features/wcp/WcpContext/index.js";
4
2
  import { S3GraphQLSchema } from "./graphql/S3GraphQLSchema.js";
5
3
  import { DeleteFileFromBucketFeature } from "./features/DeleteFileFromBucket/feature.js";
6
4
  import { WriteFileMetadataFeature } from "./features/WriteFileMetadata/feature.js";
@@ -21,12 +19,7 @@ const FileManagerS3Feature = createFeature({
21
19
  GetFileContentsByIdFeature.register(container);
22
20
  GetFileContentsByKeyFeature.register(container);
23
21
  container.register(S3GraphQLSchema);
24
- container.registerInstance(RequestContextInitializer, {
25
- async init (ctx) {
26
- const wcp = ctx.container.resolve(WcpContext);
27
- if (wcp.canUseFileManagerThreatDetection()) ApplyThreatScanningFeature.register(ctx.container);
28
- }
29
- });
22
+ ApplyThreatScanningFeature.register(container);
30
23
  }
31
24
  });
32
25
  export { FileManagerS3Feature };
@@ -1 +1 @@
1
- {"version":3,"file":"FileManagerS3Feature.js","sources":["../src/FileManagerS3Feature.ts"],"sourcesContent":["import { type Container, createFeature } from \"@webiny/feature/api\";\nimport { RequestContextInitializer } from \"@webiny/event-handler-core\";\nimport { WcpContext } from \"@webiny/api-core/features/wcp/WcpContext/index.js\";\nimport { S3GraphQLSchema } from \"./graphql/S3GraphQLSchema.js\";\nimport { DeleteFileFromBucketFeature } from \"~/features/DeleteFileFromBucket/feature.js\";\nimport { WriteFileMetadataFeature } from \"~/features/WriteFileMetadata/feature.js\";\nimport { ApplyThreatScanningFeature } from \"~/enterprise/ApplyThreatScanning/feature.js\";\nimport { FlushCacheFeature } from \"~/features/FlushCache/feature.js\";\nimport { ExtractMetadataFeature } from \"~/features/ExtractMetadata/feature.js\";\nimport { GetFileContentsByIdFeature } from \"~/features/GetFileContentsById/feature.js\";\nimport { GetFileContentsByKeyFeature } from \"~/features/GetFileContentsByKey/feature.js\";\nimport { createS3AssetDeliveryFeature } from \"./assetDelivery/feature.js\";\nimport type { AssetDeliveryParams } from \"./assetDelivery/types.js\";\n\nexport interface FileManagerS3FeatureConfig {\n assetDelivery?: AssetDeliveryParams;\n}\n\nexport const FileManagerS3Feature = createFeature({\n name: \"FileManagerS3\",\n register(container: Container, config: FileManagerS3FeatureConfig = {}) {\n // Register S3-specific asset delivery implementations (S3AssetResolver, S3OutputStrategy).\n // These replace the null implementations from AssetDeliveryFeature in FileManagerAppFeature.\n createS3AssetDeliveryFeature(config.assetDelivery).register(container);\n\n // S3 file-operation features.\n FlushCacheFeature.register(container);\n DeleteFileFromBucketFeature.register(container);\n ExtractMetadataFeature.register(container);\n WriteFileMetadataFeature.register(container);\n GetFileContentsByIdFeature.register(container);\n GetFileContentsByKeyFeature.register(container);\n\n // Static S3 GraphQL schema (extends FmQuery/FmMutation) — a DI-native\n // CoreGraphQLSchemaFactory contributor (declares its resolver dependencies).\n container.register(S3GraphQLSchema);\n\n // Threat scanning is WCP-gated. The gate MUST run after the per-request WCP license refresh\n // (a RequestInitializer, pre-auth) — at register() time WcpContext still sees the NullLicense\n // and the feature would silently never register. So gate + register it in a\n // RequestContextInitializer (post-auth, post-license).\n container.registerInstance(RequestContextInitializer, {\n async init(ctx: Record<string, any>) {\n const wcp = ctx.container.resolve(WcpContext);\n if (wcp.canUseFileManagerThreatDetection()) {\n ApplyThreatScanningFeature.register(ctx.container);\n }\n }\n });\n }\n});\n"],"names":["FileManagerS3Feature","createFeature","container","config","createS3AssetDeliveryFeature","FlushCacheFeature","DeleteFileFromBucketFeature","ExtractMetadataFeature","WriteFileMetadataFeature","GetFileContentsByIdFeature","GetFileContentsByKeyFeature","S3GraphQLSchema","RequestContextInitializer","ctx","wcp","WcpContext","ApplyThreatScanningFeature"],"mappings":";;;;;;;;;;;;AAkBO,MAAMA,uBAAuBC,cAAc;IAC9C,MAAM;IACN,UAASC,SAAoB,EAAEC,SAAqC,CAAC,CAAC;QAGlEC,6BAA6BD,OAAO,aAAa,EAAE,QAAQ,CAACD;QAG5DG,kBAAkB,QAAQ,CAACH;QAC3BI,4BAA4B,QAAQ,CAACJ;QACrCK,uBAAuB,QAAQ,CAACL;QAChCM,yBAAyB,QAAQ,CAACN;QAClCO,2BAA2B,QAAQ,CAACP;QACpCQ,4BAA4B,QAAQ,CAACR;QAIrCA,UAAU,QAAQ,CAACS;QAMnBT,UAAU,gBAAgB,CAACU,2BAA2B;YAClD,MAAM,MAAKC,GAAwB;gBAC/B,MAAMC,MAAMD,IAAI,SAAS,CAAC,OAAO,CAACE;gBAClC,IAAID,IAAI,gCAAgC,IACpCE,2BAA2B,QAAQ,CAACH,IAAI,SAAS;YAEzD;QACJ;IACJ;AACJ"}
1
+ {"version":3,"file":"FileManagerS3Feature.js","sources":["../src/FileManagerS3Feature.ts"],"sourcesContent":["import { type Container, createFeature } from \"@webiny/feature/api\";\nimport { S3GraphQLSchema } from \"./graphql/S3GraphQLSchema.js\";\nimport { DeleteFileFromBucketFeature } from \"~/features/DeleteFileFromBucket/feature.js\";\nimport { WriteFileMetadataFeature } from \"~/features/WriteFileMetadata/feature.js\";\nimport { ApplyThreatScanningFeature } from \"~/enterprise/ApplyThreatScanning/feature.js\";\nimport { FlushCacheFeature } from \"~/features/FlushCache/feature.js\";\nimport { ExtractMetadataFeature } from \"~/features/ExtractMetadata/feature.js\";\nimport { GetFileContentsByIdFeature } from \"~/features/GetFileContentsById/feature.js\";\nimport { GetFileContentsByKeyFeature } from \"~/features/GetFileContentsByKey/feature.js\";\nimport { createS3AssetDeliveryFeature } from \"./assetDelivery/feature.js\";\nimport type { AssetDeliveryParams } from \"./assetDelivery/types.js\";\n\nexport interface FileManagerS3FeatureConfig {\n assetDelivery?: AssetDeliveryParams;\n}\n\nexport const FileManagerS3Feature = createFeature({\n name: \"FileManagerS3\",\n register(container: Container, config: FileManagerS3FeatureConfig = {}) {\n // Register S3-specific asset delivery implementations (S3AssetResolver, S3OutputStrategy).\n // These replace the null implementations from AssetDeliveryFeature in FileManagerAppFeature.\n createS3AssetDeliveryFeature(config.assetDelivery).register(container);\n\n // S3 file-operation features.\n FlushCacheFeature.register(container);\n DeleteFileFromBucketFeature.register(container);\n ExtractMetadataFeature.register(container);\n WriteFileMetadataFeature.register(container);\n GetFileContentsByIdFeature.register(container);\n GetFileContentsByKeyFeature.register(container);\n\n // Static S3 GraphQL schema (extends FmQuery/FmMutation) — a DI-native\n // CoreGraphQLSchemaFactory contributor (declares its resolver dependencies).\n container.register(S3GraphQLSchema);\n\n // Threat scanning is WCP-gated, but the license is per-request (loaded post-register). Register\n // unconditionally; CreateFileWithThreatScanDecorator + the GuardDuty event handler both guard\n // on canUseFileManagerThreatDetection() at request time and no-op/pass-through when unlicensed.\n ApplyThreatScanningFeature.register(container);\n }\n});\n"],"names":["FileManagerS3Feature","createFeature","container","config","createS3AssetDeliveryFeature","FlushCacheFeature","DeleteFileFromBucketFeature","ExtractMetadataFeature","WriteFileMetadataFeature","GetFileContentsByIdFeature","GetFileContentsByKeyFeature","S3GraphQLSchema","ApplyThreatScanningFeature"],"mappings":";;;;;;;;;;AAgBO,MAAMA,uBAAuBC,cAAc;IAC9C,MAAM;IACN,UAASC,SAAoB,EAAEC,SAAqC,CAAC,CAAC;QAGlEC,6BAA6BD,OAAO,aAAa,EAAE,QAAQ,CAACD;QAG5DG,kBAAkB,QAAQ,CAACH;QAC3BI,4BAA4B,QAAQ,CAACJ;QACrCK,uBAAuB,QAAQ,CAACL;QAChCM,yBAAyB,QAAQ,CAACN;QAClCO,2BAA2B,QAAQ,CAACP;QACpCQ,4BAA4B,QAAQ,CAACR;QAIrCA,UAAU,QAAQ,CAACS;QAKnBC,2BAA2B,QAAQ,CAACV;IACxC;AACJ"}
@@ -1,7 +1,9 @@
1
1
  import type { S3 } from "@webiny/aws-sdk/client-s3/index.js";
2
+ import type { ImageFormat } from "@webiny/api-file-manager/features/assetDelivery/transformation/index.js";
2
3
  export interface IS3AssetDeliveryConfig {
3
4
  presignedUrlTtl: number;
4
5
  imageResizeWidths: number[];
6
+ imageQuality?: Partial<Record<ImageFormat, number>>;
5
7
  assetStreamingMaxSize: number;
6
8
  }
7
9
  export declare const S3AssetDeliveryConfig: import("@webiny/di").Abstraction<IS3AssetDeliveryConfig>;
@@ -1 +1 @@
1
- {"version":3,"file":"assetDelivery/abstractions.js","sources":["../../src/assetDelivery/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\n\nexport interface IS3AssetDeliveryConfig {\n presignedUrlTtl: number;\n imageResizeWidths: number[];\n assetStreamingMaxSize: number;\n}\n\nexport const S3AssetDeliveryConfig =\n createAbstraction<IS3AssetDeliveryConfig>(\"AssetDelivery/S3Config\");\n\nexport const S3Client = createAbstraction<S3>(\"AssetDelivery/S3Client\");\n\nexport const S3Bucket = createAbstraction<string>(\"AssetDelivery/S3Bucket\");\n"],"names":["S3AssetDeliveryConfig","createAbstraction","S3Client","S3Bucket"],"mappings":";AASO,MAAMA,wBACTC,kBAA0C;AAEvC,MAAMC,WAAWD,kBAAsB;AAEvC,MAAME,WAAWF,kBAA0B"}
1
+ {"version":3,"file":"assetDelivery/abstractions.js","sources":["../../src/assetDelivery/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport type { ImageFormat } from \"@webiny/api-file-manager/features/assetDelivery/transformation/index.js\";\n\nexport interface IS3AssetDeliveryConfig {\n presignedUrlTtl: number;\n imageResizeWidths: number[];\n imageQuality?: Partial<Record<ImageFormat, number>>;\n assetStreamingMaxSize: number;\n}\n\nexport const S3AssetDeliveryConfig =\n createAbstraction<IS3AssetDeliveryConfig>(\"AssetDelivery/S3Config\");\n\nexport const S3Client = createAbstraction<S3>(\"AssetDelivery/S3Client\");\n\nexport const S3Bucket = createAbstraction<string>(\"AssetDelivery/S3Bucket\");\n"],"names":["S3AssetDeliveryConfig","createAbstraction","S3Client","S3Bucket"],"mappings":";AAWO,MAAMA,wBACTC,kBAA0C;AAEvC,MAAMC,WAAWD,kBAAsB;AAEvC,MAAME,WAAWF,kBAA0B"}
@@ -3,7 +3,7 @@ import { S3 } from "@webiny/aws-sdk/client-s3/index.js";
3
3
  import { S3AssetDeliveryConfig, S3Bucket, S3Client } from "./abstractions.js";
4
4
  import { S3AssetResolverImpl } from "./s3/S3AssetResolver.js";
5
5
  import { S3OutputStrategyImpl } from "./s3/S3OutputStrategy.js";
6
- import { SharpTransformImpl } from "./s3/SharpTransform.js";
6
+ import { LazySharpTransformImpl } from "./s3/LazySharpTransform.js";
7
7
  const createS3AssetDeliveryFeature = (params = {})=>createFeature({
8
8
  name: "AssetDelivery/S3",
9
9
  register (container) {
@@ -25,11 +25,12 @@ const createS3AssetDeliveryFeature = (params = {})=>createFeature({
25
25
  2048,
26
26
  3840
27
27
  ],
28
+ imageQuality: params.imageQuality ?? {},
28
29
  assetStreamingMaxSize: params.assetStreamingMaxSize ?? 4718592
29
30
  });
30
31
  container.register(S3AssetResolverImpl);
31
32
  container.register(S3OutputStrategyImpl);
32
- container.register(SharpTransformImpl);
33
+ if ("asset-delivery" === process.env.WEBINY_FUNCTION_TYPE) container.register(LazySharpTransformImpl).inSingletonScope();
33
34
  }
34
35
  });
35
36
  export { createS3AssetDeliveryFeature };
@@ -1 +1 @@
1
- {"version":3,"file":"assetDelivery/feature.js","sources":["../../src/assetDelivery/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport { S3Client, S3Bucket, S3AssetDeliveryConfig } from \"./abstractions.js\";\nimport type { AssetDeliveryParams } from \"./types.js\";\nimport { S3AssetResolverImpl } from \"./s3/S3AssetResolver.js\";\nimport { S3OutputStrategyImpl } from \"./s3/S3OutputStrategy.js\";\nimport { SharpTransformImpl } from \"./s3/SharpTransform.js\";\n\nexport const createS3AssetDeliveryFeature = (params: AssetDeliveryParams = {}) => {\n return createFeature({\n name: \"AssetDelivery/S3\",\n register(container) {\n container.registerInstance(\n S3Client,\n new S3({ region: process.env.AWS_REGION as string })\n );\n container.registerInstance(S3Bucket, process.env.S3_BUCKET as string);\n container.registerInstance(S3AssetDeliveryConfig, {\n presignedUrlTtl: params.presignedUrlTtl ?? 3600,\n imageResizeWidths: params.imageResizeWidths ?? [\n 128, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840\n ],\n assetStreamingMaxSize: params.assetStreamingMaxSize ?? 4718592\n });\n\n container.register(S3AssetResolverImpl);\n container.register(S3OutputStrategyImpl);\n container.register(SharpTransformImpl);\n }\n });\n};\n"],"names":["createS3AssetDeliveryFeature","params","createFeature","container","S3Client","S3","process","S3Bucket","S3AssetDeliveryConfig","S3AssetResolverImpl","S3OutputStrategyImpl","SharpTransformImpl"],"mappings":";;;;;;AAQO,MAAMA,+BAA+B,CAACC,SAA8B,CAAC,CAAC,GAClEC,cAAc;QACjB,MAAM;QACN,UAASC,SAAS;YACdA,UAAU,gBAAgB,CACtBC,UACA,IAAIC,GAAG;gBAAE,QAAQC,QAAQ,GAAG,CAAC,UAAU;YAAW;YAEtDH,UAAU,gBAAgB,CAACI,UAAUD,QAAQ,GAAG,CAAC,SAAS;YAC1DH,UAAU,gBAAgB,CAACK,uBAAuB;gBAC9C,iBAAiBP,OAAO,eAAe,IAAI;gBAC3C,mBAAmBA,OAAO,iBAAiB,IAAI;oBAC3C;oBAAK;oBAAK;oBAAK;oBAAK;oBAAK;oBAAM;oBAAM;oBAAM;oBAAM;iBACpD;gBACD,uBAAuBA,OAAO,qBAAqB,IAAI;YAC3D;YAEAE,UAAU,QAAQ,CAACM;YACnBN,UAAU,QAAQ,CAACO;YACnBP,UAAU,QAAQ,CAACQ;QACvB;IACJ"}
1
+ {"version":3,"file":"assetDelivery/feature.js","sources":["../../src/assetDelivery/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport { S3Client, S3Bucket, S3AssetDeliveryConfig } from \"./abstractions.js\";\nimport type { AssetDeliveryParams } from \"./types.js\";\nimport { S3AssetResolverImpl } from \"./s3/S3AssetResolver.js\";\nimport { S3OutputStrategyImpl } from \"./s3/S3OutputStrategy.js\";\nimport { LazySharpTransformImpl } from \"./s3/LazySharpTransform.js\";\n\nexport const createS3AssetDeliveryFeature = (params: AssetDeliveryParams = {}) => {\n return createFeature({\n name: \"AssetDelivery/S3\",\n register(container) {\n container.registerInstance(\n S3Client,\n new S3({ region: process.env.AWS_REGION as string })\n );\n container.registerInstance(S3Bucket, process.env.S3_BUCKET as string);\n container.registerInstance(S3AssetDeliveryConfig, {\n presignedUrlTtl: params.presignedUrlTtl ?? 3600,\n imageResizeWidths: params.imageResizeWidths ?? [\n 128, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840\n ],\n imageQuality: params.imageQuality ?? {},\n assetStreamingMaxSize: params.assetStreamingMaxSize ?? 4718592\n });\n\n container.register(S3AssetResolverImpl);\n container.register(S3OutputStrategyImpl);\n\n if (process.env.WEBINY_FUNCTION_TYPE === \"asset-delivery\") {\n // Registered eagerly; `sharp` is still loaded lazily, inside the handler.\n container.register(LazySharpTransformImpl).inSingletonScope();\n }\n }\n });\n};\n"],"names":["createS3AssetDeliveryFeature","params","createFeature","container","S3Client","S3","process","S3Bucket","S3AssetDeliveryConfig","S3AssetResolverImpl","S3OutputStrategyImpl","LazySharpTransformImpl"],"mappings":";;;;;;AAQO,MAAMA,+BAA+B,CAACC,SAA8B,CAAC,CAAC,GAClEC,cAAc;QACjB,MAAM;QACN,UAASC,SAAS;YACdA,UAAU,gBAAgB,CACtBC,UACA,IAAIC,GAAG;gBAAE,QAAQC,QAAQ,GAAG,CAAC,UAAU;YAAW;YAEtDH,UAAU,gBAAgB,CAACI,UAAUD,QAAQ,GAAG,CAAC,SAAS;YAC1DH,UAAU,gBAAgB,CAACK,uBAAuB;gBAC9C,iBAAiBP,OAAO,eAAe,IAAI;gBAC3C,mBAAmBA,OAAO,iBAAiB,IAAI;oBAC3C;oBAAK;oBAAK;oBAAK;oBAAK;oBAAK;oBAAM;oBAAM;oBAAM;oBAAM;iBACpD;gBACD,cAAcA,OAAO,YAAY,IAAI,CAAC;gBACtC,uBAAuBA,OAAO,qBAAqB,IAAI;YAC3D;YAEAE,UAAU,QAAQ,CAACM;YACnBN,UAAU,QAAQ,CAACO;YAEnB,IAAIJ,AAAqC,qBAArCA,QAAQ,GAAG,CAAC,oBAAoB,EAEhCH,UAAU,QAAQ,CAACQ,wBAAwB,gBAAgB;QAEnE;IACJ"}
@@ -0,0 +1,34 @@
1
+ import type { S3 } from "@webiny/aws-sdk/client-s3/index.js";
2
+ import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js";
3
+ import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
4
+ import { ImageAssetTypeHandler } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js";
5
+ import type { Asset } from "@webiny/api-file-manager/delivery/AssetDelivery/Asset.js";
6
+ import type { AssetRequest } from "@webiny/api-file-manager/delivery/AssetDelivery/AssetRequest.js";
7
+ import type { IS3AssetDeliveryConfig } from "../../assetDelivery/abstractions.js";
8
+ /**
9
+ * Defers loading `sharp` until an image is actually transformed.
10
+ *
11
+ * The dynamic import is deliberate — it keeps `sharp` out of the main bundle (note the
12
+ * `webpackChunkName`). That import is async and DI resolution is not, which is why this used to be
13
+ * a per-request `RequestContextInitializer` that imported the module and then registered the real
14
+ * handler. `IAssetTypeHandler` has a single async method, so the import can be awaited there
15
+ * instead.
16
+ *
17
+ * The constructed handler IS memoized: the initializer registered it `.inSingletonScope()`, and
18
+ * nothing below caches the instance (Node caches the module, not the object built from it).
19
+ */
20
+ declare class LazySharpTransform implements ImageAssetTypeHandler.Interface {
21
+ private readonly s3;
22
+ private readonly bucket;
23
+ private readonly config;
24
+ private readonly identityContext;
25
+ private readonly getFile;
26
+ private handler;
27
+ constructor(s3: S3, bucket: string, config: IS3AssetDeliveryConfig, identityContext: IdentityContext.Interface, getFile: GetFileUseCase.Interface);
28
+ private resolveHandler;
29
+ handle(assetRequest: AssetRequest, asset: Asset): Promise<Asset>;
30
+ }
31
+ export declare const LazySharpTransformImpl: typeof LazySharpTransform & {
32
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-file-manager/features/assetDelivery/abstractions/AssetType.js").IAssetTypeHandler>;
33
+ };
34
+ export {};
@@ -0,0 +1,34 @@
1
+ import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js";
2
+ import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
3
+ import { ImageAssetTypeHandler } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js";
4
+ import { S3AssetDeliveryConfig, S3Bucket, S3Client } from "../abstractions.js";
5
+ class LazySharpTransform {
6
+ constructor(s3, bucket, config, identityContext, getFile){
7
+ this.s3 = s3;
8
+ this.bucket = bucket;
9
+ this.config = config;
10
+ this.identityContext = identityContext;
11
+ this.getFile = getFile;
12
+ this.handler = null;
13
+ }
14
+ resolveHandler() {
15
+ if (!this.handler) this.handler = import("./SharpTransform.js").then(({ SharpTransform })=>new SharpTransform(this.s3, this.bucket, this.config, this.identityContext, this.getFile));
16
+ return this.handler;
17
+ }
18
+ async handle(assetRequest, asset) {
19
+ return (await this.resolveHandler()).handle(assetRequest, asset);
20
+ }
21
+ }
22
+ const LazySharpTransformImpl = ImageAssetTypeHandler.createImplementation({
23
+ implementation: LazySharpTransform,
24
+ dependencies: [
25
+ S3Client,
26
+ S3Bucket,
27
+ S3AssetDeliveryConfig,
28
+ IdentityContext,
29
+ GetFileUseCase
30
+ ]
31
+ });
32
+ export { LazySharpTransformImpl };
33
+
34
+ //# sourceMappingURL=LazySharpTransform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assetDelivery/s3/LazySharpTransform.js","sources":["../../../src/assetDelivery/s3/LazySharpTransform.ts"],"sourcesContent":["import type { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport { IdentityContext } from \"@webiny/api-core/features/security/IdentityContext/index.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport { ImageAssetTypeHandler } from \"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js\";\nimport type { Asset } from \"@webiny/api-file-manager/delivery/AssetDelivery/Asset.js\";\nimport type { AssetRequest } from \"@webiny/api-file-manager/delivery/AssetDelivery/AssetRequest.js\";\nimport { S3AssetDeliveryConfig, S3Bucket, S3Client } from \"~/assetDelivery/abstractions.js\";\nimport type { IS3AssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\n\n/**\n * Defers loading `sharp` until an image is actually transformed.\n *\n * The dynamic import is deliberate — it keeps `sharp` out of the main bundle (note the\n * `webpackChunkName`). That import is async and DI resolution is not, which is why this used to be\n * a per-request `RequestContextInitializer` that imported the module and then registered the real\n * handler. `IAssetTypeHandler` has a single async method, so the import can be awaited there\n * instead.\n *\n * The constructed handler IS memoized: the initializer registered it `.inSingletonScope()`, and\n * nothing below caches the instance (Node caches the module, not the object built from it).\n */\nclass LazySharpTransform implements ImageAssetTypeHandler.Interface {\n private handler: Promise<ImageAssetTypeHandler.Interface> | null = null;\n\n constructor(\n private readonly s3: S3,\n private readonly bucket: string,\n private readonly config: IS3AssetDeliveryConfig,\n private readonly identityContext: IdentityContext.Interface,\n private readonly getFile: GetFileUseCase.Interface\n ) {}\n\n private resolveHandler(): Promise<ImageAssetTypeHandler.Interface> {\n if (!this.handler) {\n this.handler = import(\n /* webpackChunkName: \"s3AssetDelivery\" */ \"./SharpTransform.js\"\n ).then(\n ({ SharpTransform }) =>\n new SharpTransform(\n this.s3,\n this.bucket,\n this.config,\n this.identityContext,\n this.getFile\n )\n );\n }\n return this.handler;\n }\n\n async handle(assetRequest: AssetRequest, asset: Asset): Promise<Asset> {\n return (await this.resolveHandler()).handle(assetRequest, asset);\n }\n}\n\nexport const LazySharpTransformImpl = ImageAssetTypeHandler.createImplementation({\n implementation: LazySharpTransform,\n dependencies: [S3Client, S3Bucket, S3AssetDeliveryConfig, IdentityContext, GetFileUseCase]\n});\n"],"names":["LazySharpTransform","s3","bucket","config","identityContext","getFile","SharpTransform","assetRequest","asset","LazySharpTransformImpl","ImageAssetTypeHandler","S3Client","S3Bucket","S3AssetDeliveryConfig","IdentityContext","GetFileUseCase"],"mappings":";;;;AAqBA,MAAMA;IAGF,YACqBC,EAAM,EACNC,MAAc,EACdC,MAA8B,EAC9BC,eAA0C,EAC1CC,OAAiC,CACpD;aALmBJ,EAAE,GAAFA;aACAC,MAAM,GAANA;aACAC,MAAM,GAANA;aACAC,eAAe,GAAfA;aACAC,OAAO,GAAPA;aAPb,OAAO,GAAoD;IAQhE;IAEK,iBAA2D;QAC/D,IAAI,CAAC,IAAI,CAAC,OAAO,EACb,IAAI,CAAC,OAAO,GAAG,MAAM,CAAN,uBAEb,IAAI,CACF,CAAC,EAAEC,cAAc,EAAE,GACf,IAAIA,eACA,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,eAAe,EACpB,IAAI,CAAC,OAAO;QAI5B,OAAO,IAAI,CAAC,OAAO;IACvB;IAEA,MAAM,OAAOC,YAA0B,EAAEC,KAAY,EAAkB;QACnE,OAAQ,OAAM,IAAI,CAAC,cAAc,EAAC,EAAG,MAAM,CAACD,cAAcC;IAC9D;AACJ;AAEO,MAAMC,yBAAyBC,sBAAsB,oBAAoB,CAAC;IAC7E,gBAAgBV;IAChB,cAAc;QAACW;QAAUC;QAAUC;QAAuBC;QAAiBC;KAAe;AAC9F"}
@@ -1,18 +1,26 @@
1
1
  import type { S3 } from "@webiny/aws-sdk/client-s3/index.js";
2
- import type { Asset, AssetRequest, AssetTransformationStrategy } from "@webiny/api-file-manager";
2
+ import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js";
3
+ import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
4
+ import { ImageAssetTypeHandler } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js";
5
+ import type { Asset } from "@webiny/api-file-manager/delivery/AssetDelivery/Asset.js";
6
+ import type { AssetRequest } from "@webiny/api-file-manager/delivery/AssetDelivery/AssetRequest.js";
3
7
  import type { IS3AssetDeliveryConfig } from "../../assetDelivery/abstractions.js";
4
- export declare class SharpTransform implements AssetTransformationStrategy {
8
+ export declare class SharpTransform implements ImageAssetTypeHandler.Interface {
5
9
  private readonly s3;
6
10
  private readonly bucket;
7
11
  private readonly imageResizeWidths;
8
- constructor(s3: S3, bucket: string, config: IS3AssetDeliveryConfig);
9
- transform(assetRequest: AssetRequest, asset: Asset): Promise<Asset>;
12
+ private readonly imageQuality;
13
+ private readonly identityContext;
14
+ private readonly getFile;
15
+ private transformer?;
16
+ constructor(s3: S3, bucket: string, config: IS3AssetDeliveryConfig, identityContext: IdentityContext.Interface, getFile: GetFileUseCase.Interface);
17
+ private getTransformer;
18
+ handle(assetRequest: AssetRequest, asset: Asset): Promise<Asset>;
19
+ private loadAssetCrop;
10
20
  private transformAsset;
11
21
  private optimizeAsset;
12
22
  private isAssetAnimated;
13
- private optimizePng;
14
- private optimizeJpeg;
15
23
  }
16
24
  export declare const SharpTransformImpl: typeof SharpTransform & {
17
- __abstraction: import("@webiny/di").Abstraction<AssetTransformationStrategy>;
25
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-file-manager/features/assetDelivery/abstractions/AssetType").IAssetTypeHandler>;
18
26
  };
@@ -1,90 +1,110 @@
1
- import { AssetTransformationStrategy } from "@webiny/api-file-manager/features/assetDelivery/abstractions.js";
2
- import { WidthCollection } from "./transformation/WidthCollection.js";
3
- import { CallableContentsReader } from "./transformation/CallableContentsReader.js";
4
- import { AssetKeyGenerator } from "./transformation/AssetKeyGenerator.js";
1
+ import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js";
2
+ import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
3
+ import { CallableContentsReader, DEFAULT_IMAGE_QUALITY, contentTypeForFormat } from "@webiny/api-file-manager/features/assetDelivery/transformation/index.js";
4
+ import { normalizeImageOptions } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/normalizeImageOptions.js";
5
+ import { AssetKeyGenerator, ImageAssetTypeHandler } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js";
5
6
  import { S3AssetDeliveryConfig, S3Bucket, S3Client } from "../abstractions.js";
6
- import * as __rspack_external__transformation_utils_js_9257ec9a from "./transformation/utils.js";
7
- let sharpCache;
8
- async function loadSharp() {
9
- if (!sharpCache) sharpCache = (await import("sharp")).default;
10
- return sharpCache;
11
- }
7
+ const hasTransform = (options)=>void 0 !== options.width || void 0 !== options.format || void 0 !== options.quality;
12
8
  class SharpTransform {
13
- constructor(s3, bucket, config){
9
+ constructor(s3, bucket, config, identityContext, getFile){
14
10
  this.s3 = s3;
15
11
  this.bucket = bucket;
16
12
  this.imageResizeWidths = config.imageResizeWidths;
13
+ this.imageQuality = {
14
+ ...DEFAULT_IMAGE_QUALITY,
15
+ ...config.imageQuality ?? {}
16
+ };
17
+ this.identityContext = identityContext;
18
+ this.getFile = getFile;
17
19
  }
18
- async transform(assetRequest, asset) {
19
- if (!__rspack_external__transformation_utils_js_9257ec9a.SUPPORTED_TRANSFORMABLE_IMAGES.includes(asset.getExtension())) {
20
- console.log(`Transformations/optimizations of ${asset.getContentType()} assets are not supported. Skipping.`);
21
- return asset;
20
+ async getTransformer() {
21
+ if (!this.transformer) {
22
+ const { SharpTransformer } = await import("@webiny/api-file-manager/features/assetDelivery/assetTypes/image/SharpTransformer.js");
23
+ this.transformer = new SharpTransformer();
22
24
  }
23
- const { original, ...options } = assetRequest.getOptions();
25
+ return this.transformer;
26
+ }
27
+ async handle(assetRequest, asset) {
28
+ const rawQuery = assetRequest.getOptions();
29
+ const acceptHeader = assetRequest.getContext().accept;
30
+ const { original, crop, focal, aspectRatio, ...options } = normalizeImageOptions(rawQuery, acceptHeader);
31
+ const assetCrop = await this.loadAssetCrop(asset.getId());
32
+ const framing = {
33
+ crop: crop ?? assetCrop,
34
+ focal,
35
+ aspectRatio
36
+ };
24
37
  const transformedAsset = asset.clone();
25
- if (Object.keys(options).length > 0) return this.transformAsset(transformedAsset, options);
26
- return this.optimizeAsset(transformedAsset);
38
+ if (hasTransform(options)) return this.transformAsset(transformedAsset, options, framing);
39
+ return this.optimizeAsset(transformedAsset, framing);
27
40
  }
28
- async transformAsset(asset, options) {
29
- if (options.width) {
30
- const assetKey = new AssetKeyGenerator(asset);
31
- const transformedAssetKey = assetKey.getTransformedImageKey(options);
32
- try {
33
- const { Body } = await this.s3.getObject({
34
- Bucket: this.bucket,
35
- Key: transformedAssetKey
36
- });
37
- if (!Body) throw new Error("Missing image body!");
38
- const buffer = Buffer.from(await Body.transformToByteArray());
39
- const newAsset = asset.withProps({
40
- size: buffer.length
41
- });
42
- newAsset.setContentsReader(new CallableContentsReader(()=>buffer));
43
- console.log("Return a previously transformed asset", {
44
- key: transformedAssetKey,
45
- size: newAsset.getSize()
46
- });
47
- return newAsset;
48
- } catch {
49
- const optimizedImage = await this.optimizeAsset(asset);
50
- const widths = new WidthCollection(this.imageResizeWidths);
51
- const width = widths.getClosestOrMax(options.width);
52
- console.log(`Resize the asset (width: ${width})`);
53
- const buffer = await optimizedImage.getContents();
54
- const sharp = await loadSharp();
55
- const transformedBuffer = await sharp(buffer, {
56
- animated: this.isAssetAnimated(asset)
57
- }).withMetadata().resize({
58
- width,
59
- withoutEnlargement: true
60
- }).toBuffer();
61
- const newAsset = asset.withProps({
62
- size: transformedBuffer.length
63
- });
64
- newAsset.setContentsReader(new CallableContentsReader(()=>transformedBuffer));
65
- await this.s3.putObject({
66
- Bucket: this.bucket,
67
- Key: transformedAssetKey,
68
- ContentType: newAsset.getContentType(),
69
- Body: await newAsset.getContents()
70
- });
71
- console.log("Return the resized asset", {
72
- key: transformedAssetKey,
73
- size: newAsset.getSize()
74
- });
75
- return newAsset;
76
- }
41
+ async loadAssetCrop(fileId) {
42
+ const result = await this.identityContext.withoutAuthorization(()=>this.getFile.execute(fileId));
43
+ if (result.isFail()) return;
44
+ const imageEdit = result.value.metadata?.imageEdit;
45
+ return imageEdit?.crop;
46
+ }
47
+ async transformAsset(asset, options, framing) {
48
+ const assetKey = AssetKeyGenerator.create(asset, framing);
49
+ const transformedAssetKey = assetKey.getTransformedImageKey(options);
50
+ const contentType = options.format ? contentTypeForFormat(options.format) : asset.getContentType();
51
+ try {
52
+ const { Body } = await this.s3.getObject({
53
+ Bucket: this.bucket,
54
+ Key: transformedAssetKey
55
+ });
56
+ if (!Body) throw new Error("Missing image body!");
57
+ const buffer = Buffer.from(await Body.transformToByteArray());
58
+ const newAsset = asset.withProps({
59
+ size: buffer.length,
60
+ contentType
61
+ });
62
+ newAsset.setContentsReader(CallableContentsReader.create(()=>buffer));
63
+ console.log("Return a previously transformed asset", {
64
+ key: transformedAssetKey,
65
+ size: newAsset.getSize()
66
+ });
67
+ return newAsset;
68
+ } catch {
69
+ const optimizedImage = await this.optimizeAsset(asset, framing);
70
+ console.log("Transform the asset", options);
71
+ const baseBuffer = await optimizedImage.getContents();
72
+ const transformer = await this.getTransformer();
73
+ const { buffer: transformedBuffer, contentType: outputContentType } = await transformer.transformBuffer({
74
+ buffer: baseBuffer,
75
+ animated: this.isAssetAnimated(asset),
76
+ sourceContentType: asset.getContentType(),
77
+ widths: this.imageResizeWidths,
78
+ options,
79
+ qualityDefaults: this.imageQuality
80
+ });
81
+ const newAsset = asset.withProps({
82
+ size: transformedBuffer.length,
83
+ contentType: outputContentType
84
+ });
85
+ newAsset.setContentsReader(CallableContentsReader.create(()=>transformedBuffer));
86
+ await this.s3.putObject({
87
+ Bucket: this.bucket,
88
+ Key: transformedAssetKey,
89
+ ContentType: newAsset.getContentType(),
90
+ Body: await newAsset.getContents()
91
+ });
92
+ console.log("Return the transformed asset", {
93
+ key: transformedAssetKey,
94
+ size: newAsset.getSize(),
95
+ contentType: newAsset.getContentType()
96
+ });
97
+ return newAsset;
77
98
  }
78
- return asset;
79
99
  }
80
- async optimizeAsset(asset) {
100
+ async optimizeAsset(asset, framing) {
81
101
  console.log("Optimize asset", {
82
102
  id: asset.getId(),
83
103
  key: asset.getKey(),
84
104
  size: asset.getSize(),
85
105
  type: asset.getContentType()
86
106
  });
87
- const assetKey = new AssetKeyGenerator(asset);
107
+ const assetKey = AssetKeyGenerator.create(asset, framing);
88
108
  const optimizedAssetKey = assetKey.getOptimizedImageKey();
89
109
  try {
90
110
  const { Body } = await this.s3.getObject({
@@ -97,27 +117,31 @@ class SharpTransform {
97
117
  const newAsset = asset.withProps({
98
118
  size: buffer.length
99
119
  });
100
- newAsset.setContentsReader(new CallableContentsReader(()=>buffer));
120
+ newAsset.setContentsReader(CallableContentsReader.create(()=>buffer));
101
121
  return newAsset;
102
122
  } catch {
103
123
  console.log("Create an optimized version of the original asset", asset.getKey());
104
- const buffer = await asset.getContents();
105
- const optimizationMap = {
106
- "image/png": (buffer)=>this.optimizePng(buffer),
107
- "image/jpeg": (buffer)=>this.optimizeJpeg(buffer),
108
- "image/jpg": (buffer)=>this.optimizeJpeg(buffer)
109
- };
110
- const optimization = optimizationMap[asset.getContentType()];
111
- if (!optimization) {
112
- console.log(`No optimizations defined for ${asset.getContentType()}`);
124
+ let buffer = await asset.getContents();
125
+ const transformer = await this.getTransformer();
126
+ const framed = await transformer.extractFramedRegion(buffer, framing);
127
+ const cropped = framed !== buffer;
128
+ buffer = framed;
129
+ const contentType = asset.getContentType();
130
+ const canOptimize = "image/png" === contentType || "image/jpeg" === contentType || "image/jpg" === contentType;
131
+ if (!canOptimize && !cropped) {
132
+ console.log(`No optimizations defined for ${contentType}`);
113
133
  return asset;
114
134
  }
115
- const optimizedBuffer = await optimization(buffer);
116
- console.log("Optimized asset size", optimizedBuffer.length);
135
+ let finalBuffer = buffer;
136
+ if (canOptimize) {
137
+ const pipeline = "image/png" === contentType ? await transformer.optimizePng(buffer) : await transformer.optimizeJpeg(buffer);
138
+ finalBuffer = await pipeline.toBuffer();
139
+ }
140
+ console.log("Optimized asset size", finalBuffer.length);
117
141
  const newAsset = asset.withProps({
118
- size: optimizedBuffer.length
142
+ size: finalBuffer.length
119
143
  });
120
- newAsset.setContentsReader(new CallableContentsReader(()=>optimizedBuffer));
144
+ newAsset.setContentsReader(CallableContentsReader.create(()=>finalBuffer));
121
145
  await this.s3.putObject({
122
146
  Bucket: this.bucket,
123
147
  Key: optimizedAssetKey,
@@ -133,35 +157,15 @@ class SharpTransform {
133
157
  "webp"
134
158
  ].includes(asset.getExtension());
135
159
  }
136
- async optimizePng(buffer) {
137
- const sharp = await loadSharp();
138
- return sharp(buffer).resize({
139
- width: 2560,
140
- withoutEnlargement: true,
141
- fit: "inside"
142
- }).png({
143
- compressionLevel: 9,
144
- adaptiveFiltering: true,
145
- force: true
146
- }).withMetadata().toBuffer();
147
- }
148
- async optimizeJpeg(buffer) {
149
- const sharp = await loadSharp();
150
- return sharp(buffer).resize({
151
- width: 2560,
152
- withoutEnlargement: true,
153
- fit: "inside"
154
- }).withMetadata().toFormat("jpeg", {
155
- quality: 90
156
- }).toBuffer();
157
- }
158
160
  }
159
- const SharpTransformImpl = AssetTransformationStrategy.createImplementation({
161
+ const SharpTransformImpl = ImageAssetTypeHandler.createImplementation({
160
162
  implementation: SharpTransform,
161
163
  dependencies: [
162
164
  S3Client,
163
165
  S3Bucket,
164
- S3AssetDeliveryConfig
166
+ S3AssetDeliveryConfig,
167
+ IdentityContext,
168
+ GetFileUseCase
165
169
  ]
166
170
  });
167
171
  export { SharpTransform, SharpTransformImpl };
@@ -1 +1 @@
1
- {"version":3,"file":"assetDelivery/s3/SharpTransform.js","sources":["../../../src/assetDelivery/s3/SharpTransform.ts"],"sourcesContent":["import type { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport type {\n Asset,\n AssetRequest,\n AssetRequestOptions,\n AssetTransformationStrategy\n} from \"@webiny/api-file-manager\";\nimport { AssetTransformationStrategy as AssetTransformationStrategyAbstraction } from \"@webiny/api-file-manager/features/assetDelivery/abstractions.js\";\nimport { WidthCollection } from \"./transformation/WidthCollection.js\";\nimport * as utils from \"./transformation/utils.js\";\nimport { CallableContentsReader } from \"./transformation/CallableContentsReader.js\";\nimport { AssetKeyGenerator } from \"./transformation/AssetKeyGenerator.js\";\nimport { S3Client, S3Bucket, S3AssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\nimport type { IS3AssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\n\nimport type sharpType from \"sharp\";\ntype SharpFn = typeof sharpType;\nlet sharpCache: SharpFn | undefined;\nasync function loadSharp(): Promise<SharpFn> {\n if (!sharpCache) {\n sharpCache = (await import(\"sharp\")).default as SharpFn;\n }\n return sharpCache;\n}\n\nexport class SharpTransform implements AssetTransformationStrategy {\n private readonly s3: S3;\n private readonly bucket: string;\n private readonly imageResizeWidths: number[];\n\n constructor(s3: S3, bucket: string, config: IS3AssetDeliveryConfig) {\n this.s3 = s3;\n this.bucket = bucket;\n this.imageResizeWidths = config.imageResizeWidths;\n }\n\n async transform(assetRequest: AssetRequest, asset: Asset): Promise<Asset> {\n if (!utils.SUPPORTED_TRANSFORMABLE_IMAGES.includes(asset.getExtension())) {\n console.log(\n `Transformations/optimizations of ${asset.getContentType()} assets are not supported. Skipping.`\n );\n return asset;\n }\n\n // oxlint-disable-next-line typescript/no-unused-vars\n const { original, ...options } = assetRequest.getOptions();\n\n const transformedAsset = asset.clone();\n\n if (Object.keys(options).length > 0) {\n return this.transformAsset(transformedAsset, options);\n }\n\n return this.optimizeAsset(transformedAsset);\n }\n\n private async transformAsset(asset: Asset, options: Omit<AssetRequestOptions, \"original\">) {\n if (options.width) {\n const assetKey = new AssetKeyGenerator(asset);\n const transformedAssetKey = assetKey.getTransformedImageKey(options);\n\n try {\n const { Body } = await this.s3.getObject({\n Bucket: this.bucket,\n Key: transformedAssetKey\n });\n\n if (!Body) {\n throw new Error(`Missing image body!`);\n }\n\n const buffer = Buffer.from(await Body.transformToByteArray());\n\n const newAsset = asset.withProps({ size: buffer.length });\n newAsset.setContentsReader(new CallableContentsReader(() => buffer));\n\n console.log(`Return a previously transformed asset`, {\n key: transformedAssetKey,\n size: newAsset.getSize()\n });\n\n return newAsset;\n } catch {\n const optimizedImage = await this.optimizeAsset(asset);\n\n const widths = new WidthCollection(this.imageResizeWidths);\n const width = widths.getClosestOrMax(options.width);\n\n console.log(`Resize the asset (width: ${width})`);\n const buffer = await optimizedImage.getContents();\n const sharp = await loadSharp();\n const transformedBuffer = await sharp(buffer, {\n animated: this.isAssetAnimated(asset)\n })\n .withMetadata()\n .resize({ width, withoutEnlargement: true })\n .toBuffer();\n\n const newAsset = asset.withProps({ size: transformedBuffer.length });\n newAsset.setContentsReader(new CallableContentsReader(() => transformedBuffer));\n\n await this.s3.putObject({\n Bucket: this.bucket,\n Key: transformedAssetKey,\n ContentType: newAsset.getContentType(),\n Body: await newAsset.getContents()\n });\n\n console.log(`Return the resized asset`, {\n key: transformedAssetKey,\n size: newAsset.getSize()\n });\n\n return newAsset;\n }\n }\n\n return asset;\n }\n\n private async optimizeAsset(asset: Asset) {\n console.log(\"Optimize asset\", {\n id: asset.getId(),\n key: asset.getKey(),\n size: asset.getSize(),\n type: asset.getContentType()\n });\n\n const assetKey = new AssetKeyGenerator(asset);\n const optimizedAssetKey = assetKey.getOptimizedImageKey();\n\n try {\n const { Body } = await this.s3.getObject({\n Bucket: this.bucket,\n Key: optimizedAssetKey\n });\n\n if (!Body) {\n throw new Error(`Missing image body!`);\n }\n\n console.log(\"Return a previously optimized asset\", optimizedAssetKey);\n\n const buffer = Buffer.from(await Body.transformToByteArray());\n\n const newAsset = asset.withProps({ size: buffer.length });\n newAsset.setContentsReader(new CallableContentsReader(() => buffer));\n\n return newAsset;\n } catch {\n console.log(\"Create an optimized version of the original asset\", asset.getKey());\n const buffer = await asset.getContents();\n\n const optimizationMap: Record<\n string,\n ((buffer: Buffer) => Promise<Buffer>) | undefined\n > = {\n \"image/png\": (buffer: Buffer) => this.optimizePng(buffer),\n \"image/jpeg\": (buffer: Buffer) => this.optimizeJpeg(buffer),\n \"image/jpg\": (buffer: Buffer) => this.optimizeJpeg(buffer)\n };\n\n const optimization = optimizationMap[asset.getContentType()];\n\n if (!optimization) {\n console.log(`No optimizations defined for ${asset.getContentType()}`);\n return asset;\n }\n\n const optimizedBuffer = await optimization(buffer);\n\n console.log(\"Optimized asset size\", optimizedBuffer.length);\n\n const newAsset = asset.withProps({ size: optimizedBuffer.length });\n newAsset.setContentsReader(new CallableContentsReader(() => optimizedBuffer));\n\n await this.s3.putObject({\n Bucket: this.bucket,\n Key: optimizedAssetKey,\n ContentType: newAsset.getContentType(),\n Body: await newAsset.getContents()\n });\n\n return newAsset;\n }\n }\n\n private isAssetAnimated(asset: Asset) {\n return [\"gif\", \"webp\"].includes(asset.getExtension());\n }\n\n private async optimizePng(buffer: Buffer): Promise<Buffer> {\n const sharp = await loadSharp();\n return sharp(buffer)\n .resize({ width: 2560, withoutEnlargement: true, fit: \"inside\" })\n .png({ compressionLevel: 9, adaptiveFiltering: true, force: true })\n .withMetadata()\n .toBuffer();\n }\n\n private async optimizeJpeg(buffer: Buffer): Promise<Buffer> {\n const sharp = await loadSharp();\n return sharp(buffer)\n .resize({ width: 2560, withoutEnlargement: true, fit: \"inside\" })\n .withMetadata()\n .toFormat(\"jpeg\", { quality: 90 })\n .toBuffer();\n }\n}\n\nexport const SharpTransformImpl = AssetTransformationStrategyAbstraction.createImplementation({\n implementation: SharpTransform,\n dependencies: [S3Client, S3Bucket, S3AssetDeliveryConfig]\n});\n"],"names":["sharpCache","loadSharp","SharpTransform","s3","bucket","config","assetRequest","asset","utils","console","original","options","transformedAsset","Object","assetKey","AssetKeyGenerator","transformedAssetKey","Body","Error","buffer","Buffer","newAsset","CallableContentsReader","optimizedImage","widths","WidthCollection","width","sharp","transformedBuffer","optimizedAssetKey","optimizationMap","optimization","optimizedBuffer","SharpTransformImpl","AssetTransformationStrategyAbstraction","S3Client","S3Bucket","S3AssetDeliveryConfig"],"mappings":"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"}
1
+ {"version":3,"file":"assetDelivery/s3/SharpTransform.js","sources":["../../../src/assetDelivery/s3/SharpTransform.ts"],"sourcesContent":["import type { S3 } from \"@webiny/aws-sdk/client-s3/index.js\";\nimport { IdentityContext } from \"@webiny/api-core/features/security/IdentityContext/index.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport {\n contentTypeForFormat,\n DEFAULT_IMAGE_QUALITY,\n type ImageFormat\n} from \"@webiny/api-file-manager/features/assetDelivery/transformation/index.js\";\nimport type {\n AssetImageEdit,\n Framing,\n ImageRequestOptions\n} from \"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/imageTypes.js\";\nimport { normalizeImageOptions } from \"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/normalizeImageOptions.js\";\nimport { CallableContentsReader } from \"@webiny/api-file-manager/features/assetDelivery/transformation/index.js\";\nimport { AssetKeyGenerator } from \"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js\";\nimport { ImageAssetTypeHandler } from \"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js\";\nimport type { Asset } from \"@webiny/api-file-manager/delivery/AssetDelivery/Asset.js\";\nimport type { AssetRequest } from \"@webiny/api-file-manager/delivery/AssetDelivery/AssetRequest.js\";\nimport type { SharpTransformer } from \"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/SharpTransformer.js\";\nimport { S3Client, S3Bucket, S3AssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\nimport type { IS3AssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\n\ntype TransformOptions = Omit<ImageRequestOptions, \"original\" | \"crop\" | \"focal\" | \"aspectRatio\">;\n\nconst hasTransform = (options: TransformOptions): boolean => {\n return (\n options.width !== undefined || options.format !== undefined || options.quality !== undefined\n );\n};\n\nexport class SharpTransform implements ImageAssetTypeHandler.Interface {\n private readonly s3: S3;\n private readonly bucket: string;\n private readonly imageResizeWidths: number[];\n private readonly imageQuality: Record<ImageFormat, number>;\n private readonly identityContext: IdentityContext.Interface;\n private readonly getFile: GetFileUseCase.Interface;\n private transformer?: SharpTransformer;\n\n constructor(\n s3: S3,\n bucket: string,\n config: IS3AssetDeliveryConfig,\n identityContext: IdentityContext.Interface,\n getFile: GetFileUseCase.Interface\n ) {\n this.s3 = s3;\n this.bucket = bucket;\n this.imageResizeWidths = config.imageResizeWidths;\n this.imageQuality = { ...DEFAULT_IMAGE_QUALITY, ...(config.imageQuality ?? {}) };\n this.identityContext = identityContext;\n this.getFile = getFile;\n }\n\n private async getTransformer(): Promise<SharpTransformer> {\n if (!this.transformer) {\n const { SharpTransformer } =\n await import(\"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/SharpTransformer.js\");\n this.transformer = new SharpTransformer();\n }\n return this.transformer;\n }\n\n async handle(assetRequest: AssetRequest, asset: Asset): Promise<Asset> {\n const rawQuery = assetRequest.getOptions() as Record<string, any>;\n const acceptHeader = assetRequest.getContext<{ accept?: string }>().accept;\n // oxlint-disable-next-line typescript/no-unused-vars\n const { original, crop, focal, aspectRatio, ...options } = normalizeImageOptions(\n rawQuery,\n acceptHeader\n );\n\n const assetCrop = await this.loadAssetCrop(asset.getId());\n const framing: Framing = {\n crop: crop ?? assetCrop,\n focal,\n aspectRatio\n };\n\n const transformedAsset = asset.clone();\n\n if (hasTransform(options)) {\n return this.transformAsset(transformedAsset, options, framing);\n }\n\n return this.optimizeAsset(transformedAsset, framing);\n }\n\n private async loadAssetCrop(fileId: string) {\n const result = await this.identityContext.withoutAuthorization(() =>\n this.getFile.execute(fileId)\n );\n\n if (result.isFail()) {\n return undefined;\n }\n\n const imageEdit = result.value.metadata?.imageEdit as AssetImageEdit | undefined;\n return imageEdit?.crop;\n }\n\n private async transformAsset(asset: Asset, options: TransformOptions, framing: Framing) {\n const assetKey = AssetKeyGenerator.create(asset, framing);\n const transformedAssetKey = assetKey.getTransformedImageKey(options);\n\n const contentType = options.format\n ? contentTypeForFormat(options.format)\n : asset.getContentType();\n\n try {\n const { Body } = await this.s3.getObject({\n Bucket: this.bucket,\n Key: transformedAssetKey\n });\n\n if (!Body) {\n throw new Error(`Missing image body!`);\n }\n\n const buffer = Buffer.from(await Body.transformToByteArray());\n\n const newAsset = asset.withProps({ size: buffer.length, contentType });\n newAsset.setContentsReader(CallableContentsReader.create(() => buffer));\n\n console.log(`Return a previously transformed asset`, {\n key: transformedAssetKey,\n size: newAsset.getSize()\n });\n\n return newAsset;\n } catch {\n const optimizedImage = await this.optimizeAsset(asset, framing);\n\n console.log(`Transform the asset`, options);\n const baseBuffer = await optimizedImage.getContents();\n const transformer = await this.getTransformer();\n const { buffer: transformedBuffer, contentType: outputContentType } =\n await transformer.transformBuffer({\n buffer: baseBuffer,\n animated: this.isAssetAnimated(asset),\n sourceContentType: asset.getContentType(),\n widths: this.imageResizeWidths,\n options,\n qualityDefaults: this.imageQuality\n });\n\n const newAsset = asset.withProps({\n size: transformedBuffer.length,\n contentType: outputContentType\n });\n newAsset.setContentsReader(CallableContentsReader.create(() => transformedBuffer));\n\n await this.s3.putObject({\n Bucket: this.bucket,\n Key: transformedAssetKey,\n ContentType: newAsset.getContentType(),\n Body: await newAsset.getContents()\n });\n\n console.log(`Return the transformed asset`, {\n key: transformedAssetKey,\n size: newAsset.getSize(),\n contentType: newAsset.getContentType()\n });\n\n return newAsset;\n }\n }\n\n private async optimizeAsset(asset: Asset, framing: Framing) {\n console.log(\"Optimize asset\", {\n id: asset.getId(),\n key: asset.getKey(),\n size: asset.getSize(),\n type: asset.getContentType()\n });\n\n const assetKey = AssetKeyGenerator.create(asset, framing);\n const optimizedAssetKey = assetKey.getOptimizedImageKey();\n\n try {\n const { Body } = await this.s3.getObject({\n Bucket: this.bucket,\n Key: optimizedAssetKey\n });\n\n if (!Body) {\n throw new Error(`Missing image body!`);\n }\n\n console.log(\"Return a previously optimized asset\", optimizedAssetKey);\n\n const buffer = Buffer.from(await Body.transformToByteArray());\n\n const newAsset = asset.withProps({ size: buffer.length });\n newAsset.setContentsReader(CallableContentsReader.create(() => buffer));\n\n return newAsset;\n } catch {\n console.log(\"Create an optimized version of the original asset\", asset.getKey());\n let buffer = await asset.getContents();\n\n const transformer = await this.getTransformer();\n const framed = await transformer.extractFramedRegion(buffer, framing);\n const cropped = framed !== buffer;\n buffer = framed;\n\n const contentType = asset.getContentType();\n const canOptimize =\n contentType === \"image/png\" ||\n contentType === \"image/jpeg\" ||\n contentType === \"image/jpg\";\n\n if (!canOptimize && !cropped) {\n console.log(`No optimizations defined for ${contentType}`);\n return asset;\n }\n\n let finalBuffer = buffer;\n if (canOptimize) {\n const pipeline =\n contentType === \"image/png\"\n ? await transformer.optimizePng(buffer)\n : await transformer.optimizeJpeg(buffer);\n finalBuffer = await pipeline.toBuffer();\n }\n\n console.log(\"Optimized asset size\", finalBuffer.length);\n\n const newAsset = asset.withProps({ size: finalBuffer.length });\n newAsset.setContentsReader(CallableContentsReader.create(() => finalBuffer));\n\n await this.s3.putObject({\n Bucket: this.bucket,\n Key: optimizedAssetKey,\n ContentType: newAsset.getContentType(),\n Body: await newAsset.getContents()\n });\n\n return newAsset;\n }\n }\n\n private isAssetAnimated(asset: Asset) {\n return [\"gif\", \"webp\"].includes(asset.getExtension());\n }\n}\n\nexport const SharpTransformImpl = ImageAssetTypeHandler.createImplementation({\n implementation: SharpTransform,\n dependencies: [S3Client, S3Bucket, S3AssetDeliveryConfig, IdentityContext, GetFileUseCase]\n});\n"],"names":["hasTransform","options","undefined","SharpTransform","s3","bucket","config","identityContext","getFile","DEFAULT_IMAGE_QUALITY","SharpTransformer","assetRequest","asset","rawQuery","acceptHeader","original","crop","focal","aspectRatio","normalizeImageOptions","assetCrop","framing","transformedAsset","fileId","result","imageEdit","assetKey","AssetKeyGenerator","transformedAssetKey","contentType","contentTypeForFormat","Body","Error","buffer","Buffer","newAsset","CallableContentsReader","console","optimizedImage","baseBuffer","transformer","transformedBuffer","outputContentType","optimizedAssetKey","framed","cropped","canOptimize","finalBuffer","pipeline","SharpTransformImpl","ImageAssetTypeHandler","S3Client","S3Bucket","S3AssetDeliveryConfig","IdentityContext","GetFileUseCase"],"mappings":";;;;;;AAyBA,MAAMA,eAAe,CAACC,UAEdA,AAAkBC,WAAlBD,QAAQ,KAAK,IAAkBA,AAAmBC,WAAnBD,QAAQ,MAAM,IAAkBA,AAAoBC,WAApBD,QAAQ,OAAO;AAI/E,MAAME;IAST,YACIC,EAAM,EACNC,MAAc,EACdC,MAA8B,EAC9BC,eAA0C,EAC1CC,OAAiC,CACnC;QACE,IAAI,CAAC,EAAE,GAAGJ;QACV,IAAI,CAAC,MAAM,GAAGC;QACd,IAAI,CAAC,iBAAiB,GAAGC,OAAO,iBAAiB;QACjD,IAAI,CAAC,YAAY,GAAG;YAAE,GAAGG,qBAAqB;YAAE,GAAIH,OAAO,YAAY,IAAI,CAAC,CAAC;QAAE;QAC/E,IAAI,CAAC,eAAe,GAAGC;QACvB,IAAI,CAAC,OAAO,GAAGC;IACnB;IAEA,MAAc,iBAA4C;QACtD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YACnB,MAAM,EAAEE,gBAAgB,EAAE,GACtB,MAAM,MAAM,CAAC;YACjB,IAAI,CAAC,WAAW,GAAG,IAAIA;QAC3B;QACA,OAAO,IAAI,CAAC,WAAW;IAC3B;IAEA,MAAM,OAAOC,YAA0B,EAAEC,KAAY,EAAkB;QACnE,MAAMC,WAAWF,aAAa,UAAU;QACxC,MAAMG,eAAeH,aAAa,UAAU,GAAwB,MAAM;QAE1E,MAAM,EAAEI,QAAQ,EAAEC,IAAI,EAAEC,KAAK,EAAEC,WAAW,EAAE,GAAGjB,SAAS,GAAGkB,sBACvDN,UACAC;QAGJ,MAAMM,YAAY,MAAM,IAAI,CAAC,aAAa,CAACR,MAAM,KAAK;QACtD,MAAMS,UAAmB;YACrB,MAAML,QAAQI;YACdH;YACAC;QACJ;QAEA,MAAMI,mBAAmBV,MAAM,KAAK;QAEpC,IAAIZ,aAAaC,UACb,OAAO,IAAI,CAAC,cAAc,CAACqB,kBAAkBrB,SAASoB;QAG1D,OAAO,IAAI,CAAC,aAAa,CAACC,kBAAkBD;IAChD;IAEA,MAAc,cAAcE,MAAc,EAAE;QACxC,MAAMC,SAAS,MAAM,IAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC,IAC3D,IAAI,CAAC,OAAO,CAAC,OAAO,CAACD;QAGzB,IAAIC,OAAO,MAAM,IACb;QAGJ,MAAMC,YAAYD,OAAO,KAAK,CAAC,QAAQ,EAAE;QACzC,OAAOC,WAAW;IACtB;IAEA,MAAc,eAAeb,KAAY,EAAEX,OAAyB,EAAEoB,OAAgB,EAAE;QACpF,MAAMK,WAAWC,kBAAkB,MAAM,CAACf,OAAOS;QACjD,MAAMO,sBAAsBF,SAAS,sBAAsB,CAACzB;QAE5D,MAAM4B,cAAc5B,QAAQ,MAAM,GAC5B6B,qBAAqB7B,QAAQ,MAAM,IACnCW,MAAM,cAAc;QAE1B,IAAI;YACA,MAAM,EAAEmB,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC;gBACrC,QAAQ,IAAI,CAAC,MAAM;gBACnB,KAAKH;YACT;YAEA,IAAI,CAACG,MACD,MAAM,IAAIC,MAAM;YAGpB,MAAMC,SAASC,OAAO,IAAI,CAAC,MAAMH,KAAK,oBAAoB;YAE1D,MAAMI,WAAWvB,MAAM,SAAS,CAAC;gBAAE,MAAMqB,OAAO,MAAM;gBAAEJ;YAAY;YACpEM,SAAS,iBAAiB,CAACC,uBAAuB,MAAM,CAAC,IAAMH;YAE/DI,QAAQ,GAAG,CAAC,yCAAyC;gBACjD,KAAKT;gBACL,MAAMO,SAAS,OAAO;YAC1B;YAEA,OAAOA;QACX,EAAE,OAAM;YACJ,MAAMG,iBAAiB,MAAM,IAAI,CAAC,aAAa,CAAC1B,OAAOS;YAEvDgB,QAAQ,GAAG,CAAC,uBAAuBpC;YACnC,MAAMsC,aAAa,MAAMD,eAAe,WAAW;YACnD,MAAME,cAAc,MAAM,IAAI,CAAC,cAAc;YAC7C,MAAM,EAAE,QAAQC,iBAAiB,EAAE,aAAaC,iBAAiB,EAAE,GAC/D,MAAMF,YAAY,eAAe,CAAC;gBAC9B,QAAQD;gBACR,UAAU,IAAI,CAAC,eAAe,CAAC3B;gBAC/B,mBAAmBA,MAAM,cAAc;gBACvC,QAAQ,IAAI,CAAC,iBAAiB;gBAC9BX;gBACA,iBAAiB,IAAI,CAAC,YAAY;YACtC;YAEJ,MAAMkC,WAAWvB,MAAM,SAAS,CAAC;gBAC7B,MAAM6B,kBAAkB,MAAM;gBAC9B,aAAaC;YACjB;YACAP,SAAS,iBAAiB,CAACC,uBAAuB,MAAM,CAAC,IAAMK;YAE/D,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC;gBACpB,QAAQ,IAAI,CAAC,MAAM;gBACnB,KAAKb;gBACL,aAAaO,SAAS,cAAc;gBACpC,MAAM,MAAMA,SAAS,WAAW;YACpC;YAEAE,QAAQ,GAAG,CAAC,gCAAgC;gBACxC,KAAKT;gBACL,MAAMO,SAAS,OAAO;gBACtB,aAAaA,SAAS,cAAc;YACxC;YAEA,OAAOA;QACX;IACJ;IAEA,MAAc,cAAcvB,KAAY,EAAES,OAAgB,EAAE;QACxDgB,QAAQ,GAAG,CAAC,kBAAkB;YAC1B,IAAIzB,MAAM,KAAK;YACf,KAAKA,MAAM,MAAM;YACjB,MAAMA,MAAM,OAAO;YACnB,MAAMA,MAAM,cAAc;QAC9B;QAEA,MAAMc,WAAWC,kBAAkB,MAAM,CAACf,OAAOS;QACjD,MAAMsB,oBAAoBjB,SAAS,oBAAoB;QAEvD,IAAI;YACA,MAAM,EAAEK,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC;gBACrC,QAAQ,IAAI,CAAC,MAAM;gBACnB,KAAKY;YACT;YAEA,IAAI,CAACZ,MACD,MAAM,IAAIC,MAAM;YAGpBK,QAAQ,GAAG,CAAC,uCAAuCM;YAEnD,MAAMV,SAASC,OAAO,IAAI,CAAC,MAAMH,KAAK,oBAAoB;YAE1D,MAAMI,WAAWvB,MAAM,SAAS,CAAC;gBAAE,MAAMqB,OAAO,MAAM;YAAC;YACvDE,SAAS,iBAAiB,CAACC,uBAAuB,MAAM,CAAC,IAAMH;YAE/D,OAAOE;QACX,EAAE,OAAM;YACJE,QAAQ,GAAG,CAAC,qDAAqDzB,MAAM,MAAM;YAC7E,IAAIqB,SAAS,MAAMrB,MAAM,WAAW;YAEpC,MAAM4B,cAAc,MAAM,IAAI,CAAC,cAAc;YAC7C,MAAMI,SAAS,MAAMJ,YAAY,mBAAmB,CAACP,QAAQZ;YAC7D,MAAMwB,UAAUD,WAAWX;YAC3BA,SAASW;YAET,MAAMf,cAAcjB,MAAM,cAAc;YACxC,MAAMkC,cACFjB,AAAgB,gBAAhBA,eACAA,AAAgB,iBAAhBA,eACAA,AAAgB,gBAAhBA;YAEJ,IAAI,CAACiB,eAAe,CAACD,SAAS;gBAC1BR,QAAQ,GAAG,CAAC,CAAC,6BAA6B,EAAER,aAAa;gBACzD,OAAOjB;YACX;YAEA,IAAImC,cAAcd;YAClB,IAAIa,aAAa;gBACb,MAAME,WACFnB,AAAgB,gBAAhBA,cACM,MAAMW,YAAY,WAAW,CAACP,UAC9B,MAAMO,YAAY,YAAY,CAACP;gBACzCc,cAAc,MAAMC,SAAS,QAAQ;YACzC;YAEAX,QAAQ,GAAG,CAAC,wBAAwBU,YAAY,MAAM;YAEtD,MAAMZ,WAAWvB,MAAM,SAAS,CAAC;gBAAE,MAAMmC,YAAY,MAAM;YAAC;YAC5DZ,SAAS,iBAAiB,CAACC,uBAAuB,MAAM,CAAC,IAAMW;YAE/D,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC;gBACpB,QAAQ,IAAI,CAAC,MAAM;gBACnB,KAAKJ;gBACL,aAAaR,SAAS,cAAc;gBACpC,MAAM,MAAMA,SAAS,WAAW;YACpC;YAEA,OAAOA;QACX;IACJ;IAEQ,gBAAgBvB,KAAY,EAAE;QAClC,OAAO;YAAC;YAAO;SAAO,CAAC,QAAQ,CAACA,MAAM,YAAY;IACtD;AACJ;AAEO,MAAMqC,qBAAqBC,sBAAsB,oBAAoB,CAAC;IACzE,gBAAgB/C;IAChB,cAAc;QAACgD;QAAUC;QAAUC;QAAuBC;QAAiBC;KAAe;AAC9F"}
@@ -2,7 +2,7 @@ import { EventBridgeEventHandler } from "@webiny/event-handler-aws/abstractions/
2
2
  import { RequestContainer } from "@webiny/event-handler-core";
3
3
  import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
4
4
  import { GetTenantByIdUseCase } from "@webiny/api-core/features/tenancy/GetTenantById/index.js";
5
- import { WcpContext } from "@webiny/api-core/features/wcp/WcpContext/index.js";
5
+ import { FeatureFlags } from "@webiny/api-core/features/featureFlags/abstractions.js";
6
6
  import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
7
7
  import { processThreatScanResult } from "./processThreatScanResult.js";
8
8
  import { ObjectKey } from "./ObjectKey.js";
@@ -18,7 +18,7 @@ class ThreatDetectionEventBridgeLambdaHandlerImpl {
18
18
  if (payload["detail-type"] !== DETAIL_TYPE) return {
19
19
  success: true
20
20
  };
21
- if (!this.container.resolve(WcpContext).canUseFileManagerThreatDetection()) return {
21
+ if (!this.container.resolve(FeatureFlags).get().isEnabled("fileManager.threatDetection")) return {
22
22
  success: true
23
23
  };
24
24
  const objectKey = payload.detail.s3ObjectDetails.objectKey;
@@ -1 +1 @@
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 { WcpContext } from \"@webiny/api-core/features/wcp/WcpContext/index.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(WcpContext).canUseFileManagerThreatDetection()) {\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","WcpContext","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,YAAY,gCAAgC,IACpE,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
+ {"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,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/api-file-manager-s3",
3
- "version": "6.6.0-alpha.0",
3
+ "version": "6.6.0-alpha.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -14,22 +14,22 @@
14
14
  "author": "Webiny Ltd",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@webiny/api": "6.6.0-alpha.0",
18
- "@webiny/api-core": "6.6.0-alpha.0",
19
- "@webiny/api-file-manager": "6.6.0-alpha.0",
20
- "@webiny/api-graphql": "6.6.0-alpha.0",
21
- "@webiny/api-websockets": "6.6.0-alpha.0",
22
- "@webiny/aws-sdk": "6.6.0-alpha.0",
23
- "@webiny/background-tasks": "6.6.0-alpha.0",
24
- "@webiny/di": "1.0.2",
25
- "@webiny/event-handler-aws": "6.6.0-alpha.0",
26
- "@webiny/event-handler-core": "6.6.0-alpha.0",
27
- "@webiny/feature": "6.6.0-alpha.0",
28
- "@webiny/handler": "6.6.0-alpha.0",
29
- "@webiny/plugins": "6.6.0-alpha.0",
30
- "@webiny/utils": "6.6.0-alpha.0",
31
- "@webiny/validation": "6.6.0-alpha.0",
32
- "exifreader": "4.41.3",
17
+ "@webiny/api": "6.6.0-alpha.2",
18
+ "@webiny/api-core": "6.6.0-alpha.2",
19
+ "@webiny/api-file-manager": "6.6.0-alpha.2",
20
+ "@webiny/api-graphql": "6.6.0-alpha.2",
21
+ "@webiny/api-websockets": "6.6.0-alpha.2",
22
+ "@webiny/aws-sdk": "6.6.0-alpha.2",
23
+ "@webiny/background-tasks": "6.6.0-alpha.2",
24
+ "@webiny/di": "1.1.0",
25
+ "@webiny/event-handler-aws": "6.6.0-alpha.2",
26
+ "@webiny/event-handler-core": "6.6.0-alpha.2",
27
+ "@webiny/feature": "6.6.0-alpha.2",
28
+ "@webiny/handler": "6.6.0-alpha.2",
29
+ "@webiny/plugins": "6.6.0-alpha.2",
30
+ "@webiny/utils": "6.6.0-alpha.2",
31
+ "@webiny/validation": "6.6.0-alpha.2",
32
+ "exifreader": "4.43.0",
33
33
  "mime": "4.1.0",
34
34
  "object-hash": "3.0.0",
35
35
  "p-map": "7.0.6",
@@ -38,10 +38,10 @@
38
38
  "sharp": "0.35.3"
39
39
  },
40
40
  "devDependencies": {
41
- "@webiny/build-tools": "6.6.0-alpha.0",
41
+ "@webiny/build-tools": "6.6.0-alpha.2",
42
42
  "rimraf": "6.1.3",
43
43
  "typescript": "7.0.2",
44
- "vitest": "4.1.10"
44
+ "vitest": "4.1.11"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public"