@webiny/api-file-manager-server 0.0.0-unstable.9f53ea597d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/assetDelivery/LocalAssetResolver.d.ts +13 -0
  4. package/assetDelivery/LocalAssetResolver.js +40 -0
  5. package/assetDelivery/LocalAssetResolver.js.map +1 -0
  6. package/assetDelivery/LocalContentsReader.d.ts +7 -0
  7. package/assetDelivery/LocalContentsReader.js +18 -0
  8. package/assetDelivery/LocalContentsReader.js.map +1 -0
  9. package/assetDelivery/LocalOutputStrategy.d.ts +11 -0
  10. package/assetDelivery/LocalOutputStrategy.js +21 -0
  11. package/assetDelivery/LocalOutputStrategy.js.map +1 -0
  12. package/assetDelivery/LocalSharpTransform.d.ts +24 -0
  13. package/assetDelivery/LocalSharpTransform.js +174 -0
  14. package/assetDelivery/LocalSharpTransform.js.map +1 -0
  15. package/assetDelivery/abstractions.d.ts +8 -0
  16. package/assetDelivery/abstractions.js +6 -0
  17. package/assetDelivery/abstractions.js.map +1 -0
  18. package/assetDelivery/assetDeliveryConfig.d.ts +2 -0
  19. package/assetDelivery/assetDeliveryConfig.js +15 -0
  20. package/assetDelivery/assetDeliveryConfig.js.map +1 -0
  21. package/assetDelivery/createAssetDelivery.d.ts +3 -0
  22. package/assetDelivery/createAssetDelivery.js +7 -0
  23. package/assetDelivery/createAssetDelivery.js.map +1 -0
  24. package/assetDelivery/feature.d.ts +5 -0
  25. package/assetDelivery/feature.js +33 -0
  26. package/assetDelivery/feature.js.map +1 -0
  27. package/assetDelivery/types.d.ts +8 -0
  28. package/assetDelivery/types.js +0 -0
  29. package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.d.ts +18 -0
  30. package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js +58 -0
  31. package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js.map +1 -0
  32. package/features/CleanupStaleMultipartUploads/feature.d.ts +4 -0
  33. package/features/CleanupStaleMultipartUploads/feature.js +11 -0
  34. package/features/CleanupStaleMultipartUploads/feature.js.map +1 -0
  35. package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.d.ts +11 -0
  36. package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js +59 -0
  37. package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js.map +1 -0
  38. package/features/CompleteMultiPartUpload/feature.d.ts +4 -0
  39. package/features/CompleteMultiPartUpload/feature.js +11 -0
  40. package/features/CompleteMultiPartUpload/feature.js.map +1 -0
  41. package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.d.ts +14 -0
  42. package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.js +59 -0
  43. package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.js.map +1 -0
  44. package/features/CreateMultiPartUpload/feature.d.ts +4 -0
  45. package/features/CreateMultiPartUpload/feature.js +11 -0
  46. package/features/CreateMultiPartUpload/feature.js.map +1 -0
  47. package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.d.ts +13 -0
  48. package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.js +31 -0
  49. package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.js.map +1 -0
  50. package/features/DeleteFileFromDisk/feature.d.ts +4 -0
  51. package/features/DeleteFileFromDisk/feature.js +11 -0
  52. package/features/DeleteFileFromDisk/feature.js.map +1 -0
  53. package/features/ExtractMetadata/ExtractMetadataHandler.d.ts +1 -0
  54. package/features/ExtractMetadata/ExtractMetadataHandler.js +1 -0
  55. package/features/ExtractMetadata/ExtractMetadataTask.d.ts +22 -0
  56. package/features/ExtractMetadata/ExtractMetadataTask.js +81 -0
  57. package/features/ExtractMetadata/ExtractMetadataTask.js.map +1 -0
  58. package/features/ExtractMetadata/feature.d.ts +4 -0
  59. package/features/ExtractMetadata/feature.js +13 -0
  60. package/features/ExtractMetadata/feature.js.map +1 -0
  61. package/features/FlushCache/FlushCacheOnFileBeforeUpdateHandler.d.ts +8 -0
  62. package/features/FlushCache/FlushCacheOnFileBeforeUpdateHandler.js +11 -0
  63. package/features/FlushCache/FlushCacheOnFileBeforeUpdateHandler.js.map +1 -0
  64. package/features/FlushCache/FlushCacheOnFileDeleteHandler.d.ts +8 -0
  65. package/features/FlushCache/FlushCacheOnFileDeleteHandler.js +11 -0
  66. package/features/FlushCache/FlushCacheOnFileDeleteHandler.js.map +1 -0
  67. package/features/FlushCache/feature.d.ts +4 -0
  68. package/features/FlushCache/feature.js +13 -0
  69. package/features/FlushCache/feature.js.map +1 -0
  70. package/features/GetFileContentsById/GetFileContentsByIdUseCase.d.ts +13 -0
  71. package/features/GetFileContentsById/GetFileContentsByIdUseCase.js +35 -0
  72. package/features/GetFileContentsById/GetFileContentsByIdUseCase.js.map +1 -0
  73. package/features/GetFileContentsById/feature.d.ts +4 -0
  74. package/features/GetFileContentsById/feature.js +11 -0
  75. package/features/GetFileContentsById/feature.js.map +1 -0
  76. package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.d.ts +13 -0
  77. package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.js +69 -0
  78. package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.js.map +1 -0
  79. package/features/GetFileContentsByKey/feature.d.ts +4 -0
  80. package/features/GetFileContentsByKey/feature.js +11 -0
  81. package/features/GetFileContentsByKey/feature.js.map +1 -0
  82. package/features/GetUploadPayload/GetUploadPayloadUseCase.d.ts +16 -0
  83. package/features/GetUploadPayload/GetUploadPayloadUseCase.js +58 -0
  84. package/features/GetUploadPayload/GetUploadPayloadUseCase.js.map +1 -0
  85. package/features/GetUploadPayload/feature.d.ts +4 -0
  86. package/features/GetUploadPayload/feature.js +11 -0
  87. package/features/GetUploadPayload/feature.js.map +1 -0
  88. package/index.d.ts +4 -0
  89. package/index.js +42 -0
  90. package/index.js.map +1 -0
  91. package/package.json +44 -0
  92. package/routes/uploadRoutes.d.ts +4 -0
  93. package/routes/uploadRoutes.js +130 -0
  94. package/routes/uploadRoutes.js.map +1 -0
  95. package/types.d.ts +10 -0
  96. package/types.js +1 -0
  97. package/utils/resolveServerUrl.d.ts +2 -0
  98. package/utils/resolveServerUrl.js +10 -0
  99. package/utils/resolveServerUrl.js.map +1 -0
  100. package/utils/uploadToken.d.ts +3 -0
  101. package/utils/uploadToken.js +22 -0
  102. package/utils/uploadToken.js.map +1 -0
@@ -0,0 +1,35 @@
1
+ import { promises } from "node:fs";
2
+ import { Result } from "@webiny/feature/api";
3
+ import { GetFileContentsByIdUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsById/index.js";
4
+ import { FileNotFoundError, FilePersistenceError } from "@webiny/api-file-manager/domain/file/errors.js";
5
+ import { MetadataReader } from "@webiny/api-file-manager/features/upload/ReadFileMetadata/abstractions.js";
6
+ class GetFileContentsByIdUseCaseImpl {
7
+ constructor(metadataReader){
8
+ this.metadataReader = metadataReader;
9
+ }
10
+ async execute(fileId) {
11
+ const metadata = await this.metadataReader.read(fileId);
12
+ if (!metadata) return Result.fail(new FileNotFoundError(fileId));
13
+ const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);
14
+ try {
15
+ const filePath = `${storagePath}/${metadata.bucketKey}`;
16
+ const buffer = await promises.readFile(filePath);
17
+ return Result.ok({
18
+ buffer,
19
+ contentType: metadata.contentType
20
+ });
21
+ } catch (error) {
22
+ if ("ENOENT" === error.code) return Result.fail(new FileNotFoundError(fileId));
23
+ return Result.fail(new FilePersistenceError(error instanceof Error ? error : new Error(String(error))));
24
+ }
25
+ }
26
+ }
27
+ const GetFileContentsByIdUseCaseImplementation = GetFileContentsByIdUseCase.createImplementation({
28
+ implementation: GetFileContentsByIdUseCaseImpl,
29
+ dependencies: [
30
+ MetadataReader
31
+ ]
32
+ });
33
+ export { GetFileContentsByIdUseCaseImplementation };
34
+
35
+ //# sourceMappingURL=GetFileContentsByIdUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/GetFileContentsById/GetFileContentsByIdUseCase.js","sources":["../../../src/features/GetFileContentsById/GetFileContentsByIdUseCase.ts"],"sourcesContent":["import { promises as fs } from \"node:fs\";\nimport { Result } from \"@webiny/feature/api\";\nimport { GetFileContentsByIdUseCase } from \"@webiny/api-file-manager/features/file/GetFileContentsById/index.js\";\nimport type { FileContents } from \"@webiny/api-file-manager/features/file/GetFileContentsById/index.js\";\nimport {\n FileNotFoundError,\n FilePersistenceError\n} from \"@webiny/api-file-manager/domain/file/errors.js\";\nimport { MetadataReader } from \"@webiny/api-file-manager/features/upload/ReadFileMetadata/abstractions.js\";\n\nclass GetFileContentsByIdUseCaseImpl implements GetFileContentsByIdUseCase.Interface {\n private readonly metadataReader: MetadataReader.Interface;\n\n public constructor(metadataReader: MetadataReader.Interface) {\n this.metadataReader = metadataReader;\n }\n\n public async execute(\n fileId: string\n ): Promise<Result<FileContents, GetFileContentsByIdUseCase.Error>> {\n const metadata = await this.metadataReader.read(fileId);\n if (!metadata) {\n return Result.fail(new FileNotFoundError(fileId));\n }\n\n const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);\n\n try {\n const filePath = `${storagePath}/${metadata.bucketKey}`;\n const buffer = await fs.readFile(filePath);\n return Result.ok({ buffer, contentType: metadata.contentType });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return Result.fail(new FileNotFoundError(fileId));\n }\n return Result.fail(\n new FilePersistenceError(error instanceof Error ? error : new Error(String(error)))\n );\n }\n }\n}\n\nexport const GetFileContentsByIdUseCaseImplementation =\n GetFileContentsByIdUseCase.createImplementation({\n implementation: GetFileContentsByIdUseCaseImpl,\n dependencies: [MetadataReader]\n });\n"],"names":["GetFileContentsByIdUseCaseImpl","metadataReader","fileId","metadata","Result","FileNotFoundError","storagePath","String","process","filePath","buffer","fs","error","FilePersistenceError","Error","GetFileContentsByIdUseCaseImplementation","GetFileContentsByIdUseCase","MetadataReader"],"mappings":";;;;;AAUA,MAAMA;IAGF,YAAmBC,cAAwC,CAAE;QACzD,IAAI,CAAC,cAAc,GAAGA;IAC1B;IAEA,MAAa,QACTC,MAAc,EACiD;QAC/D,MAAMC,WAAW,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAACD;QAChD,IAAI,CAACC,UACD,OAAOC,OAAO,IAAI,CAAC,IAAIC,kBAAkBH;QAG7C,MAAMI,cAAcC,OAAOC,QAAQ,GAAG,CAAC,yBAAyB;QAEhE,IAAI;YACA,MAAMC,WAAW,GAAGH,YAAY,CAAC,EAAEH,SAAS,SAAS,EAAE;YACvD,MAAMO,SAAS,MAAMC,SAAAA,QAAW,CAACF;YACjC,OAAOL,OAAO,EAAE,CAAC;gBAAEM;gBAAQ,aAAaP,SAAS,WAAW;YAAC;QACjE,EAAE,OAAOS,OAAO;YACZ,IAAKA,AAAyC,aAAzCA,MAAgC,IAAI,EACrC,OAAOR,OAAO,IAAI,CAAC,IAAIC,kBAAkBH;YAE7C,OAAOE,OAAO,IAAI,CACd,IAAIS,qBAAqBD,iBAAiBE,QAAQF,QAAQ,IAAIE,MAAMP,OAAOK;QAEnF;IACJ;AACJ;AAEO,MAAMG,2CACTC,2BAA2B,oBAAoB,CAAC;IAC5C,gBAAgBhB;IAChB,cAAc;QAACiB;KAAe;AAClC"}
@@ -0,0 +1,4 @@
1
+ export declare const GetFileContentsByIdFeature: {
2
+ name: string;
3
+ register(container: import("@webiny/di").Container): void;
4
+ };
@@ -0,0 +1,11 @@
1
+ import { createFeature } from "@webiny/feature/api";
2
+ import { GetFileContentsByIdUseCaseImplementation } from "./GetFileContentsByIdUseCase.js";
3
+ const GetFileContentsByIdFeature = createFeature({
4
+ name: "FileManagerServer/GetFileContentsById",
5
+ register (container) {
6
+ container.register(GetFileContentsByIdUseCaseImplementation);
7
+ }
8
+ });
9
+ export { GetFileContentsByIdFeature };
10
+
11
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/GetFileContentsById/feature.js","sources":["../../../src/features/GetFileContentsById/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { GetFileContentsByIdUseCaseImplementation } from \"./GetFileContentsByIdUseCase.js\";\n\nexport const GetFileContentsByIdFeature = createFeature({\n name: \"FileManagerServer/GetFileContentsById\",\n register(container) {\n container.register(GetFileContentsByIdUseCaseImplementation);\n }\n});\n"],"names":["GetFileContentsByIdFeature","createFeature","container","GetFileContentsByIdUseCaseImplementation"],"mappings":";;AAGO,MAAMA,6BAA6BC,cAAc;IACpD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
@@ -0,0 +1,13 @@
1
+ import { Result } from "@webiny/feature/api";
2
+ import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
3
+ import { GetFileContentsByKeyUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsByKey/index.js";
4
+ import type { FileContents } from "@webiny/api-file-manager/features/file/GetFileContentsById/index.js";
5
+ declare class GetFileContentsByKeyUseCaseImpl implements GetFileContentsByKeyUseCase.Interface {
6
+ private readonly tenantContext;
7
+ constructor(tenantContext: TenantContext.Interface);
8
+ execute(key: string): Promise<Result<FileContents, GetFileContentsByKeyUseCase.Error>>;
9
+ }
10
+ export declare const GetFileContentsByKeyUseCaseImplementation: typeof GetFileContentsByKeyUseCaseImpl & {
11
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-file-manager/features/file/GetFileContentsByKey/abstractions").IGetFileContentsByKeyUseCase>;
12
+ };
13
+ export {};
@@ -0,0 +1,69 @@
1
+ import { promises } from "node:fs";
2
+ import { Result } from "@webiny/feature/api";
3
+ import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
4
+ import { GetFileContentsByKeyUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsByKey/index.js";
5
+ import { FileNotFoundError, FilePersistenceError } from "@webiny/api-file-manager/domain/file/errors.js";
6
+ const CONTENT_TYPE_MAP = {
7
+ jpg: "image/jpeg",
8
+ jpeg: "image/jpeg",
9
+ png: "image/png",
10
+ gif: "image/gif",
11
+ webp: "image/webp",
12
+ svg: "image/svg+xml",
13
+ ico: "image/x-icon",
14
+ bmp: "image/bmp",
15
+ tiff: "image/tiff",
16
+ tif: "image/tiff",
17
+ pdf: "application/pdf",
18
+ json: "application/json",
19
+ xml: "application/xml",
20
+ zip: "application/zip",
21
+ gz: "application/gzip",
22
+ txt: "text/plain",
23
+ html: "text/html",
24
+ htm: "text/html",
25
+ css: "text/css",
26
+ js: "application/javascript",
27
+ ts: "application/typescript",
28
+ mp4: "video/mp4",
29
+ webm: "video/webm",
30
+ mp3: "audio/mpeg",
31
+ wav: "audio/wav",
32
+ ogg: "audio/ogg"
33
+ };
34
+ function resolveContentType(key) {
35
+ const ext = key.split(".").pop()?.toLowerCase();
36
+ if (!ext) return "application/octet-stream";
37
+ return CONTENT_TYPE_MAP[ext] ?? "application/octet-stream";
38
+ }
39
+ class GetFileContentsByKeyUseCaseImpl {
40
+ constructor(tenantContext){
41
+ this.tenantContext = tenantContext;
42
+ }
43
+ async execute(key) {
44
+ const tenant = this.tenantContext.getTenant();
45
+ const bucketKey = `tenants/${tenant.id}/files/${key}`;
46
+ const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);
47
+ const filePath = `${storagePath}/${bucketKey}`;
48
+ try {
49
+ const buffer = await promises.readFile(filePath);
50
+ const contentType = resolveContentType(key);
51
+ return Result.ok({
52
+ buffer,
53
+ contentType
54
+ });
55
+ } catch (error) {
56
+ if ("ENOENT" === error.code) return Result.fail(new FileNotFoundError(key));
57
+ return Result.fail(new FilePersistenceError(error instanceof Error ? error : new Error(String(error))));
58
+ }
59
+ }
60
+ }
61
+ const GetFileContentsByKeyUseCaseImplementation = GetFileContentsByKeyUseCase.createImplementation({
62
+ implementation: GetFileContentsByKeyUseCaseImpl,
63
+ dependencies: [
64
+ TenantContext
65
+ ]
66
+ });
67
+ export { GetFileContentsByKeyUseCaseImplementation };
68
+
69
+ //# sourceMappingURL=GetFileContentsByKeyUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/GetFileContentsByKey/GetFileContentsByKeyUseCase.js","sources":["../../../src/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.ts"],"sourcesContent":["import { promises as fs } from \"node:fs\";\nimport { Result } from \"@webiny/feature/api\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { GetFileContentsByKeyUseCase } from \"@webiny/api-file-manager/features/file/GetFileContentsByKey/index.js\";\nimport type { FileContents } from \"@webiny/api-file-manager/features/file/GetFileContentsById/index.js\";\nimport {\n FileNotFoundError,\n FilePersistenceError\n} from \"@webiny/api-file-manager/domain/file/errors.js\";\n\nconst CONTENT_TYPE_MAP: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n bmp: \"image/bmp\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n pdf: \"application/pdf\",\n json: \"application/json\",\n xml: \"application/xml\",\n zip: \"application/zip\",\n gz: \"application/gzip\",\n txt: \"text/plain\",\n html: \"text/html\",\n htm: \"text/html\",\n css: \"text/css\",\n js: \"application/javascript\",\n ts: \"application/typescript\",\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n ogg: \"audio/ogg\"\n};\n\nfunction resolveContentType(key: string): string {\n const ext = key.split(\".\").pop()?.toLowerCase();\n if (!ext) {\n return \"application/octet-stream\";\n }\n return CONTENT_TYPE_MAP[ext] ?? \"application/octet-stream\";\n}\n\nclass GetFileContentsByKeyUseCaseImpl implements GetFileContentsByKeyUseCase.Interface {\n constructor(private readonly tenantContext: TenantContext.Interface) {}\n\n async execute(key: string): Promise<Result<FileContents, GetFileContentsByKeyUseCase.Error>> {\n const tenant = this.tenantContext.getTenant();\n const bucketKey = `tenants/${tenant.id}/files/${key}`;\n const storagePath = String(process.env.WEBINY_LOCAL_STORAGE_PATH);\n const filePath = `${storagePath}/${bucketKey}`;\n\n try {\n const buffer = await fs.readFile(filePath);\n const contentType = resolveContentType(key);\n return Result.ok({ buffer, contentType });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return Result.fail(new FileNotFoundError(key));\n }\n return Result.fail(\n new FilePersistenceError(error instanceof Error ? error : new Error(String(error)))\n );\n }\n }\n}\n\nexport const GetFileContentsByKeyUseCaseImplementation =\n GetFileContentsByKeyUseCase.createImplementation({\n implementation: GetFileContentsByKeyUseCaseImpl,\n dependencies: [TenantContext]\n });\n"],"names":["CONTENT_TYPE_MAP","resolveContentType","key","ext","GetFileContentsByKeyUseCaseImpl","tenantContext","tenant","bucketKey","storagePath","String","process","filePath","buffer","fs","contentType","Result","error","FileNotFoundError","FilePersistenceError","Error","GetFileContentsByKeyUseCaseImplementation","GetFileContentsByKeyUseCase","TenantContext"],"mappings":";;;;;AAUA,MAAMA,mBAA2C;IAC7C,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,IAAI;IACJ,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,MAAM;IACN,KAAK;IACL,KAAK;IACL,KAAK;AACT;AAEA,SAASC,mBAAmBC,GAAW;IACnC,MAAMC,MAAMD,IAAI,KAAK,CAAC,KAAK,GAAG,IAAI;IAClC,IAAI,CAACC,KACD,OAAO;IAEX,OAAOH,gBAAgB,CAACG,IAAI,IAAI;AACpC;AAEA,MAAMC;IACF,YAA6BC,aAAsC,CAAE;aAAxCA,aAAa,GAAbA;IAAyC;IAEtE,MAAM,QAAQH,GAAW,EAAoE;QACzF,MAAMI,SAAS,IAAI,CAAC,aAAa,CAAC,SAAS;QAC3C,MAAMC,YAAY,CAAC,QAAQ,EAAED,OAAO,EAAE,CAAC,OAAO,EAAEJ,KAAK;QACrD,MAAMM,cAAcC,OAAOC,QAAQ,GAAG,CAAC,yBAAyB;QAChE,MAAMC,WAAW,GAAGH,YAAY,CAAC,EAAED,WAAW;QAE9C,IAAI;YACA,MAAMK,SAAS,MAAMC,SAAAA,QAAW,CAACF;YACjC,MAAMG,cAAcb,mBAAmBC;YACvC,OAAOa,OAAO,EAAE,CAAC;gBAAEH;gBAAQE;YAAY;QAC3C,EAAE,OAAOE,OAAO;YACZ,IAAKA,AAAyC,aAAzCA,MAAgC,IAAI,EACrC,OAAOD,OAAO,IAAI,CAAC,IAAIE,kBAAkBf;YAE7C,OAAOa,OAAO,IAAI,CACd,IAAIG,qBAAqBF,iBAAiBG,QAAQH,QAAQ,IAAIG,MAAMV,OAAOO;QAEnF;IACJ;AACJ;AAEO,MAAMI,4CACTC,4BAA4B,oBAAoB,CAAC;IAC7C,gBAAgBjB;IAChB,cAAc;QAACkB;KAAc;AACjC"}
@@ -0,0 +1,4 @@
1
+ export declare const GetFileContentsByKeyFeature: {
2
+ name: string;
3
+ register(container: import("@webiny/di").Container): void;
4
+ };
@@ -0,0 +1,11 @@
1
+ import { createFeature } from "@webiny/feature/api";
2
+ import { GetFileContentsByKeyUseCaseImplementation } from "./GetFileContentsByKeyUseCase.js";
3
+ const GetFileContentsByKeyFeature = createFeature({
4
+ name: "FileManagerServer/GetFileContentsByKey",
5
+ register (container) {
6
+ container.register(GetFileContentsByKeyUseCaseImplementation);
7
+ }
8
+ });
9
+ export { GetFileContentsByKeyFeature };
10
+
11
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/GetFileContentsByKey/feature.js","sources":["../../../src/features/GetFileContentsByKey/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { GetFileContentsByKeyUseCaseImplementation } from \"./GetFileContentsByKeyUseCase.js\";\n\nexport const GetFileContentsByKeyFeature = createFeature({\n name: \"FileManagerServer/GetFileContentsByKey\",\n register(container) {\n container.register(GetFileContentsByKeyUseCaseImplementation);\n }\n});\n"],"names":["GetFileContentsByKeyFeature","createFeature","container","GetFileContentsByKeyUseCaseImplementation"],"mappings":";;AAGO,MAAMA,8BAA8BC,cAAc;IACrD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
@@ -0,0 +1,16 @@
1
+ import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
2
+ import { Request } from "@webiny/handler/abstractions/Request.js";
3
+ import { GetUploadPayloadUseCase } from "@webiny/api-file-manager/features/upload/GetUploadPayload/index.js";
4
+ import type { FileData } from "@webiny/api-file-manager/features/upload/types.js";
5
+ import type { UploadPayloadResponse } from "@webiny/api-file-manager/features/upload/types.js";
6
+ import type { FileManagerSettings } from "@webiny/api-file-manager/domain/settings/types.js";
7
+ declare class GetUploadPayloadUseCaseImpl implements GetUploadPayloadUseCase.Interface {
8
+ private readonly tenantContext;
9
+ private readonly request;
10
+ constructor(tenantContext: TenantContext.Interface, request: Request.Interface);
11
+ execute(file: FileData, settings: FileManagerSettings): Promise<UploadPayloadResponse>;
12
+ }
13
+ export declare const GetUploadPayloadUseCaseImplementation: typeof GetUploadPayloadUseCaseImpl & {
14
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-file-manager/features/upload/GetUploadPayload/abstractions").IGetUploadPayloadUseCase>;
15
+ };
16
+ export {};
@@ -0,0 +1,58 @@
1
+ import { validation } from "@webiny/validation";
2
+ import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
3
+ import { Request } from "@webiny/handler/abstractions/Request.js";
4
+ import { GetUploadPayloadUseCase } from "@webiny/api-file-manager/features/upload/GetUploadPayload/index.js";
5
+ import { createUploadToken } from "../../utils/uploadToken.js";
6
+ import { resolveServerUrl } from "../../utils/resolveServerUrl.js";
7
+ const UPLOAD_MAX_FILE_SIZE_DEFAULT = 1099511627776;
8
+ const sanitizeFileSizeValue = (value, defaultValue)=>{
9
+ try {
10
+ validation.validateSync(value, "required,numeric,gte:0");
11
+ return value;
12
+ } catch {
13
+ return defaultValue;
14
+ }
15
+ };
16
+ class GetUploadPayloadUseCaseImpl {
17
+ constructor(tenantContext, request){
18
+ this.tenantContext = tenantContext;
19
+ this.request = request;
20
+ }
21
+ async execute(file, settings) {
22
+ const uploadMinFileSize = sanitizeFileSizeValue(settings.uploadMinFileSize, 0);
23
+ const uploadMaxFileSize = sanitizeFileSizeValue(settings.uploadMaxFileSize, UPLOAD_MAX_FILE_SIZE_DEFAULT);
24
+ const tenant = this.tenantContext.getTenant();
25
+ const storageKey = `tenants/${tenant.id}/files/${file.key}`;
26
+ const secret = process.env.WEBINY_UPLOAD_SECRET;
27
+ const expiresAt = Date.now() + 60000;
28
+ const token = createUploadToken({
29
+ key: storageKey,
30
+ tenantId: tenant.id,
31
+ expiresAt,
32
+ uploadMinFileSize,
33
+ uploadMaxFileSize
34
+ }, secret);
35
+ const serverUrl = await resolveServerUrl(this.request);
36
+ const data = {
37
+ url: `${serverUrl}/webiny-file-upload`,
38
+ fields: {
39
+ key: storageKey,
40
+ token
41
+ }
42
+ };
43
+ return {
44
+ data: data,
45
+ file
46
+ };
47
+ }
48
+ }
49
+ const GetUploadPayloadUseCaseImplementation = GetUploadPayloadUseCase.createImplementation({
50
+ implementation: GetUploadPayloadUseCaseImpl,
51
+ dependencies: [
52
+ TenantContext,
53
+ Request
54
+ ]
55
+ });
56
+ export { GetUploadPayloadUseCaseImplementation };
57
+
58
+ //# sourceMappingURL=GetUploadPayloadUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/GetUploadPayload/GetUploadPayloadUseCase.js","sources":["../../../src/features/GetUploadPayload/GetUploadPayloadUseCase.ts"],"sourcesContent":["import { validation } from \"@webiny/validation\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { Request } from \"@webiny/handler/abstractions/Request.js\";\nimport { GetUploadPayloadUseCase } from \"@webiny/api-file-manager/features/upload/GetUploadPayload/index.js\";\nimport type { FileData } from \"@webiny/api-file-manager/features/upload/types.js\";\nimport type { UploadPayloadResponse } from \"@webiny/api-file-manager/features/upload/types.js\";\nimport type { FileManagerSettings } from \"@webiny/api-file-manager/domain/settings/types.js\";\nimport { createUploadToken } from \"~/utils/uploadToken.js\";\nimport { resolveServerUrl } from \"~/utils/resolveServerUrl.js\";\n\nconst UPLOAD_MAX_FILE_SIZE_DEFAULT = 1099511627776; /* 1TB */\n\nconst sanitizeFileSizeValue = (value: number, defaultValue: number): number => {\n try {\n validation.validateSync(value, \"required,numeric,gte:0\");\n return value;\n } catch {\n return defaultValue;\n }\n};\n\nclass GetUploadPayloadUseCaseImpl implements GetUploadPayloadUseCase.Interface {\n public constructor(\n private readonly tenantContext: TenantContext.Interface,\n private readonly request: Request.Interface\n ) {}\n\n public async execute(\n file: FileData,\n settings: FileManagerSettings\n ): Promise<UploadPayloadResponse> {\n const uploadMinFileSize = sanitizeFileSizeValue(settings.uploadMinFileSize, 0);\n const uploadMaxFileSize = sanitizeFileSizeValue(\n settings.uploadMaxFileSize,\n UPLOAD_MAX_FILE_SIZE_DEFAULT\n );\n\n const tenant = this.tenantContext.getTenant();\n const storageKey = `tenants/${tenant.id}/files/${file.key}`;\n const secret = process.env.WEBINY_UPLOAD_SECRET as string;\n const expiresAt = Date.now() + 60_000;\n\n const token = createUploadToken(\n {\n key: storageKey,\n tenantId: tenant.id,\n expiresAt,\n uploadMinFileSize,\n uploadMaxFileSize\n },\n secret\n );\n\n const serverUrl = await resolveServerUrl(this.request);\n\n const data = {\n url: `${serverUrl}/webiny-file-upload`,\n fields: {\n key: storageKey,\n token\n }\n };\n\n return {\n data: data as unknown as Record<string, unknown>,\n file\n };\n }\n}\n\nexport const GetUploadPayloadUseCaseImplementation = GetUploadPayloadUseCase.createImplementation({\n implementation: GetUploadPayloadUseCaseImpl,\n dependencies: [TenantContext, Request]\n});\n"],"names":["UPLOAD_MAX_FILE_SIZE_DEFAULT","sanitizeFileSizeValue","value","defaultValue","validation","GetUploadPayloadUseCaseImpl","tenantContext","request","file","settings","uploadMinFileSize","uploadMaxFileSize","tenant","storageKey","secret","process","expiresAt","Date","token","createUploadToken","serverUrl","resolveServerUrl","data","GetUploadPayloadUseCaseImplementation","GetUploadPayloadUseCase","TenantContext","Request"],"mappings":";;;;;;AAUA,MAAMA,+BAA+B;AAErC,MAAMC,wBAAwB,CAACC,OAAeC;IAC1C,IAAI;QACAC,WAAW,YAAY,CAACF,OAAO;QAC/B,OAAOA;IACX,EAAE,OAAM;QACJ,OAAOC;IACX;AACJ;AAEA,MAAME;IACF,YACqBC,aAAsC,EACtCC,OAA0B,CAC7C;aAFmBD,aAAa,GAAbA;aACAC,OAAO,GAAPA;IAClB;IAEH,MAAa,QACTC,IAAc,EACdC,QAA6B,EACC;QAC9B,MAAMC,oBAAoBT,sBAAsBQ,SAAS,iBAAiB,EAAE;QAC5E,MAAME,oBAAoBV,sBACtBQ,SAAS,iBAAiB,EAC1BT;QAGJ,MAAMY,SAAS,IAAI,CAAC,aAAa,CAAC,SAAS;QAC3C,MAAMC,aAAa,CAAC,QAAQ,EAAED,OAAO,EAAE,CAAC,OAAO,EAAEJ,KAAK,GAAG,EAAE;QAC3D,MAAMM,SAASC,QAAQ,GAAG,CAAC,oBAAoB;QAC/C,MAAMC,YAAYC,KAAK,GAAG,KAAK;QAE/B,MAAMC,QAAQC,kBACV;YACI,KAAKN;YACL,UAAUD,OAAO,EAAE;YACnBI;YACAN;YACAC;QACJ,GACAG;QAGJ,MAAMM,YAAY,MAAMC,iBAAiB,IAAI,CAAC,OAAO;QAErD,MAAMC,OAAO;YACT,KAAK,GAAGF,UAAU,mBAAmB,CAAC;YACtC,QAAQ;gBACJ,KAAKP;gBACLK;YACJ;QACJ;QAEA,OAAO;YACH,MAAMI;YACNd;QACJ;IACJ;AACJ;AAEO,MAAMe,wCAAwCC,wBAAwB,oBAAoB,CAAC;IAC9F,gBAAgBnB;IAChB,cAAc;QAACoB;QAAeC;KAAQ;AAC1C"}
@@ -0,0 +1,4 @@
1
+ export declare const GetUploadPayloadFeature: {
2
+ name: string;
3
+ register(container: import("@webiny/di").Container): void;
4
+ };
@@ -0,0 +1,11 @@
1
+ import { createFeature } from "@webiny/feature/api";
2
+ import { GetUploadPayloadUseCaseImplementation } from "./GetUploadPayloadUseCase.js";
3
+ const GetUploadPayloadFeature = createFeature({
4
+ name: "FileManagerServer/GetUploadPayload",
5
+ register (container) {
6
+ container.register(GetUploadPayloadUseCaseImplementation);
7
+ }
8
+ });
9
+ export { GetUploadPayloadFeature };
10
+
11
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/GetUploadPayload/feature.js","sources":["../../../src/features/GetUploadPayload/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { GetUploadPayloadUseCaseImplementation } from \"./GetUploadPayloadUseCase.js\";\n\nexport const GetUploadPayloadFeature = createFeature({\n name: \"FileManagerServer/GetUploadPayload\",\n register(container) {\n container.register(GetUploadPayloadUseCaseImplementation);\n }\n});\n"],"names":["GetUploadPayloadFeature","createFeature","container","GetUploadPayloadUseCaseImplementation"],"mappings":";;AAGO,MAAMA,0BAA0BC,cAAc;IACjD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { ContextPlugin } from "@webiny/api";
2
+ export { createFileUploadModifier } from "@webiny/api-file-manager/features/upload/index.js";
3
+ export { createAssetDelivery } from "./assetDelivery/createAssetDelivery.js";
4
+ export declare const createFileManagerServer: () => (ContextPlugin<import("@webiny/api/types.js").Context> | import("@webiny/handler/index.js").ModifyFastifyPlugin | import("@webiny/handler/index.js").RoutePlugin<import("@webiny/handler/types.js").Context>)[];
package/index.js ADDED
@@ -0,0 +1,42 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { ContextPlugin } from "@webiny/api";
3
+ import { modifyFastifyPlugin, uploadRoutesPlugin } from "./routes/uploadRoutes.js";
4
+ import { CleanupStaleMultipartUploadsFeature } from "./features/CleanupStaleMultipartUploads/feature.js";
5
+ import { DeleteFileFromDiskFeature } from "./features/DeleteFileFromDisk/feature.js";
6
+ import { ExtractMetadataFeature } from "./features/ExtractMetadata/feature.js";
7
+ import { FlushCacheFeature } from "./features/FlushCache/feature.js";
8
+ import { GetFileContentsByIdFeature } from "./features/GetFileContentsById/feature.js";
9
+ import { GetFileContentsByKeyFeature } from "./features/GetFileContentsByKey/feature.js";
10
+ import { GetUploadPayloadFeature } from "./features/GetUploadPayload/feature.js";
11
+ import { CreateMultiPartUploadFeature } from "./features/CreateMultiPartUpload/feature.js";
12
+ import { CompleteMultiPartUploadFeature } from "./features/CompleteMultiPartUpload/feature.js";
13
+ const contextPlugin = new ContextPlugin((context)=>{
14
+ const storagePath = process.env["WEBINY_LOCAL_STORAGE_PATH"];
15
+ if (!storagePath) throw new Error('"WEBINY_LOCAL_STORAGE_PATH" environment variable is not defined. Please set it to a valid local path.');
16
+ const uploadSecret = process.env["WEBINY_UPLOAD_SECRET"];
17
+ if (!uploadSecret) throw new Error('"WEBINY_UPLOAD_SECRET" environment variable is not defined. Please set it to a secret string used to sign upload tokens.');
18
+ if (!existsSync(storagePath)) mkdirSync(storagePath, {
19
+ recursive: true
20
+ });
21
+ const container = context.container;
22
+ FlushCacheFeature.register(container);
23
+ DeleteFileFromDiskFeature.register(container);
24
+ ExtractMetadataFeature.register(container);
25
+ GetFileContentsByIdFeature.register(container);
26
+ GetFileContentsByKeyFeature.register(container);
27
+ GetUploadPayloadFeature.register(container);
28
+ CreateMultiPartUploadFeature.register(container);
29
+ CompleteMultiPartUploadFeature.register(container);
30
+ CleanupStaleMultipartUploadsFeature.register(container);
31
+ });
32
+ contextPlugin.name = "fileManagerServer.context";
33
+ const createFileManagerServer = ()=>[
34
+ contextPlugin,
35
+ uploadRoutesPlugin,
36
+ modifyFastifyPlugin
37
+ ];
38
+ export { createFileUploadModifier } from "@webiny/api-file-manager/features/upload/index.js";
39
+ export { createAssetDelivery } from "./assetDelivery/createAssetDelivery.js";
40
+ export { createFileManagerServer };
41
+
42
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdirSync } from \"node:fs\";\nimport { ContextPlugin } from \"@webiny/api\";\nimport { uploadRoutesPlugin } from \"~/routes/uploadRoutes.js\";\nimport { modifyFastifyPlugin } from \"~/routes/uploadRoutes.js\";\nimport { CleanupStaleMultipartUploadsFeature } from \"~/features/CleanupStaleMultipartUploads/feature.js\";\nimport { DeleteFileFromDiskFeature } from \"~/features/DeleteFileFromDisk/feature.js\";\nimport { ExtractMetadataFeature } from \"~/features/ExtractMetadata/feature.js\";\nimport { FlushCacheFeature } from \"~/features/FlushCache/feature.js\";\nimport { GetFileContentsByIdFeature } from \"~/features/GetFileContentsById/feature.js\";\nimport { GetFileContentsByKeyFeature } from \"~/features/GetFileContentsByKey/feature.js\";\nimport { GetUploadPayloadFeature } from \"~/features/GetUploadPayload/feature.js\";\nimport { CreateMultiPartUploadFeature } from \"~/features/CreateMultiPartUpload/feature.js\";\nimport { CompleteMultiPartUploadFeature } from \"~/features/CompleteMultiPartUpload/feature.js\";\nexport { createFileUploadModifier } from \"@webiny/api-file-manager/features/upload/index.js\";\nexport { createAssetDelivery } from \"./assetDelivery/createAssetDelivery.js\";\n\nconst contextPlugin = new ContextPlugin(context => {\n const storagePath = process.env[\"WEBINY_LOCAL_STORAGE_PATH\"];\n if (!storagePath) {\n throw new Error(\n `\"WEBINY_LOCAL_STORAGE_PATH\" environment variable is not defined. Please set it to a valid local path.`\n );\n }\n\n const uploadSecret = process.env[\"WEBINY_UPLOAD_SECRET\"];\n if (!uploadSecret) {\n throw new Error(\n `\"WEBINY_UPLOAD_SECRET\" environment variable is not defined. Please set it to a secret string used to sign upload tokens.`\n );\n }\n\n if (!existsSync(storagePath)) {\n mkdirSync(storagePath, { recursive: true });\n }\n\n const container = context.container;\n\n FlushCacheFeature.register(container);\n DeleteFileFromDiskFeature.register(container);\n ExtractMetadataFeature.register(container);\n GetFileContentsByIdFeature.register(container);\n GetFileContentsByKeyFeature.register(container);\n GetUploadPayloadFeature.register(container);\n CreateMultiPartUploadFeature.register(container);\n CompleteMultiPartUploadFeature.register(container);\n CleanupStaleMultipartUploadsFeature.register(container);\n});\n\ncontextPlugin.name = `fileManagerServer.context`;\n\nexport const createFileManagerServer = () => [\n contextPlugin,\n uploadRoutesPlugin,\n modifyFastifyPlugin\n];\n"],"names":["contextPlugin","ContextPlugin","context","storagePath","process","Error","uploadSecret","existsSync","mkdirSync","container","FlushCacheFeature","DeleteFileFromDiskFeature","ExtractMetadataFeature","GetFileContentsByIdFeature","GetFileContentsByKeyFeature","GetUploadPayloadFeature","CreateMultiPartUploadFeature","CompleteMultiPartUploadFeature","CleanupStaleMultipartUploadsFeature","createFileManagerServer","uploadRoutesPlugin","modifyFastifyPlugin"],"mappings":";;;;;;;;;;;;AAiBA,MAAMA,gBAAgB,IAAIC,cAAcC,CAAAA;IACpC,MAAMC,cAAcC,QAAQ,GAAG,CAAC,4BAA4B;IAC5D,IAAI,CAACD,aACD,MAAM,IAAIE,MACN;IAIR,MAAMC,eAAeF,QAAQ,GAAG,CAAC,uBAAuB;IACxD,IAAI,CAACE,cACD,MAAM,IAAID,MACN;IAIR,IAAI,CAACE,WAAWJ,cACZK,UAAUL,aAAa;QAAE,WAAW;IAAK;IAG7C,MAAMM,YAAYP,QAAQ,SAAS;IAEnCQ,kBAAkB,QAAQ,CAACD;IAC3BE,0BAA0B,QAAQ,CAACF;IACnCG,uBAAuB,QAAQ,CAACH;IAChCI,2BAA2B,QAAQ,CAACJ;IACpCK,4BAA4B,QAAQ,CAACL;IACrCM,wBAAwB,QAAQ,CAACN;IACjCO,6BAA6B,QAAQ,CAACP;IACtCQ,+BAA+B,QAAQ,CAACR;IACxCS,oCAAoC,QAAQ,CAACT;AACjD;AAEAT,cAAc,IAAI,GAAG;AAEd,MAAMmB,0BAA0B,IAAM;QACzCnB;QACAoB;QACAC;KACH"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@webiny/api-file-manager-server",
3
+ "version": "0.0.0-unstable.9f53ea597d",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./index.js",
7
+ "./*": "./*"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/webiny/webiny-js.git"
12
+ },
13
+ "description": "File storage local server plugin for the @webiny/api-file-manager.",
14
+ "author": "Webiny Ltd",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@fastify/multipart": "10.1.0",
18
+ "@webiny/api": "0.0.0-unstable.9f53ea597d",
19
+ "@webiny/api-core": "0.0.0-unstable.9f53ea597d",
20
+ "@webiny/api-file-manager": "0.0.0-unstable.9f53ea597d",
21
+ "@webiny/background-tasks": "0.0.0-unstable.9f53ea597d",
22
+ "@webiny/feature": "0.0.0-unstable.9f53ea597d",
23
+ "@webiny/handler": "0.0.0-unstable.9f53ea597d",
24
+ "@webiny/plugins": "0.0.0-unstable.9f53ea597d",
25
+ "@webiny/utils": "0.0.0-unstable.9f53ea597d",
26
+ "@webiny/validation": "0.0.0-unstable.9f53ea597d",
27
+ "exifreader": "4.41.3",
28
+ "fastify": "5.10.0",
29
+ "sharp": "0.35.3"
30
+ },
31
+ "devDependencies": {
32
+ "@webiny/build-tools": "0.0.0-unstable.9f53ea597d",
33
+ "rimraf": "6.1.3",
34
+ "typescript": "7.0.2",
35
+ "vitest": "4.1.10"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "gitHead": "8476da73b653c89cc1474d968baf55c1b0ae0e5f",
41
+ "webiny": {
42
+ "publishFrom": "dist"
43
+ }
44
+ }
@@ -0,0 +1,4 @@
1
+ import { RoutePlugin } from "@webiny/handler/plugins/RoutePlugin.js";
2
+ import { ModifyFastifyPlugin } from "@webiny/handler/plugins/ModifyFastifyPlugin.js";
3
+ export declare const modifyFastifyPlugin: ModifyFastifyPlugin;
4
+ export declare const uploadRoutesPlugin: RoutePlugin<import("@webiny/handler/types").Context>;
@@ -0,0 +1,130 @@
1
+ import node_path from "node:path";
2
+ import { Readable } from "node:stream";
3
+ import { createHash } from "node:crypto";
4
+ import { mkdir, writeFile } from "node:fs/promises";
5
+ import { createWriteStream } from "node:fs";
6
+ import { pipeline } from "node:stream/promises";
7
+ import { RoutePlugin } from "@webiny/handler/plugins/RoutePlugin.js";
8
+ import { ModifyFastifyPlugin } from "@webiny/handler/plugins/ModifyFastifyPlugin.js";
9
+ import { verifyUploadToken } from "../utils/uploadToken.js";
10
+ const getStoragePath = ()=>String(process.env.WEBINY_LOCAL_STORAGE_PATH);
11
+ const getUploadSecret = ()=>String(process.env.WEBINY_UPLOAD_SECRET);
12
+ const isPathContained = (filePath, storagePath)=>{
13
+ const resolved = node_path.resolve(filePath);
14
+ const root = node_path.resolve(storagePath);
15
+ if (!resolved.startsWith(root + node_path.sep) && resolved !== root) return false;
16
+ return true;
17
+ };
18
+ const modifyFastifyPlugin = new ModifyFastifyPlugin((app)=>{
19
+ app.register(import("@fastify/multipart"), {
20
+ limits: {
21
+ fileSize: 5368709120
22
+ }
23
+ });
24
+ app.addContentTypeParser("application/octet-stream", {
25
+ parseAs: "buffer"
26
+ }, (_request, body, done)=>{
27
+ done(null, body);
28
+ });
29
+ });
30
+ modifyFastifyPlugin.name = "fileManagerServer.modifyFastify";
31
+ const uploadRoutesPlugin = new RoutePlugin(({ onPost, onPut })=>{
32
+ onPost("/webiny-file-upload", async (request, reply)=>{
33
+ const storagePath = getStoragePath();
34
+ const secret = getUploadSecret();
35
+ let key;
36
+ let token;
37
+ let fileChunks;
38
+ let fileSize = 0;
39
+ const parts = request.parts();
40
+ for await (const part of parts)if ("field" === part.type) {
41
+ if ("key" === part.fieldname) key = part.value;
42
+ else if ("token" === part.fieldname) token = part.value;
43
+ } else if ("file" === part.type) {
44
+ const chunks = [];
45
+ for await (const chunk of part.file){
46
+ chunks.push(chunk);
47
+ fileSize += chunk.length;
48
+ }
49
+ fileChunks = chunks;
50
+ }
51
+ if (!key || !token) return reply.code(400).send({
52
+ error: "Missing key or token."
53
+ });
54
+ if (!fileChunks) return reply.code(400).send({
55
+ error: "No file in request."
56
+ });
57
+ let payload;
58
+ try {
59
+ payload = verifyUploadToken(token, secret);
60
+ } catch (err) {
61
+ return reply.code(400).send({
62
+ error: err instanceof Error ? err.message : "Invalid token."
63
+ });
64
+ }
65
+ if (payload.key !== key) return reply.code(400).send({
66
+ error: "Token key mismatch."
67
+ });
68
+ if (payload.uploadMaxFileSize > 0 && fileSize > payload.uploadMaxFileSize) return reply.code(400).send({
69
+ error: "File exceeds maximum allowed size."
70
+ });
71
+ if (payload.uploadMinFileSize > 0 && fileSize < payload.uploadMinFileSize) return reply.code(400).send({
72
+ error: "File is below minimum allowed size."
73
+ });
74
+ const destPath = node_path.join(storagePath, key);
75
+ if (!isPathContained(destPath, storagePath)) return reply.code(400).send({
76
+ error: "Invalid path."
77
+ });
78
+ const destDir = node_path.dirname(destPath);
79
+ await mkdir(destDir, {
80
+ recursive: true
81
+ });
82
+ const writeStream = createWriteStream(destPath);
83
+ const readable = Readable.from(fileChunks);
84
+ await pipeline(readable, writeStream);
85
+ return reply.code(204).send();
86
+ });
87
+ onPut("/webiny-file-upload/parts", async (request, reply)=>{
88
+ const storagePath = getStoragePath();
89
+ const secret = getUploadSecret();
90
+ const query = request.query;
91
+ const uploadId = query["uploadId"];
92
+ const partNumberStr = query["partNumber"];
93
+ const token = query["token"];
94
+ if (!uploadId || !partNumberStr || !token) return reply.code(400).send({
95
+ error: "Missing uploadId, partNumber, or token."
96
+ });
97
+ const partNumber = parseInt(partNumberStr, 10);
98
+ if (isNaN(partNumber) || partNumber < 1) return reply.code(400).send({
99
+ error: "Invalid partNumber."
100
+ });
101
+ let payload;
102
+ try {
103
+ payload = verifyUploadToken(token, secret);
104
+ } catch (err) {
105
+ return reply.code(400).send({
106
+ error: err instanceof Error ? err.message : "Invalid token."
107
+ });
108
+ }
109
+ const expectedKey = `tenants/${payload.tenantId}/multipart/${uploadId}/part-${partNumber}`;
110
+ if (payload.key !== expectedKey) return reply.code(400).send({
111
+ error: "Token key mismatch."
112
+ });
113
+ const destPath = node_path.join(storagePath, expectedKey);
114
+ if (!isPathContained(destPath, storagePath)) return reply.code(400).send({
115
+ error: "Invalid path."
116
+ });
117
+ const destDir = node_path.dirname(destPath);
118
+ await mkdir(destDir, {
119
+ recursive: true
120
+ });
121
+ const body = request.body;
122
+ await writeFile(destPath, body);
123
+ const etag = createHash("md5").update(body).digest("hex");
124
+ return reply.code(200).header("ETag", etag).send();
125
+ });
126
+ });
127
+ uploadRoutesPlugin.name = "fileManagerServer.uploadRoutes";
128
+ export { modifyFastifyPlugin, uploadRoutesPlugin };
129
+
130
+ //# sourceMappingURL=uploadRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes/uploadRoutes.js","sources":["../../src/routes/uploadRoutes.ts"],"sourcesContent":["import path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir } from \"node:fs/promises\";\nimport { writeFile } from \"node:fs/promises\";\nimport { createWriteStream } from \"node:fs\";\nimport { pipeline } from \"node:stream/promises\";\nimport { RoutePlugin } from \"@webiny/handler/plugins/RoutePlugin.js\";\nimport { ModifyFastifyPlugin } from \"@webiny/handler/plugins/ModifyFastifyPlugin.js\";\nimport { verifyUploadToken } from \"~/utils/uploadToken.js\";\n\nconst getStoragePath = (): string => {\n return String(process.env.WEBINY_LOCAL_STORAGE_PATH);\n};\n\nconst getUploadSecret = (): string => {\n return String(process.env.WEBINY_UPLOAD_SECRET);\n};\n\nconst isPathContained = (filePath: string, storagePath: string): boolean => {\n const resolved = path.resolve(filePath);\n const root = path.resolve(storagePath);\n if (!resolved.startsWith(root + path.sep) && resolved !== root) {\n return false;\n }\n return true;\n};\n\nexport const modifyFastifyPlugin = new ModifyFastifyPlugin(app => {\n app.register(import(\"@fastify/multipart\"), {\n limits: {\n fileSize: 5 * 1024 * 1024 * 1024 /* 5 GB */\n }\n });\n\n app.addContentTypeParser(\n \"application/octet-stream\",\n { parseAs: \"buffer\" },\n (_request, body, done) => {\n done(null, body);\n }\n );\n});\n\nmodifyFastifyPlugin.name = \"fileManagerServer.modifyFastify\";\n\nexport const uploadRoutesPlugin = new RoutePlugin(({ onPost, onPut }) => {\n /* POST /webiny-file-upload — simple single-file upload via multipart/form-data.\n Form fields: key (storage path), token (HMAC upload token).\n File field: the raw file bytes (any field name). */\n onPost(\"/webiny-file-upload\", async (request, reply) => {\n const storagePath = getStoragePath();\n const secret = getUploadSecret();\n\n let key: string | undefined;\n let token: string | undefined;\n let fileChunks: Buffer[] | undefined;\n let fileSize = 0;\n\n const parts = request.parts();\n\n for await (const part of parts) {\n if (part.type === \"field\") {\n if (part.fieldname === \"key\") {\n key = part.value as string;\n } else if (part.fieldname === \"token\") {\n token = part.value as string;\n }\n } else if (part.type === \"file\") {\n /* Collect the file stream into memory so we can measure and hash it. */\n const chunks: Buffer[] = [];\n for await (const chunk of part.file) {\n chunks.push(chunk as Buffer);\n fileSize += (chunk as Buffer).length;\n }\n fileChunks = chunks;\n }\n }\n\n if (!key || !token) {\n return reply.code(400).send({ error: \"Missing key or token.\" });\n }\n\n if (!fileChunks) {\n return reply.code(400).send({ error: \"No file in request.\" });\n }\n\n let payload;\n try {\n payload = verifyUploadToken(token, secret);\n } catch (err) {\n return reply\n .code(400)\n .send({ error: err instanceof Error ? err.message : \"Invalid token.\" });\n }\n\n if (payload.key !== key) {\n return reply.code(400).send({ error: \"Token key mismatch.\" });\n }\n\n if (payload.uploadMaxFileSize > 0 && fileSize > payload.uploadMaxFileSize) {\n return reply.code(400).send({ error: \"File exceeds maximum allowed size.\" });\n }\n\n if (payload.uploadMinFileSize > 0 && fileSize < payload.uploadMinFileSize) {\n return reply.code(400).send({ error: \"File is below minimum allowed size.\" });\n }\n\n const destPath = path.join(storagePath, key);\n\n if (!isPathContained(destPath, storagePath)) {\n return reply.code(400).send({ error: \"Invalid path.\" });\n }\n\n const destDir = path.dirname(destPath);\n await mkdir(destDir, { recursive: true });\n\n const writeStream = createWriteStream(destPath);\n const readable = Readable.from(fileChunks);\n\n await pipeline(readable, writeStream);\n\n return reply.code(204).send();\n });\n\n /* PUT /webiny-file-upload/parts — multipart chunk upload via raw binary body.\n Query params: uploadId, partNumber, token.\n Body: raw binary chunk (application/octet-stream). */\n onPut(\"/webiny-file-upload/parts\", async (request, reply) => {\n const storagePath = getStoragePath();\n const secret = getUploadSecret();\n\n const query = request.query as Record<string, string | undefined>;\n const uploadId = query[\"uploadId\"];\n const partNumberStr = query[\"partNumber\"];\n const token = query[\"token\"];\n\n if (!uploadId || !partNumberStr || !token) {\n return reply.code(400).send({ error: \"Missing uploadId, partNumber, or token.\" });\n }\n\n const partNumber = parseInt(partNumberStr, 10);\n if (isNaN(partNumber) || partNumber < 1) {\n return reply.code(400).send({ error: \"Invalid partNumber.\" });\n }\n\n let payload;\n try {\n payload = verifyUploadToken(token, secret);\n } catch (err) {\n return reply\n .code(400)\n .send({ error: err instanceof Error ? err.message : \"Invalid token.\" });\n }\n\n const expectedKey = `tenants/${payload.tenantId}/multipart/${uploadId}/part-${partNumber}`;\n\n if (payload.key !== expectedKey) {\n return reply.code(400).send({ error: \"Token key mismatch.\" });\n }\n\n const destPath = path.join(storagePath, expectedKey);\n\n if (!isPathContained(destPath, storagePath)) {\n return reply.code(400).send({ error: \"Invalid path.\" });\n }\n\n const destDir = path.dirname(destPath);\n await mkdir(destDir, { recursive: true });\n\n const body = request.body as Buffer;\n\n await writeFile(destPath, body);\n\n const etag = createHash(\"md5\").update(body).digest(\"hex\");\n\n return reply.code(200).header(\"ETag\", etag).send();\n });\n});\n\nuploadRoutesPlugin.name = \"fileManagerServer.uploadRoutes\";\n"],"names":["getStoragePath","String","process","getUploadSecret","isPathContained","filePath","storagePath","resolved","path","root","modifyFastifyPlugin","ModifyFastifyPlugin","app","_request","body","done","uploadRoutesPlugin","RoutePlugin","onPost","onPut","request","reply","secret","key","token","fileChunks","fileSize","parts","part","chunks","chunk","payload","verifyUploadToken","err","Error","destPath","destDir","mkdir","writeStream","createWriteStream","readable","Readable","pipeline","query","uploadId","partNumberStr","partNumber","parseInt","isNaN","expectedKey","writeFile","etag","createHash"],"mappings":"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"}
package/types.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import "@webiny/background-tasks/api/features/TaskController/augmentation.js";
2
+ export type { PresignedPostPayloadData } from "@webiny/api-file-manager/features/upload/index.js";
3
+ export type { FileData } from "@webiny/api-file-manager/features/upload/index.js";
4
+ export interface UploadTokenPayload {
5
+ key: string;
6
+ tenantId: string;
7
+ expiresAt: number;
8
+ uploadMinFileSize: number;
9
+ uploadMaxFileSize: number;
10
+ }
package/types.js ADDED
@@ -0,0 +1 @@
1
+ import "@webiny/background-tasks/api/features/TaskController/augmentation.js";
@@ -0,0 +1,2 @@
1
+ import type { FastifyRequest } from "fastify";
2
+ export declare const resolveServerUrl: (request: FastifyRequest) => Promise<string>;
@@ -0,0 +1,10 @@
1
+ import { ServiceDiscovery } from "@webiny/api-core/features/serviceDiscovery/ServiceDiscovery.js";
2
+ const resolveServerUrl = async (request)=>{
3
+ const manifest = await ServiceDiscovery.load();
4
+ const domain = manifest?.api?.cloudfront?.domain;
5
+ if (domain) return domain;
6
+ return `${request.protocol}://${request.hostname}`;
7
+ };
8
+ export { resolveServerUrl };
9
+
10
+ //# sourceMappingURL=resolveServerUrl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils/resolveServerUrl.js","sources":["../../src/utils/resolveServerUrl.ts"],"sourcesContent":["import { ServiceDiscovery } from \"@webiny/api-core/features/serviceDiscovery/ServiceDiscovery.js\";\nimport type { FastifyRequest } from \"fastify\";\n\nexport const resolveServerUrl = async (request: FastifyRequest): Promise<string> => {\n const manifest = await ServiceDiscovery.load();\n const domain = manifest?.api?.cloudfront?.domain;\n if (domain) {\n return domain as string;\n }\n\n return `${request.protocol}://${request.hostname}`;\n};\n"],"names":["resolveServerUrl","request","manifest","ServiceDiscovery","domain"],"mappings":";AAGO,MAAMA,mBAAmB,OAAOC;IACnC,MAAMC,WAAW,MAAMC,iBAAiB,IAAI;IAC5C,MAAMC,SAASF,UAAU,KAAK,YAAY;IAC1C,IAAIE,QACA,OAAOA;IAGX,OAAO,GAAGH,QAAQ,QAAQ,CAAC,GAAG,EAAEA,QAAQ,QAAQ,EAAE;AACtD"}
@@ -0,0 +1,3 @@
1
+ import type { UploadTokenPayload } from "../types.js";
2
+ export declare const createUploadToken: (payload: UploadTokenPayload, secret: string) => string;
3
+ export declare const verifyUploadToken: (token: string, secret: string) => UploadTokenPayload;