@webiny/api-file-manager-server 0.0.0-unstable.0d717d18dd → 0.0.0-unstable.a4f3e4ea8b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assetDelivery/LocalAssetResolver.d.ts +4 -6
- package/assetDelivery/LocalAssetResolver.js +9 -9
- package/assetDelivery/LocalAssetResolver.js.map +1 -1
- package/assetDelivery/LocalOutputStrategy.d.ts +2 -3
- package/assetDelivery/LocalOutputStrategy.js +4 -4
- package/assetDelivery/LocalOutputStrategy.js.map +1 -1
- package/assetDelivery/LocalSharpTransform.d.ts +2 -2
- package/assetDelivery/LocalSharpTransform.js +34 -21
- package/assetDelivery/LocalSharpTransform.js.map +1 -1
- package/assetDelivery/assetDeliveryConfig.d.ts +1 -1
- package/assetDelivery/feature.js +6 -11
- package/assetDelivery/feature.js.map +1 -1
- package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.d.ts +0 -3
- package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js +14 -18
- package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js.map +1 -1
- package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.d.ts +5 -7
- package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js +5 -8
- package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js.map +1 -1
- package/features/CompleteMultiPartUpload/feature.js +2 -2
- package/features/CompleteMultiPartUpload/feature.js.map +1 -1
- package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.d.ts +7 -7
- package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.js +10 -9
- package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.js.map +1 -1
- package/features/CreateMultiPartUpload/feature.js +2 -2
- package/features/CreateMultiPartUpload/feature.js.map +1 -1
- package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.d.ts +1 -3
- package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.js +3 -6
- package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.js.map +1 -1
- package/features/ExtractMetadata/ExtractMetadataTask.d.ts +1 -3
- package/features/ExtractMetadata/ExtractMetadataTask.js +3 -6
- package/features/ExtractMetadata/ExtractMetadataTask.js.map +1 -1
- package/features/GetFileContentsById/GetFileContentsByIdUseCase.d.ts +5 -7
- package/features/GetFileContentsById/GetFileContentsByIdUseCase.js +5 -8
- package/features/GetFileContentsById/GetFileContentsByIdUseCase.js.map +1 -1
- package/features/GetFileContentsById/feature.js +2 -2
- package/features/GetFileContentsById/feature.js.map +1 -1
- package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.d.ts +5 -7
- package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.js +5 -8
- package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.js.map +1 -1
- package/features/GetFileContentsByKey/feature.js +2 -2
- package/features/GetFileContentsByKey/feature.js.map +1 -1
- package/features/GetUploadPayload/GetUploadPayloadUseCase.d.ts +6 -6
- package/features/GetUploadPayload/GetUploadPayloadUseCase.js +9 -8
- package/features/GetUploadPayload/GetUploadPayloadUseCase.js.map +1 -1
- package/features/GetUploadPayload/feature.js +2 -2
- package/features/GetUploadPayload/feature.js.map +1 -1
- package/index.d.ts +2 -1
- package/index.js +40 -1
- package/index.js.map +1 -0
- package/package.json +13 -13
- package/routes/uploadRoutes.d.ts +4 -0
- package/routes/uploadRoutes.js +130 -0
- package/routes/uploadRoutes.js.map +1 -0
- package/utils/resolveServerUrl.d.ts +2 -0
- package/utils/resolveServerUrl.js +10 -0
- package/utils/resolveServerUrl.js.map +1 -0
- package/FileManagerServerFeature.d.ts +0 -5
- package/FileManagerServerFeature.js +0 -41
- package/FileManagerServerFeature.js.map +0 -1
- package/features/FileManagerServerConfig/FileManagerServerConfig.d.ts +0 -12
- package/features/FileManagerServerConfig/FileManagerServerConfig.js +0 -28
- package/features/FileManagerServerConfig/FileManagerServerConfig.js.map +0 -1
- package/features/FileManagerServerConfig/abstractions.d.ts +0 -10
- package/features/FileManagerServerConfig/abstractions.js +0 -5
- package/features/FileManagerServerConfig/abstractions.js.map +0 -1
- package/features/FileManagerServerConfig/feature.d.ts +0 -4
- package/features/FileManagerServerConfig/feature.js +0 -11
- package/features/FileManagerServerConfig/feature.js.map +0 -1
- package/graphql/ServerGraphQLSchema.d.ts +0 -15
- package/graphql/ServerGraphQLSchema.js +0 -221
- package/graphql/ServerGraphQLSchema.js.map +0 -1
- package/routes/UploadPartRoute/UploadPartRoute.d.ts +0 -14
- package/routes/UploadPartRoute/UploadPartRoute.js +0 -66
- package/routes/UploadPartRoute/UploadPartRoute.js.map +0 -1
- package/routes/UploadPartRoute/feature.d.ts +0 -4
- package/routes/UploadPartRoute/feature.js +0 -11
- package/routes/UploadPartRoute/feature.js.map +0 -1
- package/routes/UploadSingleFileRoute/UploadSingleFileRoute.d.ts +0 -14
- package/routes/UploadSingleFileRoute/UploadSingleFileRoute.js +0 -70
- package/routes/UploadSingleFileRoute/UploadSingleFileRoute.js.map +0 -1
- package/routes/UploadSingleFileRoute/feature.d.ts +0 -4
- package/routes/UploadSingleFileRoute/feature.js +0 -11
- package/routes/UploadSingleFileRoute/feature.js.map +0 -1
- package/routes/utils.d.ts +0 -20
- package/routes/utils.js +0 -62
- package/routes/utils.js.map +0 -1
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { AssetFactory, AssetResolver as AssetResolverAbstraction, ObjectKey } from "@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js";
|
|
2
2
|
import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
|
|
3
|
-
|
|
4
|
-
declare class AssetResolverImpl implements AssetResolverAbstraction.Interface {
|
|
3
|
+
export declare class LocalAssetResolver implements AssetResolverAbstraction.Interface {
|
|
5
4
|
private readonly keyValueStore;
|
|
6
|
-
private readonly
|
|
5
|
+
private readonly storagePath;
|
|
7
6
|
private readonly objectKey;
|
|
8
7
|
private readonly assetFactory;
|
|
9
|
-
constructor(keyValueStore: GlobalKeyValueStore.Interface,
|
|
8
|
+
constructor(keyValueStore: GlobalKeyValueStore.Interface, storagePath: string, objectKey: ObjectKey.Interface, assetFactory: AssetFactory.Interface);
|
|
10
9
|
resolve(request: AssetResolverAbstraction.Request): Promise<AssetFactory.Asset | undefined>;
|
|
11
10
|
}
|
|
12
|
-
export declare const
|
|
11
|
+
export declare const LocalAssetResolverImpl: typeof LocalAssetResolver & {
|
|
13
12
|
__abstraction: import("@webiny/di").Abstraction<import("@webiny/api-file-manager/delivery").AssetResolver>;
|
|
14
13
|
};
|
|
15
|
-
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { AssetFactory, AssetResolver, ObjectKey } from "@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js";
|
|
2
2
|
import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
|
|
3
3
|
import { LocalContentsReader } from "./LocalContentsReader.js";
|
|
4
|
-
import {
|
|
5
|
-
class
|
|
6
|
-
constructor(keyValueStore,
|
|
4
|
+
import { LocalStoragePath } from "./abstractions.js";
|
|
5
|
+
class LocalAssetResolver {
|
|
6
|
+
constructor(keyValueStore, storagePath, objectKey, assetFactory){
|
|
7
7
|
this.keyValueStore = keyValueStore;
|
|
8
|
-
this.
|
|
8
|
+
this.storagePath = storagePath;
|
|
9
9
|
this.objectKey = objectKey;
|
|
10
10
|
this.assetFactory = assetFactory;
|
|
11
11
|
}
|
|
@@ -22,19 +22,19 @@ class AssetResolverImpl {
|
|
|
22
22
|
contentType: metadata.contentType,
|
|
23
23
|
key: metadata.bucketKey
|
|
24
24
|
});
|
|
25
|
-
asset.setContentsReader(LocalContentsReader.create(this.
|
|
25
|
+
asset.setContentsReader(LocalContentsReader.create(this.storagePath));
|
|
26
26
|
return asset;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
const
|
|
30
|
-
implementation:
|
|
29
|
+
const LocalAssetResolverImpl = AssetResolver.createImplementation({
|
|
30
|
+
implementation: LocalAssetResolver,
|
|
31
31
|
dependencies: [
|
|
32
32
|
GlobalKeyValueStore,
|
|
33
|
-
|
|
33
|
+
LocalStoragePath,
|
|
34
34
|
ObjectKey,
|
|
35
35
|
AssetFactory
|
|
36
36
|
]
|
|
37
37
|
});
|
|
38
|
-
export { LocalAssetResolver };
|
|
38
|
+
export { LocalAssetResolver, LocalAssetResolverImpl };
|
|
39
39
|
|
|
40
40
|
//# sourceMappingURL=LocalAssetResolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetDelivery/LocalAssetResolver.js","sources":["../../src/assetDelivery/LocalAssetResolver.ts"],"sourcesContent":["import {\n AssetFactory,\n AssetResolver as AssetResolverAbstraction,\n ObjectKey\n} from \"@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { LocalContentsReader } from \"~/assetDelivery/LocalContentsReader.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"assetDelivery/LocalAssetResolver.js","sources":["../../src/assetDelivery/LocalAssetResolver.ts"],"sourcesContent":["import {\n AssetFactory,\n AssetResolver as AssetResolverAbstraction,\n ObjectKey\n} from \"@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { LocalContentsReader } from \"~/assetDelivery/LocalContentsReader.js\";\nimport { LocalStoragePath } from \"~/assetDelivery/abstractions.js\";\n\ninterface AssetMetadata {\n id: string;\n tenant: string;\n size: number;\n contentType: string;\n bucketKey: string;\n}\n\nexport class LocalAssetResolver implements AssetResolverAbstraction.Interface {\n constructor(\n private readonly keyValueStore: GlobalKeyValueStore.Interface,\n private readonly storagePath: string,\n private readonly objectKey: ObjectKey.Interface,\n private readonly assetFactory: AssetFactory.Interface\n ) {}\n\n async resolve(\n request: AssetResolverAbstraction.Request\n ): Promise<AssetFactory.Asset | undefined> {\n const objectKey = this.objectKey.from(request.getKey());\n const fileId = objectKey.id();\n const result = await this.keyValueStore.get<AssetMetadata>(\n `FileManager/File/${fileId}/Metadata`\n );\n\n if (result.isFail()) {\n return undefined;\n }\n\n const metadata = result.value;\n\n const asset = this.assetFactory.create({\n id: metadata.id,\n tenant: metadata.tenant,\n size: metadata.size,\n contentType: metadata.contentType,\n key: metadata.bucketKey\n });\n\n asset.setContentsReader(LocalContentsReader.create(this.storagePath));\n\n return asset;\n }\n}\n\nexport const LocalAssetResolverImpl = AssetResolverAbstraction.createImplementation({\n implementation: LocalAssetResolver,\n dependencies: [GlobalKeyValueStore, LocalStoragePath, ObjectKey, AssetFactory]\n});\n"],"names":["LocalAssetResolver","keyValueStore","storagePath","objectKey","assetFactory","request","fileId","result","metadata","asset","LocalContentsReader","LocalAssetResolverImpl","AssetResolverAbstraction","GlobalKeyValueStore","LocalStoragePath","ObjectKey","AssetFactory"],"mappings":";;;;AAiBO,MAAMA;IACT,YACqBC,aAA4C,EAC5CC,WAAmB,EACnBC,SAA8B,EAC9BC,YAAoC,CACvD;aAJmBH,aAAa,GAAbA;aACAC,WAAW,GAAXA;aACAC,SAAS,GAATA;aACAC,YAAY,GAAZA;IAClB;IAEH,MAAM,QACFC,OAAyC,EACF;QACvC,MAAMF,YAAY,IAAI,CAAC,SAAS,CAAC,IAAI,CAACE,QAAQ,MAAM;QACpD,MAAMC,SAASH,UAAU,EAAE;QAC3B,MAAMI,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CACvC,CAAC,iBAAiB,EAAED,OAAO,SAAS,CAAC;QAGzC,IAAIC,OAAO,MAAM,IACb;QAGJ,MAAMC,WAAWD,OAAO,KAAK;QAE7B,MAAME,QAAQ,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;YACnC,IAAID,SAAS,EAAE;YACf,QAAQA,SAAS,MAAM;YACvB,MAAMA,SAAS,IAAI;YACnB,aAAaA,SAAS,WAAW;YACjC,KAAKA,SAAS,SAAS;QAC3B;QAEAC,MAAM,iBAAiB,CAACC,oBAAoB,MAAM,CAAC,IAAI,CAAC,WAAW;QAEnE,OAAOD;IACX;AACJ;AAEO,MAAME,yBAAyBC,cAAAA,oBAA6C,CAAC;IAChF,gBAAgBZ;IAChB,cAAc;QAACa;QAAqBC;QAAkBC;QAAWC;KAAa;AAClF"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { AssetOutputStrategy as AssetOutputStrategyAbstraction, StreamAssetReply } from "@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js";
|
|
2
2
|
import type { ILocalAssetDeliveryConfig } from "../assetDelivery/abstractions.js";
|
|
3
|
-
declare class
|
|
3
|
+
export declare class LocalOutputStrategy implements AssetOutputStrategyAbstraction.Interface {
|
|
4
4
|
private readonly assetStreamingMaxSize;
|
|
5
5
|
private readonly streamAssetReply;
|
|
6
6
|
constructor(config: ILocalAssetDeliveryConfig, streamAssetReply: StreamAssetReply.Interface);
|
|
7
7
|
output(asset: AssetOutputStrategyAbstraction.Asset): Promise<AssetOutputStrategyAbstraction.AssetReply>;
|
|
8
8
|
}
|
|
9
|
-
export declare const
|
|
9
|
+
export declare const LocalOutputStrategyImpl: typeof LocalOutputStrategy & {
|
|
10
10
|
__abstraction: import("@webiny/di").Abstraction<import("@webiny/api-file-manager/delivery").AssetOutputStrategy>;
|
|
11
11
|
};
|
|
12
|
-
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AssetOutputStrategy, StreamAssetReply } from "@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js";
|
|
2
2
|
import { LocalAssetDeliveryConfig } from "./abstractions.js";
|
|
3
|
-
class
|
|
3
|
+
class LocalOutputStrategy {
|
|
4
4
|
constructor(config, streamAssetReply){
|
|
5
5
|
this.assetStreamingMaxSize = config.assetStreamingMaxSize;
|
|
6
6
|
this.streamAssetReply = streamAssetReply;
|
|
@@ -9,13 +9,13 @@ class AssetOutputStrategyImpl {
|
|
|
9
9
|
return this.streamAssetReply.create(asset);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
const
|
|
13
|
-
implementation:
|
|
12
|
+
const LocalOutputStrategyImpl = AssetOutputStrategy.createImplementation({
|
|
13
|
+
implementation: LocalOutputStrategy,
|
|
14
14
|
dependencies: [
|
|
15
15
|
LocalAssetDeliveryConfig,
|
|
16
16
|
StreamAssetReply
|
|
17
17
|
]
|
|
18
18
|
});
|
|
19
|
-
export { LocalOutputStrategy };
|
|
19
|
+
export { LocalOutputStrategy, LocalOutputStrategyImpl };
|
|
20
20
|
|
|
21
21
|
//# sourceMappingURL=LocalOutputStrategy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetDelivery/LocalOutputStrategy.js","sources":["../../src/assetDelivery/LocalOutputStrategy.ts"],"sourcesContent":["import {\n AssetOutputStrategy as AssetOutputStrategyAbstraction,\n StreamAssetReply\n} from \"@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js\";\nimport { LocalAssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\nimport type { ILocalAssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\n\
|
|
1
|
+
{"version":3,"file":"assetDelivery/LocalOutputStrategy.js","sources":["../../src/assetDelivery/LocalOutputStrategy.ts"],"sourcesContent":["import {\n AssetOutputStrategy as AssetOutputStrategyAbstraction,\n StreamAssetReply\n} from \"@webiny/api-file-manager/exports/api/file-manager/assetDelivery.js\";\nimport { LocalAssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\nimport type { ILocalAssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\n\nexport class LocalOutputStrategy implements AssetOutputStrategyAbstraction.Interface {\n private readonly assetStreamingMaxSize: number;\n private readonly streamAssetReply: StreamAssetReply.Interface;\n\n constructor(config: ILocalAssetDeliveryConfig, streamAssetReply: StreamAssetReply.Interface) {\n this.assetStreamingMaxSize = config.assetStreamingMaxSize;\n this.streamAssetReply = streamAssetReply;\n }\n\n async output(\n asset: AssetOutputStrategyAbstraction.Asset\n ): Promise<AssetOutputStrategyAbstraction.AssetReply> {\n return this.streamAssetReply.create(asset);\n }\n}\n\nexport const LocalOutputStrategyImpl = AssetOutputStrategyAbstraction.createImplementation({\n implementation: LocalOutputStrategy,\n dependencies: [LocalAssetDeliveryConfig, StreamAssetReply]\n});\n"],"names":["LocalOutputStrategy","config","streamAssetReply","asset","LocalOutputStrategyImpl","AssetOutputStrategyAbstraction","LocalAssetDeliveryConfig","StreamAssetReply"],"mappings":";;AAOO,MAAMA;IAIT,YAAYC,MAAiC,EAAEC,gBAA4C,CAAE;QACzF,IAAI,CAAC,qBAAqB,GAAGD,OAAO,qBAAqB;QACzD,IAAI,CAAC,gBAAgB,GAAGC;IAC5B;IAEA,MAAM,OACFC,KAA2C,EACO;QAClD,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAACA;IACxC;AACJ;AAEO,MAAMC,0BAA0BC,oBAAAA,oBAAmD,CAAC;IACvF,gBAAgBL;IAChB,cAAc;QAACM;QAA0BC;KAAiB;AAC9D"}
|
|
@@ -10,14 +10,14 @@ export declare class LocalSharpTransform implements IAssetTypeHandler {
|
|
|
10
10
|
private readonly imageQuality;
|
|
11
11
|
private readonly identityContext;
|
|
12
12
|
private readonly getFile;
|
|
13
|
-
private transformer?;
|
|
14
13
|
constructor(storagePath: string, config: ILocalAssetDeliveryConfig, identityContext: IdentityContext.Interface, getFile: GetFileUseCase.Interface);
|
|
15
|
-
private getTransformer;
|
|
16
14
|
handle(assetRequest: AssetRequest, asset: Asset): Promise<Asset>;
|
|
17
15
|
private loadAssetCrop;
|
|
18
16
|
private transformAsset;
|
|
19
17
|
private optimizeAsset;
|
|
20
18
|
private isAssetAnimated;
|
|
19
|
+
private optimizePng;
|
|
20
|
+
private optimizeJpeg;
|
|
21
21
|
}
|
|
22
22
|
export declare const LocalSharpTransformImpl: typeof LocalSharpTransform & {
|
|
23
23
|
__abstraction: import("@webiny/di").Abstraction<IAssetTypeHandler>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import sharp from "sharp";
|
|
1
2
|
import promises from "node:fs/promises";
|
|
2
3
|
import node_path from "node:path";
|
|
3
4
|
import { IdentityContext } from "@webiny/api-core/features/security/IdentityContext/index.js";
|
|
4
5
|
import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
|
|
5
6
|
import { CallableContentsReader, DEFAULT_IMAGE_QUALITY, contentTypeForFormat } from "@webiny/api-file-manager/features/assetDelivery/transformation/index.js";
|
|
7
|
+
import { extractFramedRegion, transformImageBuffer } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/transformImage.js";
|
|
6
8
|
import { normalizeImageOptions } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/normalizeImageOptions.js";
|
|
7
9
|
import { AssetKeyGenerator, ImageAssetTypeHandler } from "@webiny/api-file-manager/features/assetDelivery/assetTypes/image/index.js";
|
|
8
10
|
import { LocalAssetDeliveryConfig, LocalStoragePath } from "./abstractions.js";
|
|
@@ -18,13 +20,6 @@ class LocalSharpTransform {
|
|
|
18
20
|
this.identityContext = identityContext;
|
|
19
21
|
this.getFile = getFile;
|
|
20
22
|
}
|
|
21
|
-
async getTransformer() {
|
|
22
|
-
if (!this.transformer) {
|
|
23
|
-
const { SharpTransformer } = await import("@webiny/api-file-manager/features/assetDelivery/assetTypes/image/SharpTransformer.js");
|
|
24
|
-
this.transformer = new SharpTransformer();
|
|
25
|
-
}
|
|
26
|
-
return this.transformer;
|
|
27
|
-
}
|
|
28
23
|
async handle(assetRequest, asset) {
|
|
29
24
|
const rawQuery = assetRequest.getOptions();
|
|
30
25
|
const acceptHeader = assetRequest.getContext().accept;
|
|
@@ -42,7 +37,7 @@ class LocalSharpTransform {
|
|
|
42
37
|
async loadAssetCrop(fileId) {
|
|
43
38
|
const result = await this.identityContext.withoutAuthorization(()=>this.getFile.execute(fileId));
|
|
44
39
|
if (result.isFail()) return;
|
|
45
|
-
const imageEdit = result.value.metadata?.
|
|
40
|
+
const imageEdit = result.value.metadata?.image;
|
|
46
41
|
return imageEdit?.crop;
|
|
47
42
|
}
|
|
48
43
|
async transformAsset(asset, options, framing) {
|
|
@@ -66,8 +61,7 @@ class LocalSharpTransform {
|
|
|
66
61
|
const optimizedImage = await this.optimizeAsset(asset, framing);
|
|
67
62
|
console.log("Transform the asset", options);
|
|
68
63
|
const baseBuffer = await optimizedImage.getContents();
|
|
69
|
-
const
|
|
70
|
-
const { buffer: transformedBuffer, contentType: outputContentType } = await transformer.transformBuffer({
|
|
64
|
+
const { buffer: transformedBuffer, contentType: outputContentType } = await transformImageBuffer({
|
|
71
65
|
buffer: baseBuffer,
|
|
72
66
|
animated: this.isAssetAnimated(asset),
|
|
73
67
|
sourceContentType: asset.getContentType(),
|
|
@@ -113,21 +107,20 @@ class LocalSharpTransform {
|
|
|
113
107
|
} catch {
|
|
114
108
|
console.log("Create an optimized version of the original asset", asset.getKey());
|
|
115
109
|
let buffer = await asset.getContents();
|
|
116
|
-
const
|
|
117
|
-
const framed = await transformer.extractFramedRegion(buffer, framing);
|
|
110
|
+
const framed = await extractFramedRegion(buffer, framing);
|
|
118
111
|
const cropped = framed !== buffer;
|
|
119
112
|
buffer = framed;
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
const optimizationMap = {
|
|
114
|
+
"image/png": (buffer)=>this.optimizePng(buffer),
|
|
115
|
+
"image/jpeg": (buffer)=>this.optimizeJpeg(buffer),
|
|
116
|
+
"image/jpg": (buffer)=>this.optimizeJpeg(buffer)
|
|
117
|
+
};
|
|
118
|
+
const optimization = optimizationMap[asset.getContentType()];
|
|
119
|
+
if (!optimization && !cropped) {
|
|
120
|
+
console.log(`No optimizations defined for ${asset.getContentType()}`);
|
|
124
121
|
return asset;
|
|
125
122
|
}
|
|
126
|
-
|
|
127
|
-
if (canOptimize) {
|
|
128
|
-
const pipeline = "image/png" === contentType ? await transformer.optimizePng(buffer) : await transformer.optimizeJpeg(buffer);
|
|
129
|
-
finalBuffer = await pipeline.toBuffer();
|
|
130
|
-
}
|
|
123
|
+
const finalBuffer = optimization ? await optimization(buffer).toBuffer() : buffer;
|
|
131
124
|
console.log("Optimized asset size", finalBuffer.length);
|
|
132
125
|
const newAsset = asset.withProps({
|
|
133
126
|
size: finalBuffer.length
|
|
@@ -146,6 +139,26 @@ class LocalSharpTransform {
|
|
|
146
139
|
"webp"
|
|
147
140
|
].includes(asset.getExtension());
|
|
148
141
|
}
|
|
142
|
+
optimizePng(buffer) {
|
|
143
|
+
return sharp(buffer).resize({
|
|
144
|
+
width: 2560,
|
|
145
|
+
withoutEnlargement: true,
|
|
146
|
+
fit: "inside"
|
|
147
|
+
}).png({
|
|
148
|
+
compressionLevel: 9,
|
|
149
|
+
adaptiveFiltering: true,
|
|
150
|
+
force: true
|
|
151
|
+
}).withMetadata();
|
|
152
|
+
}
|
|
153
|
+
optimizeJpeg(buffer) {
|
|
154
|
+
return sharp(buffer).resize({
|
|
155
|
+
width: 2560,
|
|
156
|
+
withoutEnlargement: true,
|
|
157
|
+
fit: "inside"
|
|
158
|
+
}).withMetadata().toFormat("jpeg", {
|
|
159
|
+
quality: 90
|
|
160
|
+
});
|
|
161
|
+
}
|
|
149
162
|
}
|
|
150
163
|
const LocalSharpTransformImpl = ImageAssetTypeHandler.createImplementation({
|
|
151
164
|
implementation: LocalSharpTransform,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetDelivery/LocalSharpTransform.js","sources":["../../src/assetDelivery/LocalSharpTransform.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\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 { IAssetTypeHandler } from \"@webiny/api-file-manager/features/assetDelivery/abstractions/AssetType.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 { LocalStoragePath } from \"~/assetDelivery/abstractions.js\";\nimport { LocalAssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\nimport type { ILocalAssetDeliveryConfig } 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 LocalSharpTransform implements IAssetTypeHandler {\n private readonly storagePath: 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 storagePath: string,\n config: ILocalAssetDeliveryConfig,\n identityContext: IdentityContext.Interface,\n getFile: GetFileUseCase.Interface\n ) {\n this.storagePath = storagePath;\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 const transformedFilePath = path.join(this.storagePath, transformedAssetKey);\n\n const contentType = options.format\n ? contentTypeForFormat(options.format)\n : asset.getContentType();\n\n try {\n const buffer = await fs.readFile(transformedFilePath);\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 fs.mkdir(path.dirname(transformedFilePath), { recursive: true });\n await fs.writeFile(transformedFilePath, await newAsset.getContents());\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 const optimizedFilePath = path.join(this.storagePath, optimizedAssetKey);\n\n try {\n const buffer = await fs.readFile(optimizedFilePath);\n\n console.log(\"Return a previously optimized asset\", optimizedAssetKey);\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 fs.mkdir(path.dirname(optimizedFilePath), { recursive: true });\n await fs.writeFile(optimizedFilePath, await newAsset.getContents());\n\n return newAsset;\n }\n }\n\n private isAssetAnimated(asset: Asset) {\n return [\"gif\", \"webp\"].includes(asset.getExtension());\n }\n}\n\nexport const LocalSharpTransformImpl = ImageAssetTypeHandler.createImplementation({\n implementation: LocalSharpTransform,\n dependencies: [LocalStoragePath, LocalAssetDeliveryConfig, IdentityContext, GetFileUseCase]\n});\n"],"names":["hasTransform","options","undefined","LocalSharpTransform","storagePath","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","transformedFilePath","path","contentType","contentTypeForFormat","buffer","fs","newAsset","CallableContentsReader","console","optimizedImage","baseBuffer","transformer","transformedBuffer","outputContentType","optimizedAssetKey","optimizedFilePath","framed","cropped","canOptimize","finalBuffer","pipeline","LocalSharpTransformImpl","ImageAssetTypeHandler","LocalStoragePath","LocalAssetDeliveryConfig","IdentityContext","GetFileUseCase"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"assetDelivery/LocalSharpTransform.js","sources":["../../src/assetDelivery/LocalSharpTransform.ts"],"sourcesContent":["import sharp from \"sharp\";\nimport type { Sharp } from \"sharp\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\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 {\n extractFramedRegion,\n transformImageBuffer\n} from \"@webiny/api-file-manager/features/assetDelivery/assetTypes/image/transformImage.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 { IAssetTypeHandler } from \"@webiny/api-file-manager/features/assetDelivery/abstractions/AssetType.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 { LocalStoragePath } from \"~/assetDelivery/abstractions.js\";\nimport { LocalAssetDeliveryConfig } from \"~/assetDelivery/abstractions.js\";\nimport type { ILocalAssetDeliveryConfig } 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 LocalSharpTransform implements IAssetTypeHandler {\n private readonly storagePath: 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\n constructor(\n storagePath: string,\n config: ILocalAssetDeliveryConfig,\n identityContext: IdentityContext.Interface,\n getFile: GetFileUseCase.Interface\n ) {\n this.storagePath = storagePath;\n this.imageResizeWidths = config.imageResizeWidths;\n this.imageQuality = { ...DEFAULT_IMAGE_QUALITY, ...(config.imageQuality ?? {}) };\n this.identityContext = identityContext;\n this.getFile = getFile;\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?.image 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 const transformedFilePath = path.join(this.storagePath, transformedAssetKey);\n\n const contentType = options.format\n ? contentTypeForFormat(options.format)\n : asset.getContentType();\n\n try {\n const buffer = await fs.readFile(transformedFilePath);\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 { buffer: transformedBuffer, contentType: outputContentType } =\n await transformImageBuffer({\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 fs.mkdir(path.dirname(transformedFilePath), { recursive: true });\n await fs.writeFile(transformedFilePath, await newAsset.getContents());\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 const optimizedFilePath = path.join(this.storagePath, optimizedAssetKey);\n\n try {\n const buffer = await fs.readFile(optimizedFilePath);\n\n console.log(\"Return a previously optimized asset\", optimizedAssetKey);\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 framed = await extractFramedRegion(buffer, framing);\n const cropped = framed !== buffer;\n buffer = framed;\n\n const optimizationMap: Record<string, ((buffer: Buffer) => Sharp) | undefined> = {\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 && !cropped) {\n console.log(`No optimizations defined for ${asset.getContentType()}`);\n return asset;\n }\n\n const finalBuffer = optimization ? await optimization(buffer).toBuffer() : buffer;\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 fs.mkdir(path.dirname(optimizedFilePath), { recursive: true });\n await fs.writeFile(optimizedFilePath, await newAsset.getContents());\n\n return newAsset;\n }\n }\n\n private isAssetAnimated(asset: Asset) {\n return [\"gif\", \"webp\"].includes(asset.getExtension());\n }\n\n private optimizePng(buffer: Buffer) {\n return sharp(buffer)\n .resize({ width: 2560, withoutEnlargement: true, fit: \"inside\" })\n .png({ compressionLevel: 9, adaptiveFiltering: true, force: true })\n .withMetadata();\n }\n\n private optimizeJpeg(buffer: Buffer) {\n return sharp(buffer)\n .resize({ width: 2560, withoutEnlargement: true, fit: \"inside\" })\n .withMetadata()\n .toFormat(\"jpeg\", { quality: 90 });\n }\n}\n\nexport const LocalSharpTransformImpl = ImageAssetTypeHandler.createImplementation({\n implementation: LocalSharpTransform,\n dependencies: [LocalStoragePath, LocalAssetDeliveryConfig, IdentityContext, GetFileUseCase]\n});\n"],"names":["hasTransform","options","undefined","LocalSharpTransform","storagePath","config","identityContext","getFile","DEFAULT_IMAGE_QUALITY","assetRequest","asset","rawQuery","acceptHeader","original","crop","focal","aspectRatio","normalizeImageOptions","assetCrop","framing","transformedAsset","fileId","result","imageEdit","assetKey","AssetKeyGenerator","transformedAssetKey","transformedFilePath","path","contentType","contentTypeForFormat","buffer","fs","newAsset","CallableContentsReader","console","optimizedImage","baseBuffer","transformedBuffer","outputContentType","transformImageBuffer","optimizedAssetKey","optimizedFilePath","framed","extractFramedRegion","cropped","optimizationMap","optimization","finalBuffer","sharp","LocalSharpTransformImpl","ImageAssetTypeHandler","LocalStoragePath","LocalAssetDeliveryConfig","IdentityContext","GetFileUseCase"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { AssetDeliveryParams } from "../assetDelivery/types.js";
|
|
2
|
-
export declare const assetDeliveryConfig: (params: AssetDeliveryParams) => (import("@webiny/handler").RegisterExtensionPlugin<import("@webiny/handler/types").Context>[] | import("@webiny/handler").RegisterExtensionPlugin<import("@webiny/handler/types").Context>)[];
|
|
2
|
+
export declare const assetDeliveryConfig: (params: AssetDeliveryParams) => ((import("@webiny/handler").ModifyFastifyPlugin | import("@webiny/handler").RegisterExtensionPlugin<import("@webiny/handler/types").Context>)[] | import("@webiny/handler").RegisterExtensionPlugin<import("@webiny/handler/types").Context>)[];
|
package/assetDelivery/feature.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createFeature } from "@webiny/feature/api";
|
|
2
|
-
import { RequestContextInitializer } from "@webiny/event-handler-core";
|
|
3
2
|
import { LocalAssetDeliveryConfig, LocalStoragePath } from "./abstractions.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { LocalAssetResolverImpl } from "./LocalAssetResolver.js";
|
|
4
|
+
import { LocalOutputStrategyImpl } from "./LocalOutputStrategy.js";
|
|
5
|
+
import { LocalSharpTransformImpl } from "./LocalSharpTransform.js";
|
|
6
6
|
const createLocalAssetDeliveryFeature = (params = {})=>createFeature({
|
|
7
7
|
name: "AssetDelivery/Local",
|
|
8
8
|
register (container) {
|
|
@@ -23,14 +23,9 @@ const createLocalAssetDeliveryFeature = (params = {})=>createFeature({
|
|
|
23
23
|
imageQuality: params.imageQuality ?? {},
|
|
24
24
|
assetStreamingMaxSize: params.assetStreamingMaxSize ?? 4718592
|
|
25
25
|
});
|
|
26
|
-
container.register(
|
|
27
|
-
container.register(
|
|
28
|
-
|
|
29
|
-
async init (ctx) {
|
|
30
|
-
const { LocalSharpTransformImpl } = await import("./LocalSharpTransform.js");
|
|
31
|
-
ctx.container.register(LocalSharpTransformImpl).inSingletonScope();
|
|
32
|
-
}
|
|
33
|
-
});
|
|
26
|
+
container.register(LocalAssetResolverImpl);
|
|
27
|
+
container.register(LocalOutputStrategyImpl);
|
|
28
|
+
container.register(LocalSharpTransformImpl).inSingletonScope();
|
|
34
29
|
}
|
|
35
30
|
});
|
|
36
31
|
export { createLocalAssetDeliveryFeature };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetDelivery/feature.js","sources":["../../src/assetDelivery/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport {
|
|
1
|
+
{"version":3,"file":"assetDelivery/feature.js","sources":["../../src/assetDelivery/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { LocalStoragePath } from \"./abstractions.js\";\nimport { LocalAssetDeliveryConfig } from \"./abstractions.js\";\nimport type { AssetDeliveryParams } from \"./types.js\";\nimport { LocalAssetResolverImpl } from \"./LocalAssetResolver.js\";\nimport { LocalOutputStrategyImpl } from \"./LocalOutputStrategy.js\";\nimport { LocalSharpTransformImpl } from \"./LocalSharpTransform.js\";\n\nexport const createLocalAssetDeliveryFeature = (params: AssetDeliveryParams = {}) => {\n return createFeature({\n name: \"AssetDelivery/Local\",\n register(container) {\n container.registerInstance(\n LocalStoragePath,\n process.env.WEBINY_LOCAL_STORAGE_PATH as string\n );\n container.registerInstance(LocalAssetDeliveryConfig, {\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(LocalAssetResolverImpl);\n container.register(LocalOutputStrategyImpl);\n container.register(LocalSharpTransformImpl).inSingletonScope();\n }\n });\n};\n"],"names":["createLocalAssetDeliveryFeature","params","createFeature","container","LocalStoragePath","process","LocalAssetDeliveryConfig","LocalAssetResolverImpl","LocalOutputStrategyImpl","LocalSharpTransformImpl"],"mappings":";;;;;AAQO,MAAMA,kCAAkC,CAACC,SAA8B,CAAC,CAAC,GACrEC,cAAc;QACjB,MAAM;QACN,UAASC,SAAS;YACdA,UAAU,gBAAgB,CACtBC,kBACAC,QAAQ,GAAG,CAAC,yBAAyB;YAEzCF,UAAU,gBAAgB,CAACG,0BAA0B;gBACjD,mBAAmBL,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,CAACI;YACnBJ,UAAU,QAAQ,CAACK;YACnBL,UAAU,QAAQ,CAACM,yBAAyB,gBAAgB;QAChE;IACJ"}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
|
|
2
|
-
import { FileManagerServerConfig } from "../../features/FileManagerServerConfig/abstractions.js";
|
|
3
2
|
export interface CleanupStaleMultipartUploadsInput {
|
|
4
3
|
noop?: boolean;
|
|
5
4
|
}
|
|
6
5
|
declare class CleanupStaleMultipartUploadsTaskImpl implements TaskDefinition.Interface<CleanupStaleMultipartUploadsInput> {
|
|
7
|
-
private readonly config;
|
|
8
|
-
constructor(config: FileManagerServerConfig.Interface);
|
|
9
6
|
readonly id = "fileManagerCleanupStaleMultipartUploads";
|
|
10
7
|
readonly title = "Clean up stale multipart upload directories";
|
|
11
8
|
readonly description = "Removes multipart upload directories that are older than 24 hours.";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { promises } from "node:fs";
|
|
2
2
|
import node_path from "node:path";
|
|
3
3
|
import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
|
|
4
|
-
import { FileManagerServerConfig } from "../FileManagerServerConfig/abstractions.js";
|
|
5
4
|
const STALE_THRESHOLD_MS = 86400000;
|
|
6
5
|
async function readdirSafe(dirPath) {
|
|
7
6
|
try {
|
|
@@ -12,22 +11,9 @@ async function readdirSafe(dirPath) {
|
|
|
12
11
|
}
|
|
13
12
|
}
|
|
14
13
|
class CleanupStaleMultipartUploadsTaskImpl {
|
|
15
|
-
constructor(config){
|
|
16
|
-
this.config = config;
|
|
17
|
-
this.id = "fileManagerCleanupStaleMultipartUploads";
|
|
18
|
-
this.title = "Clean up stale multipart upload directories";
|
|
19
|
-
this.description = "Removes multipart upload directories that are older than 24 hours.";
|
|
20
|
-
this.maxIterations = 1;
|
|
21
|
-
this.isPrivate = true;
|
|
22
|
-
this.databaseLogs = false;
|
|
23
|
-
this.selfCleanup = [
|
|
24
|
-
"onSuccess",
|
|
25
|
-
"onAbort"
|
|
26
|
-
];
|
|
27
|
-
}
|
|
28
14
|
async run({ controller }) {
|
|
29
15
|
if (controller.runtime.isAborted()) return controller.response.aborted();
|
|
30
|
-
const storagePath =
|
|
16
|
+
const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);
|
|
31
17
|
const tenantsDir = node_path.join(storagePath, "tenants");
|
|
32
18
|
const now = Date.now();
|
|
33
19
|
const tenantDirs = await readdirSafe(tenantsDir);
|
|
@@ -50,12 +36,22 @@ class CleanupStaleMultipartUploadsTaskImpl {
|
|
|
50
36
|
}
|
|
51
37
|
return controller.response.done();
|
|
52
38
|
}
|
|
39
|
+
constructor(){
|
|
40
|
+
this.id = "fileManagerCleanupStaleMultipartUploads";
|
|
41
|
+
this.title = "Clean up stale multipart upload directories";
|
|
42
|
+
this.description = "Removes multipart upload directories that are older than 24 hours.";
|
|
43
|
+
this.maxIterations = 1;
|
|
44
|
+
this.isPrivate = true;
|
|
45
|
+
this.databaseLogs = false;
|
|
46
|
+
this.selfCleanup = [
|
|
47
|
+
"onSuccess",
|
|
48
|
+
"onAbort"
|
|
49
|
+
];
|
|
50
|
+
}
|
|
53
51
|
}
|
|
54
52
|
const CleanupStaleMultipartUploadsTaskDefinition = TaskDefinition.createImplementation({
|
|
55
53
|
implementation: CleanupStaleMultipartUploadsTaskImpl,
|
|
56
|
-
dependencies: [
|
|
57
|
-
FileManagerServerConfig
|
|
58
|
-
]
|
|
54
|
+
dependencies: []
|
|
59
55
|
});
|
|
60
56
|
export { CleanupStaleMultipartUploadsTaskDefinition };
|
|
61
57
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js","sources":["../../../src/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.ts"],"sourcesContent":["import { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\
|
|
1
|
+
{"version":3,"file":"features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js","sources":["../../../src/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.ts"],"sourcesContent":["import { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\n\nexport interface CleanupStaleMultipartUploadsInput {\n /* Reserved for future use. */\n noop?: boolean;\n}\n\nconst STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;\n\n/* Returns directory entries or an empty array if the path does not exist. */\nasync function readdirSafe(dirPath: string): Promise<string[]> {\n try {\n return await fs.readdir(dirPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return [];\n }\n throw err;\n }\n}\n\nclass CleanupStaleMultipartUploadsTaskImpl implements TaskDefinition.Interface<CleanupStaleMultipartUploadsInput> {\n public readonly id = \"fileManagerCleanupStaleMultipartUploads\";\n public readonly title = \"Clean up stale multipart upload directories\";\n public readonly description =\n \"Removes multipart upload directories that are older than 24 hours.\";\n public readonly maxIterations = 1;\n public readonly isPrivate = true;\n public readonly databaseLogs = false;\n public readonly selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n public async run({\n controller\n }: TaskDefinition.RunParams<CleanupStaleMultipartUploadsInput>): Promise<\n TaskDefinition.Result<CleanupStaleMultipartUploadsInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);\n const tenantsDir = path.join(storagePath, \"tenants\");\n const now = Date.now();\n\n const tenantDirs = await readdirSafe(tenantsDir);\n\n for (const tenantId of tenantDirs) {\n const multipartDir = path.join(tenantsDir, tenantId, \"multipart\");\n const uploadDirs = await readdirSafe(multipartDir);\n\n for (const uploadId of uploadDirs) {\n const uploadPath = path.join(multipartDir, uploadId);\n\n try {\n const stat = await fs.stat(uploadPath);\n const ageMs = now - stat.mtimeMs;\n\n if (ageMs > STALE_THRESHOLD_MS) {\n await fs.rm(uploadPath, { recursive: true, force: true });\n }\n } catch (err) {\n /* Skip entries that have already been removed or cannot be accessed. */\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n }\n }\n }\n\n return controller.response.done();\n }\n}\n\nexport const CleanupStaleMultipartUploadsTaskDefinition = TaskDefinition.createImplementation({\n implementation: CleanupStaleMultipartUploadsTaskImpl,\n dependencies: []\n});\n"],"names":["STALE_THRESHOLD_MS","readdirSafe","dirPath","fs","err","CleanupStaleMultipartUploadsTaskImpl","controller","storagePath","String","process","tenantsDir","path","now","Date","tenantDirs","tenantId","multipartDir","uploadDirs","uploadId","uploadPath","stat","ageMs","CleanupStaleMultipartUploadsTaskDefinition","TaskDefinition"],"mappings":";;;AASA,MAAMA,qBAAqB;AAG3B,eAAeC,YAAYC,OAAe;IACtC,IAAI;QACA,OAAO,MAAMC,SAAAA,OAAU,CAACD;IAC5B,EAAE,OAAOE,KAAK;QACV,IAAKA,AAAuC,aAAvCA,IAA8B,IAAI,EACnC,OAAO,EAAE;QAEb,MAAMA;IACV;AACJ;AAEA,MAAMC;IAUF,MAAa,IAAI,EACbC,UAAU,EACgD,EAE5D;QACE,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,MAAMC,cAAcC,OAAOC,QAAQ,GAAG,CAAC,yBAAyB;QAChE,MAAMC,aAAaC,UAAAA,IAAS,CAACJ,aAAa;QAC1C,MAAMK,MAAMC,KAAK,GAAG;QAEpB,MAAMC,aAAa,MAAMb,YAAYS;QAErC,KAAK,MAAMK,YAAYD,WAAY;YAC/B,MAAME,eAAeL,UAAAA,IAAS,CAACD,YAAYK,UAAU;YACrD,MAAME,aAAa,MAAMhB,YAAYe;YAErC,KAAK,MAAME,YAAYD,WAAY;gBAC/B,MAAME,aAAaR,UAAAA,IAAS,CAACK,cAAcE;gBAE3C,IAAI;oBACA,MAAME,OAAO,MAAMjB,SAAAA,IAAO,CAACgB;oBAC3B,MAAME,QAAQT,MAAMQ,KAAK,OAAO;oBAEhC,IAAIC,QAAQrB,oBACR,MAAMG,SAAAA,EAAK,CAACgB,YAAY;wBAAE,WAAW;wBAAM,OAAO;oBAAK;gBAE/D,EAAE,OAAOf,KAAK;oBAEV,IAAKA,AAAuC,aAAvCA,IAA8B,IAAI,EACnC,MAAMA;gBAEd;YACJ;QACJ;QAEA,OAAOE,WAAW,QAAQ,CAAC,IAAI;IACnC;;aAhDgB,EAAE,GAAG;aACL,KAAK,GAAG;aACR,WAAW,GACvB;aACY,aAAa,GAAG;aAChB,SAAS,GAAG;aACZ,YAAY,GAAG;aACf,WAAW,GAAG;YAAC;YAAsB;SAAmB;;AA0C5E;AAEO,MAAMgB,6CAA6CC,eAAe,oBAAoB,CAAC;IAC1F,gBAAgBlB;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
2
|
-
import { CompleteMultiPartUploadUseCase
|
|
3
|
-
|
|
4
|
-
declare class CompleteMultiPartUploadUseCaseImpl implements CompleteMultiPartUploadUseCaseAbstraction.Interface {
|
|
2
|
+
import { CompleteMultiPartUploadUseCase } from "@webiny/api-file-manager/features/upload/CompleteMultiPartUpload/index.js";
|
|
3
|
+
declare class CompleteMultiPartUploadUseCaseImpl implements CompleteMultiPartUploadUseCase.Interface {
|
|
5
4
|
private readonly tenantContext;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
execute(params: CompleteMultiPartUploadUseCaseAbstraction.Params): Promise<void>;
|
|
5
|
+
constructor(tenantContext: TenantContext.Interface);
|
|
6
|
+
execute(params: CompleteMultiPartUploadUseCase.Params): Promise<void>;
|
|
9
7
|
}
|
|
10
|
-
export declare const
|
|
8
|
+
export declare const CompleteMultiPartUploadUseCaseImplementation: typeof CompleteMultiPartUploadUseCaseImpl & {
|
|
11
9
|
__abstraction: import("@webiny/di").Abstraction<import("@webiny/api-file-manager/features/upload/CompleteMultiPartUpload/abstractions").ICompleteMultiPartUploadUseCase>;
|
|
12
10
|
};
|
|
13
11
|
export {};
|
|
@@ -2,15 +2,13 @@ import node_path from "node:path";
|
|
|
2
2
|
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
4
4
|
import { CompleteMultiPartUploadUseCase } from "@webiny/api-file-manager/features/upload/CompleteMultiPartUpload/index.js";
|
|
5
|
-
import { FileManagerServerConfig } from "../FileManagerServerConfig/abstractions.js";
|
|
6
5
|
class CompleteMultiPartUploadUseCaseImpl {
|
|
7
|
-
constructor(tenantContext
|
|
6
|
+
constructor(tenantContext){
|
|
8
7
|
this.tenantContext = tenantContext;
|
|
9
|
-
this.config = config;
|
|
10
8
|
}
|
|
11
9
|
async execute(params) {
|
|
12
10
|
const { fileKey, uploadId } = params;
|
|
13
|
-
const storagePath =
|
|
11
|
+
const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);
|
|
14
12
|
const tenant = this.tenantContext.getTenant();
|
|
15
13
|
assertNoTraversal(uploadId);
|
|
16
14
|
for (const seg of fileKey.split(/[\\/]/))assertNoTraversal(seg);
|
|
@@ -50,13 +48,12 @@ function assertPathContained(resolved, root) {
|
|
|
50
48
|
const normalizedRoot = node_path.resolve(root);
|
|
51
49
|
if (!normalized.startsWith(normalizedRoot + node_path.sep) && normalized !== normalizedRoot) throw new Error("Path escapes tenant root.");
|
|
52
50
|
}
|
|
53
|
-
const
|
|
51
|
+
const CompleteMultiPartUploadUseCaseImplementation = CompleteMultiPartUploadUseCase.createImplementation({
|
|
54
52
|
implementation: CompleteMultiPartUploadUseCaseImpl,
|
|
55
53
|
dependencies: [
|
|
56
|
-
TenantContext
|
|
57
|
-
FileManagerServerConfig
|
|
54
|
+
TenantContext
|
|
58
55
|
]
|
|
59
56
|
});
|
|
60
|
-
export {
|
|
57
|
+
export { CompleteMultiPartUploadUseCaseImplementation };
|
|
61
58
|
|
|
62
59
|
//# sourceMappingURL=CompleteMultiPartUploadUseCase.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js","sources":["../../../src/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.ts"],"sourcesContent":["import path from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { readdir } from \"node:fs/promises\";\nimport { readFile } from \"node:fs/promises\";\nimport { writeFile } from \"node:fs/promises\";\nimport { rm } from \"node:fs/promises\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { CompleteMultiPartUploadUseCase
|
|
1
|
+
{"version":3,"file":"features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js","sources":["../../../src/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.ts"],"sourcesContent":["import path from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport { readdir } from \"node:fs/promises\";\nimport { readFile } from \"node:fs/promises\";\nimport { writeFile } from \"node:fs/promises\";\nimport { rm } from \"node:fs/promises\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { CompleteMultiPartUploadUseCase } from \"@webiny/api-file-manager/features/upload/CompleteMultiPartUpload/index.js\";\n\nclass CompleteMultiPartUploadUseCaseImpl implements CompleteMultiPartUploadUseCase.Interface {\n public constructor(private readonly tenantContext: TenantContext.Interface) {}\n\n public async execute(params: CompleteMultiPartUploadUseCase.Params): Promise<void> {\n const { fileKey, uploadId } = params;\n const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);\n const tenant = this.tenantContext.getTenant();\n\n assertNoTraversal(uploadId);\n for (const seg of fileKey.split(/[\\\\/]/)) {\n assertNoTraversal(seg);\n }\n\n const tenantRoot = path.resolve(path.join(storagePath, \"tenants\", tenant.id, \"files\"));\n const destPath = path.resolve(path.join(tenantRoot, fileKey));\n assertPathContained(destPath, tenantRoot);\n\n const multipartDir = path.join(storagePath, \"tenants\", tenant.id, \"multipart\", uploadId);\n\n const entries = await readdir(multipartDir);\n const sorted = entries\n .filter(name => name.startsWith(\"part-\"))\n .sort((a, b) => {\n const numA = parseInt(a.replace(\"part-\", \"\"), 10);\n const numB = parseInt(b.replace(\"part-\", \"\"), 10);\n return numA - numB;\n });\n\n const destDir = path.dirname(destPath);\n await mkdir(destDir, { recursive: true });\n\n const buffers: Buffer[] = [];\n for (const partName of sorted) {\n const partPath = path.join(multipartDir, partName);\n const partData = await readFile(partPath);\n buffers.push(partData);\n }\n\n const finalBuffer = Buffer.concat(buffers);\n await writeFile(destPath, finalBuffer);\n\n await rm(multipartDir, { recursive: true, force: true });\n }\n}\n\nfunction assertNoTraversal(segment: string): void {\n if (segment.includes(\"..\") || segment.includes(\"/\") || segment.includes(\"\\\\\")) {\n throw new Error(\"Invalid path segment.\");\n }\n}\n\nfunction assertPathContained(resolved: string, root: string): void {\n const normalized = path.resolve(resolved);\n const normalizedRoot = path.resolve(root);\n if (!normalized.startsWith(normalizedRoot + path.sep) && normalized !== normalizedRoot) {\n throw new Error(\"Path escapes tenant root.\");\n }\n}\n\nexport const CompleteMultiPartUploadUseCaseImplementation =\n CompleteMultiPartUploadUseCase.createImplementation({\n implementation: CompleteMultiPartUploadUseCaseImpl,\n dependencies: [TenantContext]\n });\n"],"names":["CompleteMultiPartUploadUseCaseImpl","tenantContext","params","fileKey","uploadId","storagePath","String","process","tenant","assertNoTraversal","seg","tenantRoot","path","destPath","assertPathContained","multipartDir","entries","readdir","sorted","name","a","b","numA","parseInt","numB","destDir","mkdir","buffers","partName","partPath","partData","readFile","finalBuffer","Buffer","writeFile","rm","segment","Error","resolved","root","normalized","normalizedRoot","CompleteMultiPartUploadUseCaseImplementation","CompleteMultiPartUploadUseCase","TenantContext"],"mappings":";;;;AASA,MAAMA;IACF,YAAoCC,aAAsC,CAAE;aAAxCA,aAAa,GAAbA;IAAyC;IAE7E,MAAa,QAAQC,MAA6C,EAAiB;QAC/E,MAAM,EAAEC,OAAO,EAAEC,QAAQ,EAAE,GAAGF;QAC9B,MAAMG,cAAcC,OAAOC,QAAQ,GAAG,CAAC,yBAAyB;QAChE,MAAMC,SAAS,IAAI,CAAC,aAAa,CAAC,SAAS;QAE3CC,kBAAkBL;QAClB,KAAK,MAAMM,OAAOP,QAAQ,KAAK,CAAC,SAC5BM,kBAAkBC;QAGtB,MAAMC,aAAaC,UAAAA,OAAY,CAACA,UAAAA,IAAS,CAACP,aAAa,WAAWG,OAAO,EAAE,EAAE;QAC7E,MAAMK,WAAWD,UAAAA,OAAY,CAACA,UAAAA,IAAS,CAACD,YAAYR;QACpDW,oBAAoBD,UAAUF;QAE9B,MAAMI,eAAeH,UAAAA,IAAS,CAACP,aAAa,WAAWG,OAAO,EAAE,EAAE,aAAaJ;QAE/E,MAAMY,UAAU,MAAMC,QAAQF;QAC9B,MAAMG,SAASF,QACV,MAAM,CAACG,CAAAA,OAAQA,KAAK,UAAU,CAAC,UAC/B,IAAI,CAAC,CAACC,GAAGC;YACN,MAAMC,OAAOC,SAASH,EAAE,OAAO,CAAC,SAAS,KAAK;YAC9C,MAAMI,OAAOD,SAASF,EAAE,OAAO,CAAC,SAAS,KAAK;YAC9C,OAAOC,OAAOE;QAClB;QAEJ,MAAMC,UAAUb,UAAAA,OAAY,CAACC;QAC7B,MAAMa,MAAMD,SAAS;YAAE,WAAW;QAAK;QAEvC,MAAME,UAAoB,EAAE;QAC5B,KAAK,MAAMC,YAAYV,OAAQ;YAC3B,MAAMW,WAAWjB,UAAAA,IAAS,CAACG,cAAca;YACzC,MAAME,WAAW,MAAMC,SAASF;YAChCF,QAAQ,IAAI,CAACG;QACjB;QAEA,MAAME,cAAcC,OAAO,MAAM,CAACN;QAClC,MAAMO,UAAUrB,UAAUmB;QAE1B,MAAMG,GAAGpB,cAAc;YAAE,WAAW;YAAM,OAAO;QAAK;IAC1D;AACJ;AAEA,SAASN,kBAAkB2B,OAAe;IACtC,IAAIA,QAAQ,QAAQ,CAAC,SAASA,QAAQ,QAAQ,CAAC,QAAQA,QAAQ,QAAQ,CAAC,OACpE,MAAM,IAAIC,MAAM;AAExB;AAEA,SAASvB,oBAAoBwB,QAAgB,EAAEC,IAAY;IACvD,MAAMC,aAAa5B,UAAAA,OAAY,CAAC0B;IAChC,MAAMG,iBAAiB7B,UAAAA,OAAY,CAAC2B;IACpC,IAAI,CAACC,WAAW,UAAU,CAACC,iBAAiB7B,UAAAA,GAAQ,KAAK4B,eAAeC,gBACpE,MAAM,IAAIJ,MAAM;AAExB;AAEO,MAAMK,+CACTC,+BAA+B,oBAAoB,CAAC;IAChD,gBAAgB3C;IAChB,cAAc;QAAC4C;KAAc;AACjC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createFeature } from "@webiny/feature/api";
|
|
2
|
-
import {
|
|
2
|
+
import { CompleteMultiPartUploadUseCaseImplementation } from "./CompleteMultiPartUploadUseCase.js";
|
|
3
3
|
const CompleteMultiPartUploadFeature = createFeature({
|
|
4
4
|
name: "FileManagerServer/CompleteMultiPartUpload",
|
|
5
5
|
register (container) {
|
|
6
|
-
container.register(
|
|
6
|
+
container.register(CompleteMultiPartUploadUseCaseImplementation);
|
|
7
7
|
}
|
|
8
8
|
});
|
|
9
9
|
export { CompleteMultiPartUploadFeature };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/CompleteMultiPartUpload/feature.js","sources":["../../../src/features/CompleteMultiPartUpload/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport {
|
|
1
|
+
{"version":3,"file":"features/CompleteMultiPartUpload/feature.js","sources":["../../../src/features/CompleteMultiPartUpload/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { CompleteMultiPartUploadUseCaseImplementation } from \"./CompleteMultiPartUploadUseCase.js\";\n\nexport const CompleteMultiPartUploadFeature = createFeature({\n name: \"FileManagerServer/CompleteMultiPartUpload\",\n register(container) {\n container.register(CompleteMultiPartUploadUseCaseImplementation);\n }\n});\n"],"names":["CompleteMultiPartUploadFeature","createFeature","container","CompleteMultiPartUploadUseCaseImplementation"],"mappings":";;AAGO,MAAMA,iCAAiCC,cAAc;IACxD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
|