@webiny/api-file-manager-server 0.0.0-unstable.b6d7105cee → 6.6.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/FileManagerServerFeature.d.ts +5 -0
  2. package/FileManagerServerFeature.js +41 -0
  3. package/FileManagerServerFeature.js.map +1 -0
  4. package/assetDelivery/LocalAssetResolver.d.ts +6 -4
  5. package/assetDelivery/LocalAssetResolver.js +9 -9
  6. package/assetDelivery/LocalAssetResolver.js.map +1 -1
  7. package/assetDelivery/LocalOutputStrategy.d.ts +3 -2
  8. package/assetDelivery/LocalOutputStrategy.js +4 -4
  9. package/assetDelivery/LocalOutputStrategy.js.map +1 -1
  10. package/assetDelivery/LocalSharpTransform.d.ts +8 -14
  11. package/assetDelivery/LocalSharpTransform.js +72 -98
  12. package/assetDelivery/LocalSharpTransform.js.map +1 -1
  13. package/assetDelivery/abstractions.d.ts +0 -3
  14. package/assetDelivery/abstractions.js +1 -2
  15. package/assetDelivery/abstractions.js.map +1 -1
  16. package/assetDelivery/assetDeliveryConfig.d.ts +1 -1
  17. package/assetDelivery/feature.js +7 -9
  18. package/assetDelivery/feature.js.map +1 -1
  19. package/assetDelivery/types.d.ts +0 -3
  20. package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.d.ts +3 -0
  21. package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js +18 -14
  22. package/features/CleanupStaleMultipartUploads/CleanupStaleMultipartUploadsTask.js.map +1 -1
  23. package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.d.ts +7 -5
  24. package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js +8 -5
  25. package/features/CompleteMultiPartUpload/CompleteMultiPartUploadUseCase.js.map +1 -1
  26. package/features/CompleteMultiPartUpload/feature.js +2 -2
  27. package/features/CompleteMultiPartUpload/feature.js.map +1 -1
  28. package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.d.ts +7 -7
  29. package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.js +9 -10
  30. package/features/CreateMultiPartUpload/CreateMultiPartUploadUseCase.js.map +1 -1
  31. package/features/CreateMultiPartUpload/feature.js +2 -2
  32. package/features/CreateMultiPartUpload/feature.js.map +1 -1
  33. package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.d.ts +3 -1
  34. package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.js +6 -3
  35. package/features/DeleteFileFromDisk/DeleteFileFromDiskHandler.js.map +1 -1
  36. package/features/ExtractMetadata/ExtractMetadataTask.d.ts +3 -1
  37. package/features/ExtractMetadata/ExtractMetadataTask.js +6 -3
  38. package/features/ExtractMetadata/ExtractMetadataTask.js.map +1 -1
  39. package/features/FileManagerServerConfig/FileManagerServerConfig.d.ts +12 -0
  40. package/features/FileManagerServerConfig/FileManagerServerConfig.js +28 -0
  41. package/features/FileManagerServerConfig/FileManagerServerConfig.js.map +1 -0
  42. package/features/FileManagerServerConfig/abstractions.d.ts +10 -0
  43. package/features/FileManagerServerConfig/abstractions.js +5 -0
  44. package/features/FileManagerServerConfig/abstractions.js.map +1 -0
  45. package/features/FileManagerServerConfig/feature.d.ts +4 -0
  46. package/features/FileManagerServerConfig/feature.js +11 -0
  47. package/features/FileManagerServerConfig/feature.js.map +1 -0
  48. package/features/GetFileContentsById/GetFileContentsByIdUseCase.d.ts +7 -5
  49. package/features/GetFileContentsById/GetFileContentsByIdUseCase.js +8 -5
  50. package/features/GetFileContentsById/GetFileContentsByIdUseCase.js.map +1 -1
  51. package/features/GetFileContentsById/feature.js +2 -2
  52. package/features/GetFileContentsById/feature.js.map +1 -1
  53. package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.d.ts +7 -5
  54. package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.js +8 -5
  55. package/features/GetFileContentsByKey/GetFileContentsByKeyUseCase.js.map +1 -1
  56. package/features/GetFileContentsByKey/feature.js +2 -2
  57. package/features/GetFileContentsByKey/feature.js.map +1 -1
  58. package/features/GetUploadPayload/GetUploadPayloadUseCase.d.ts +6 -6
  59. package/features/GetUploadPayload/GetUploadPayloadUseCase.js +8 -9
  60. package/features/GetUploadPayload/GetUploadPayloadUseCase.js.map +1 -1
  61. package/features/GetUploadPayload/feature.js +2 -2
  62. package/features/GetUploadPayload/feature.js.map +1 -1
  63. package/graphql/ServerGraphQLSchema.d.ts +15 -0
  64. package/graphql/ServerGraphQLSchema.js +221 -0
  65. package/graphql/ServerGraphQLSchema.js.map +1 -0
  66. package/index.d.ts +1 -2
  67. package/index.js +1 -40
  68. package/package.json +13 -13
  69. package/routes/UploadPartRoute/UploadPartRoute.d.ts +14 -0
  70. package/routes/UploadPartRoute/UploadPartRoute.js +66 -0
  71. package/routes/UploadPartRoute/UploadPartRoute.js.map +1 -0
  72. package/routes/UploadPartRoute/feature.d.ts +4 -0
  73. package/routes/UploadPartRoute/feature.js +11 -0
  74. package/routes/UploadPartRoute/feature.js.map +1 -0
  75. package/routes/UploadSingleFileRoute/UploadSingleFileRoute.d.ts +14 -0
  76. package/routes/UploadSingleFileRoute/UploadSingleFileRoute.js +70 -0
  77. package/routes/UploadSingleFileRoute/UploadSingleFileRoute.js.map +1 -0
  78. package/routes/UploadSingleFileRoute/feature.d.ts +4 -0
  79. package/routes/UploadSingleFileRoute/feature.js +11 -0
  80. package/routes/UploadSingleFileRoute/feature.js.map +1 -0
  81. package/routes/utils.d.ts +20 -0
  82. package/routes/utils.js +62 -0
  83. package/routes/utils.js.map +1 -0
  84. package/index.js.map +0 -1
  85. package/routes/uploadRoutes.d.ts +0 -4
  86. package/routes/uploadRoutes.js +0 -130
  87. package/routes/uploadRoutes.js.map +0 -1
  88. package/utils/resolveServerUrl.d.ts +0 -2
  89. package/utils/resolveServerUrl.js +0 -10
  90. package/utils/resolveServerUrl.js.map +0 -1
package/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { ContextPlugin } from "@webiny/api";
2
1
  export { createFileUploadModifier } from "@webiny/api-file-manager/features/upload/index.js";
3
2
  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>)[];
3
+ export { FileManagerServerFeature } from "./FileManagerServerFeature.js";
package/index.js CHANGED
@@ -1,42 +1,3 @@
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
1
  export { createFileUploadModifier } from "@webiny/api-file-manager/features/upload/index.js";
39
2
  export { createAssetDelivery } from "./assetDelivery/createAssetDelivery.js";
40
- export { createFileManagerServer };
41
-
42
- //# sourceMappingURL=index.js.map
3
+ export { FileManagerServerFeature } from "./FileManagerServerFeature.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/api-file-manager-server",
3
- "version": "0.0.0-unstable.b6d7105cee",
3
+ "version": "6.6.0-alpha.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -14,22 +14,22 @@
14
14
  "author": "Webiny Ltd",
15
15
  "license": "MIT",
16
16
  "dependencies": {
17
- "@fastify/multipart": "10.1.0",
18
- "@webiny/api": "0.0.0-unstable.b6d7105cee",
19
- "@webiny/api-core": "0.0.0-unstable.b6d7105cee",
20
- "@webiny/api-file-manager": "0.0.0-unstable.b6d7105cee",
21
- "@webiny/background-tasks": "0.0.0-unstable.b6d7105cee",
22
- "@webiny/feature": "0.0.0-unstable.b6d7105cee",
23
- "@webiny/handler": "0.0.0-unstable.b6d7105cee",
24
- "@webiny/plugins": "0.0.0-unstable.b6d7105cee",
25
- "@webiny/utils": "0.0.0-unstable.b6d7105cee",
26
- "@webiny/validation": "0.0.0-unstable.b6d7105cee",
17
+ "@webiny/api-core": "6.6.0-alpha.0",
18
+ "@webiny/api-file-manager": "6.6.0-alpha.0",
19
+ "@webiny/api-graphql": "6.6.0-alpha.0",
20
+ "@webiny/background-tasks": "6.6.0-alpha.0",
21
+ "@webiny/event-handler-core": "6.6.0-alpha.0",
22
+ "@webiny/feature": "6.6.0-alpha.0",
23
+ "@webiny/handler": "6.6.0-alpha.0",
24
+ "@webiny/plugins": "6.6.0-alpha.0",
25
+ "@webiny/utils": "6.6.0-alpha.0",
26
+ "@webiny/validation": "6.6.0-alpha.0",
27
27
  "exifreader": "4.41.3",
28
- "fastify": "5.10.0",
28
+ "p-map": "7.0.6",
29
29
  "sharp": "0.35.3"
30
30
  },
31
31
  "devDependencies": {
32
- "@webiny/build-tools": "0.0.0-unstable.b6d7105cee",
32
+ "@webiny/build-tools": "6.6.0-alpha.0",
33
33
  "rimraf": "6.1.3",
34
34
  "typescript": "7.0.2",
35
35
  "vitest": "4.1.10"
@@ -0,0 +1,14 @@
1
+ import { HttpRoute } from "@webiny/event-handler-core";
2
+ import type { IHttpRequest, IHttpResponse } from "@webiny/event-handler-core";
3
+ import { FileManagerServerConfig } from "../../features/FileManagerServerConfig/abstractions.js";
4
+ declare class UploadPartRouteImpl implements HttpRoute.Interface {
5
+ private readonly config;
6
+ readonly method = "PUT";
7
+ readonly path = "/webiny-file-upload/parts";
8
+ constructor(config: FileManagerServerConfig.Interface);
9
+ handle(request: IHttpRequest): Promise<IHttpResponse>;
10
+ }
11
+ export declare const UploadPartRoute: typeof UploadPartRouteImpl & {
12
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-core").IHttpRoute>;
13
+ };
14
+ export {};
@@ -0,0 +1,66 @@
1
+ import node_path from "node:path";
2
+ import { createHash } from "node:crypto";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { HttpRoute } from "@webiny/event-handler-core";
5
+ import { verifyUploadToken } from "../../utils/uploadToken.js";
6
+ import { FileManagerServerConfig } from "../../features/FileManagerServerConfig/abstractions.js";
7
+ import { isPathContained, json, toBuffer } from "../utils.js";
8
+ class UploadPartRouteImpl {
9
+ constructor(config){
10
+ this.config = config;
11
+ this.method = "PUT";
12
+ this.path = "/webiny-file-upload/parts";
13
+ }
14
+ async handle(request) {
15
+ const storagePath = this.config.storagePath;
16
+ const secret = this.config.uploadSecret;
17
+ const query = request.query ?? {};
18
+ const uploadId = query["uploadId"];
19
+ const partNumberStr = query["partNumber"];
20
+ const token = query["token"];
21
+ if (!uploadId || !partNumberStr || !token) return json(400, {
22
+ error: "Missing uploadId, partNumber, or token."
23
+ });
24
+ const partNumber = parseInt(partNumberStr, 10);
25
+ if (isNaN(partNumber) || partNumber < 1) return json(400, {
26
+ error: "Invalid partNumber."
27
+ });
28
+ let payload;
29
+ try {
30
+ payload = verifyUploadToken(token, secret);
31
+ } catch (err) {
32
+ return json(400, {
33
+ error: err instanceof Error ? err.message : "Invalid token."
34
+ });
35
+ }
36
+ const expectedKey = `tenants/${payload.tenantId}/multipart/${uploadId}/part-${partNumber}`;
37
+ if (payload.key !== expectedKey) return json(400, {
38
+ error: "Token key mismatch."
39
+ });
40
+ const destPath = node_path.join(storagePath, expectedKey);
41
+ if (!isPathContained(destPath, storagePath)) return json(400, {
42
+ error: "Invalid path."
43
+ });
44
+ await mkdir(node_path.dirname(destPath), {
45
+ recursive: true
46
+ });
47
+ const body = toBuffer(request.body);
48
+ await writeFile(destPath, body);
49
+ const etag = createHash("md5").update(body).digest("hex");
50
+ return {
51
+ statusCode: 200,
52
+ headers: {
53
+ ETag: etag
54
+ }
55
+ };
56
+ }
57
+ }
58
+ const UploadPartRoute = HttpRoute.createImplementation({
59
+ implementation: UploadPartRouteImpl,
60
+ dependencies: [
61
+ FileManagerServerConfig
62
+ ]
63
+ });
64
+ export { UploadPartRoute };
65
+
66
+ //# sourceMappingURL=UploadPartRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes/UploadPartRoute/UploadPartRoute.js","sources":["../../../src/routes/UploadPartRoute/UploadPartRoute.ts"],"sourcesContent":["import path from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { HttpRoute } from \"@webiny/event-handler-core\";\nimport type { IHttpRequest, IHttpResponse } from \"@webiny/event-handler-core\";\nimport { verifyUploadToken } from \"~/utils/uploadToken.js\";\nimport { FileManagerServerConfig } from \"~/features/FileManagerServerConfig/abstractions.js\";\nimport { isPathContained, json, toBuffer } from \"../utils.js\";\n\nclass UploadPartRouteImpl implements HttpRoute.Interface {\n public readonly method = \"PUT\";\n public readonly path = \"/webiny-file-upload/parts\";\n\n public constructor(private readonly config: FileManagerServerConfig.Interface) {}\n\n public async handle(request: IHttpRequest): Promise<IHttpResponse> {\n const storagePath = this.config.storagePath;\n const secret = this.config.uploadSecret;\n\n const query = request.query ?? {};\n const uploadId = query[\"uploadId\"];\n const partNumberStr = query[\"partNumber\"];\n const token = query[\"token\"];\n\n if (!uploadId || !partNumberStr || !token) {\n return json(400, { error: \"Missing uploadId, partNumber, or token.\" });\n }\n\n const partNumber = parseInt(partNumberStr, 10);\n if (isNaN(partNumber) || partNumber < 1) {\n return json(400, { error: \"Invalid partNumber.\" });\n }\n\n let payload;\n try {\n payload = verifyUploadToken(token, secret);\n } catch (err) {\n return json(400, { error: err instanceof Error ? err.message : \"Invalid token.\" });\n }\n\n const expectedKey = `tenants/${payload.tenantId}/multipart/${uploadId}/part-${partNumber}`;\n if (payload.key !== expectedKey) {\n return json(400, { error: \"Token key mismatch.\" });\n }\n\n const destPath = path.join(storagePath, expectedKey);\n if (!isPathContained(destPath, storagePath)) {\n return json(400, { error: \"Invalid path.\" });\n }\n\n await mkdir(path.dirname(destPath), { recursive: true });\n\n const body = toBuffer(request.body);\n await writeFile(destPath, body);\n\n const etag = createHash(\"md5\").update(body).digest(\"hex\");\n\n return { statusCode: 200, headers: { ETag: etag } };\n }\n}\n\nexport const UploadPartRoute = HttpRoute.createImplementation({\n implementation: UploadPartRouteImpl,\n dependencies: [FileManagerServerConfig]\n});\n"],"names":["UploadPartRouteImpl","config","request","storagePath","secret","query","uploadId","partNumberStr","token","json","partNumber","parseInt","isNaN","payload","verifyUploadToken","err","Error","expectedKey","destPath","path","isPathContained","mkdir","body","toBuffer","writeFile","etag","createHash","UploadPartRoute","HttpRoute","FileManagerServerConfig"],"mappings":";;;;;;;AASA,MAAMA;IAIF,YAAoCC,MAAyC,CAAE;aAA3CA,MAAM,GAANA;aAHpB,MAAM,GAAG;aACT,IAAI,GAAG;IAEyD;IAEhF,MAAa,OAAOC,OAAqB,EAA0B;QAC/D,MAAMC,cAAc,IAAI,CAAC,MAAM,CAAC,WAAW;QAC3C,MAAMC,SAAS,IAAI,CAAC,MAAM,CAAC,YAAY;QAEvC,MAAMC,QAAQH,QAAQ,KAAK,IAAI,CAAC;QAChC,MAAMI,WAAWD,KAAK,CAAC,WAAW;QAClC,MAAME,gBAAgBF,KAAK,CAAC,aAAa;QACzC,MAAMG,QAAQH,KAAK,CAAC,QAAQ;QAE5B,IAAI,CAACC,YAAY,CAACC,iBAAiB,CAACC,OAChC,OAAOC,KAAK,KAAK;YAAE,OAAO;QAA0C;QAGxE,MAAMC,aAAaC,SAASJ,eAAe;QAC3C,IAAIK,MAAMF,eAAeA,aAAa,GAClC,OAAOD,KAAK,KAAK;YAAE,OAAO;QAAsB;QAGpD,IAAII;QACJ,IAAI;YACAA,UAAUC,kBAAkBN,OAAOJ;QACvC,EAAE,OAAOW,KAAK;YACV,OAAON,KAAK,KAAK;gBAAE,OAAOM,eAAeC,QAAQD,IAAI,OAAO,GAAG;YAAiB;QACpF;QAEA,MAAME,cAAc,CAAC,QAAQ,EAAEJ,QAAQ,QAAQ,CAAC,WAAW,EAAEP,SAAS,MAAM,EAAEI,YAAY;QAC1F,IAAIG,QAAQ,GAAG,KAAKI,aAChB,OAAOR,KAAK,KAAK;YAAE,OAAO;QAAsB;QAGpD,MAAMS,WAAWC,UAAAA,IAAS,CAAChB,aAAac;QACxC,IAAI,CAACG,gBAAgBF,UAAUf,cAC3B,OAAOM,KAAK,KAAK;YAAE,OAAO;QAAgB;QAG9C,MAAMY,MAAMF,UAAAA,OAAY,CAACD,WAAW;YAAE,WAAW;QAAK;QAEtD,MAAMI,OAAOC,SAASrB,QAAQ,IAAI;QAClC,MAAMsB,UAAUN,UAAUI;QAE1B,MAAMG,OAAOC,WAAW,OAAO,MAAM,CAACJ,MAAM,MAAM,CAAC;QAEnD,OAAO;YAAE,YAAY;YAAK,SAAS;gBAAE,MAAMG;YAAK;QAAE;IACtD;AACJ;AAEO,MAAME,kBAAkBC,UAAU,oBAAoB,CAAC;IAC1D,gBAAgB5B;IAChB,cAAc;QAAC6B;KAAwB;AAC3C"}
@@ -0,0 +1,4 @@
1
+ export declare const UploadPartRouteFeature: {
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 { UploadPartRoute } from "./UploadPartRoute.js";
3
+ const UploadPartRouteFeature = createFeature({
4
+ name: "FileManagerServer/UploadPartRoute",
5
+ register (container) {
6
+ container.register(UploadPartRoute);
7
+ }
8
+ });
9
+ export { UploadPartRouteFeature };
10
+
11
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes/UploadPartRoute/feature.js","sources":["../../../src/routes/UploadPartRoute/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { UploadPartRoute } from \"./UploadPartRoute.js\";\n\nexport const UploadPartRouteFeature = createFeature({\n name: \"FileManagerServer/UploadPartRoute\",\n register(container) {\n container.register(UploadPartRoute);\n }\n});\n"],"names":["UploadPartRouteFeature","createFeature","container","UploadPartRoute"],"mappings":";;AAGO,MAAMA,yBAAyBC,cAAc;IAChD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
@@ -0,0 +1,14 @@
1
+ import { HttpRoute } from "@webiny/event-handler-core";
2
+ import type { IHttpRequest, IHttpResponse } from "@webiny/event-handler-core";
3
+ import { FileManagerServerConfig } from "../../features/FileManagerServerConfig/abstractions.js";
4
+ declare class UploadSingleFileRouteImpl implements HttpRoute.Interface {
5
+ private readonly config;
6
+ readonly method = "POST";
7
+ readonly path = "/webiny-file-upload";
8
+ constructor(config: FileManagerServerConfig.Interface);
9
+ handle(request: IHttpRequest): Promise<IHttpResponse>;
10
+ }
11
+ export declare const UploadSingleFileRoute: typeof UploadSingleFileRouteImpl & {
12
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-core").IHttpRoute>;
13
+ };
14
+ export {};
@@ -0,0 +1,70 @@
1
+ import node_path from "node:path";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { HttpRoute } from "@webiny/event-handler-core";
4
+ import { verifyUploadToken } from "../../utils/uploadToken.js";
5
+ import { FileManagerServerConfig } from "../../features/FileManagerServerConfig/abstractions.js";
6
+ import { getBoundary, isPathContained, json, parseMultipart, toBuffer } from "../utils.js";
7
+ class UploadSingleFileRouteImpl {
8
+ constructor(config){
9
+ this.config = config;
10
+ this.method = "POST";
11
+ this.path = "/webiny-file-upload";
12
+ }
13
+ async handle(request) {
14
+ const storagePath = this.config.storagePath;
15
+ const secret = this.config.uploadSecret;
16
+ const contentType = request.headers["content-type"] ?? request.headers["Content-Type"] ?? "";
17
+ const boundary = getBoundary(contentType);
18
+ if (!boundary) return json(400, {
19
+ error: "Expected multipart/form-data with a boundary."
20
+ });
21
+ const { fields, files } = parseMultipart(toBuffer(request.body), boundary);
22
+ const key = fields.find((field)=>"key" === field.name)?.value;
23
+ const token = fields.find((field)=>"token" === field.name)?.value;
24
+ const file = files[0];
25
+ if (!key || !token) return json(400, {
26
+ error: "Missing key or token."
27
+ });
28
+ if (!file) return json(400, {
29
+ error: "No file in request."
30
+ });
31
+ let payload;
32
+ try {
33
+ payload = verifyUploadToken(token, secret);
34
+ } catch (err) {
35
+ return json(400, {
36
+ error: err instanceof Error ? err.message : "Invalid token."
37
+ });
38
+ }
39
+ if (payload.key !== key) return json(400, {
40
+ error: "Token key mismatch."
41
+ });
42
+ const fileSize = file.data.length;
43
+ if (payload.uploadMaxFileSize > 0 && fileSize > payload.uploadMaxFileSize) return json(400, {
44
+ error: "File exceeds maximum allowed size."
45
+ });
46
+ if (payload.uploadMinFileSize > 0 && fileSize < payload.uploadMinFileSize) return json(400, {
47
+ error: "File is below minimum allowed size."
48
+ });
49
+ const destPath = node_path.join(storagePath, key);
50
+ if (!isPathContained(destPath, storagePath)) return json(400, {
51
+ error: "Invalid path."
52
+ });
53
+ await mkdir(node_path.dirname(destPath), {
54
+ recursive: true
55
+ });
56
+ await writeFile(destPath, file.data);
57
+ return {
58
+ statusCode: 204
59
+ };
60
+ }
61
+ }
62
+ const UploadSingleFileRoute = HttpRoute.createImplementation({
63
+ implementation: UploadSingleFileRouteImpl,
64
+ dependencies: [
65
+ FileManagerServerConfig
66
+ ]
67
+ });
68
+ export { UploadSingleFileRoute };
69
+
70
+ //# sourceMappingURL=UploadSingleFileRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes/UploadSingleFileRoute/UploadSingleFileRoute.js","sources":["../../../src/routes/UploadSingleFileRoute/UploadSingleFileRoute.ts"],"sourcesContent":["import path from \"node:path\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { HttpRoute } from \"@webiny/event-handler-core\";\nimport type { IHttpRequest, IHttpResponse } from \"@webiny/event-handler-core\";\nimport { verifyUploadToken } from \"~/utils/uploadToken.js\";\nimport { FileManagerServerConfig } from \"~/features/FileManagerServerConfig/abstractions.js\";\nimport { isPathContained, json, toBuffer, parseMultipart, getBoundary } from \"../utils.js\";\n\nclass UploadSingleFileRouteImpl implements HttpRoute.Interface {\n public readonly method = \"POST\";\n public readonly path = \"/webiny-file-upload\";\n\n public constructor(private readonly config: FileManagerServerConfig.Interface) {}\n\n public async handle(request: IHttpRequest): Promise<IHttpResponse> {\n const storagePath = this.config.storagePath;\n const secret = this.config.uploadSecret;\n\n const contentType =\n request.headers[\"content-type\"] ?? request.headers[\"Content-Type\"] ?? \"\";\n const boundary = getBoundary(contentType);\n if (!boundary) {\n return json(400, { error: \"Expected multipart/form-data with a boundary.\" });\n }\n\n const { fields, files } = parseMultipart(toBuffer(request.body), boundary);\n const key = fields.find(field => field.name === \"key\")?.value;\n const token = fields.find(field => field.name === \"token\")?.value;\n const file = files[0];\n\n if (!key || !token) {\n return json(400, { error: \"Missing key or token.\" });\n }\n\n if (!file) {\n return json(400, { error: \"No file in request.\" });\n }\n\n let payload;\n try {\n payload = verifyUploadToken(token, secret);\n } catch (err) {\n return json(400, { error: err instanceof Error ? err.message : \"Invalid token.\" });\n }\n\n if (payload.key !== key) {\n return json(400, { error: \"Token key mismatch.\" });\n }\n\n const fileSize = file.data.length;\n if (payload.uploadMaxFileSize > 0 && fileSize > payload.uploadMaxFileSize) {\n return json(400, { error: \"File exceeds maximum allowed size.\" });\n }\n\n if (payload.uploadMinFileSize > 0 && fileSize < payload.uploadMinFileSize) {\n return json(400, { error: \"File is below minimum allowed size.\" });\n }\n\n const destPath = path.join(storagePath, key);\n if (!isPathContained(destPath, storagePath)) {\n return json(400, { error: \"Invalid path.\" });\n }\n\n await mkdir(path.dirname(destPath), { recursive: true });\n await writeFile(destPath, file.data);\n\n return { statusCode: 204 };\n }\n}\n\nexport const UploadSingleFileRoute = HttpRoute.createImplementation({\n implementation: UploadSingleFileRouteImpl,\n dependencies: [FileManagerServerConfig]\n});\n"],"names":["UploadSingleFileRouteImpl","config","request","storagePath","secret","contentType","boundary","getBoundary","json","fields","files","parseMultipart","toBuffer","key","field","token","file","payload","verifyUploadToken","err","Error","fileSize","destPath","path","isPathContained","mkdir","writeFile","UploadSingleFileRoute","HttpRoute","FileManagerServerConfig"],"mappings":";;;;;;AAQA,MAAMA;IAIF,YAAoCC,MAAyC,CAAE;aAA3CA,MAAM,GAANA;aAHpB,MAAM,GAAG;aACT,IAAI,GAAG;IAEyD;IAEhF,MAAa,OAAOC,OAAqB,EAA0B;QAC/D,MAAMC,cAAc,IAAI,CAAC,MAAM,CAAC,WAAW;QAC3C,MAAMC,SAAS,IAAI,CAAC,MAAM,CAAC,YAAY;QAEvC,MAAMC,cACFH,QAAQ,OAAO,CAAC,eAAe,IAAIA,QAAQ,OAAO,CAAC,eAAe,IAAI;QAC1E,MAAMI,WAAWC,YAAYF;QAC7B,IAAI,CAACC,UACD,OAAOE,KAAK,KAAK;YAAE,OAAO;QAAgD;QAG9E,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAE,GAAGC,eAAeC,SAASV,QAAQ,IAAI,GAAGI;QACjE,MAAMO,MAAMJ,OAAO,IAAI,CAACK,CAAAA,QAASA,AAAe,UAAfA,MAAM,IAAI,GAAa;QACxD,MAAMC,QAAQN,OAAO,IAAI,CAACK,CAAAA,QAASA,AAAe,YAAfA,MAAM,IAAI,GAAe;QAC5D,MAAME,OAAON,KAAK,CAAC,EAAE;QAErB,IAAI,CAACG,OAAO,CAACE,OACT,OAAOP,KAAK,KAAK;YAAE,OAAO;QAAwB;QAGtD,IAAI,CAACQ,MACD,OAAOR,KAAK,KAAK;YAAE,OAAO;QAAsB;QAGpD,IAAIS;QACJ,IAAI;YACAA,UAAUC,kBAAkBH,OAAOX;QACvC,EAAE,OAAOe,KAAK;YACV,OAAOX,KAAK,KAAK;gBAAE,OAAOW,eAAeC,QAAQD,IAAI,OAAO,GAAG;YAAiB;QACpF;QAEA,IAAIF,QAAQ,GAAG,KAAKJ,KAChB,OAAOL,KAAK,KAAK;YAAE,OAAO;QAAsB;QAGpD,MAAMa,WAAWL,KAAK,IAAI,CAAC,MAAM;QACjC,IAAIC,QAAQ,iBAAiB,GAAG,KAAKI,WAAWJ,QAAQ,iBAAiB,EACrE,OAAOT,KAAK,KAAK;YAAE,OAAO;QAAqC;QAGnE,IAAIS,QAAQ,iBAAiB,GAAG,KAAKI,WAAWJ,QAAQ,iBAAiB,EACrE,OAAOT,KAAK,KAAK;YAAE,OAAO;QAAsC;QAGpE,MAAMc,WAAWC,UAAAA,IAAS,CAACpB,aAAaU;QACxC,IAAI,CAACW,gBAAgBF,UAAUnB,cAC3B,OAAOK,KAAK,KAAK;YAAE,OAAO;QAAgB;QAG9C,MAAMiB,MAAMF,UAAAA,OAAY,CAACD,WAAW;YAAE,WAAW;QAAK;QACtD,MAAMI,UAAUJ,UAAUN,KAAK,IAAI;QAEnC,OAAO;YAAE,YAAY;QAAI;IAC7B;AACJ;AAEO,MAAMW,wBAAwBC,UAAU,oBAAoB,CAAC;IAChE,gBAAgB5B;IAChB,cAAc;QAAC6B;KAAwB;AAC3C"}
@@ -0,0 +1,4 @@
1
+ export declare const UploadSingleFileRouteFeature: {
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 { UploadSingleFileRoute } from "./UploadSingleFileRoute.js";
3
+ const UploadSingleFileRouteFeature = createFeature({
4
+ name: "FileManagerServer/UploadSingleFileRoute",
5
+ register (container) {
6
+ container.register(UploadSingleFileRoute);
7
+ }
8
+ });
9
+ export { UploadSingleFileRouteFeature };
10
+
11
+ //# sourceMappingURL=feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes/UploadSingleFileRoute/feature.js","sources":["../../../src/routes/UploadSingleFileRoute/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { UploadSingleFileRoute } from \"./UploadSingleFileRoute.js\";\n\nexport const UploadSingleFileRouteFeature = createFeature({\n name: \"FileManagerServer/UploadSingleFileRoute\",\n register(container) {\n container.register(UploadSingleFileRoute);\n }\n});\n"],"names":["UploadSingleFileRouteFeature","createFeature","container","UploadSingleFileRoute"],"mappings":";;AAGO,MAAMA,+BAA+BC,cAAc;IACtD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
@@ -0,0 +1,20 @@
1
+ import type { IHttpResponse } from "@webiny/event-handler-core";
2
+ export declare const isPathContained: (filePath: string, storagePath: string) => boolean;
3
+ export declare const json: (statusCode: number, data: unknown) => IHttpResponse;
4
+ export declare const toBuffer: (body: unknown) => Buffer;
5
+ interface MultipartField {
6
+ name: string;
7
+ value: string;
8
+ }
9
+ interface MultipartFile {
10
+ name: string;
11
+ filename: string;
12
+ data: Buffer;
13
+ }
14
+ export interface MultipartResult {
15
+ fields: MultipartField[];
16
+ files: MultipartFile[];
17
+ }
18
+ export declare const parseMultipart: (buffer: Buffer, boundary: string) => MultipartResult;
19
+ export declare const getBoundary: (contentType: string) => string | undefined;
20
+ export {};
@@ -0,0 +1,62 @@
1
+ import node_path from "node:path";
2
+ const isPathContained = (filePath, storagePath)=>{
3
+ const resolved = node_path.resolve(filePath);
4
+ const root = node_path.resolve(storagePath);
5
+ return resolved === root || resolved.startsWith(root + node_path.sep);
6
+ };
7
+ const json = (statusCode, data)=>({
8
+ statusCode,
9
+ headers: {
10
+ "content-type": "application/json"
11
+ },
12
+ body: JSON.stringify(data)
13
+ });
14
+ const toBuffer = (body)=>{
15
+ if (Buffer.isBuffer(body)) return body;
16
+ if (body instanceof Uint8Array) return Buffer.from(body);
17
+ if ("string" == typeof body) return Buffer.from(body, "base64");
18
+ return Buffer.alloc(0);
19
+ };
20
+ const parseMultipart = (buffer, boundary)=>{
21
+ const fields = [];
22
+ const files = [];
23
+ const separator = Buffer.from(`--${boundary}`);
24
+ let idx = buffer.indexOf(separator);
25
+ while(-1 !== idx){
26
+ const next = buffer.indexOf(separator, idx + separator.length);
27
+ if (-1 === next) break;
28
+ let part = buffer.subarray(idx + separator.length, next);
29
+ if ("\r\n" === part.subarray(0, 2).toString("latin1")) part = part.subarray(2);
30
+ if ("\r\n" === part.subarray(-2).toString("latin1")) part = part.subarray(0, -2);
31
+ const headerEnd = part.indexOf("\r\n\r\n");
32
+ if (-1 !== headerEnd) {
33
+ const rawHeaders = part.subarray(0, headerEnd).toString("utf8");
34
+ const content = part.subarray(headerEnd + 4);
35
+ const disposition = /content-disposition:[^\r\n]*/i.exec(rawHeaders)?.[0] ?? "";
36
+ const name = /name="([^"]*)"/i.exec(disposition)?.[1];
37
+ const filename = /filename="([^"]*)"/i.exec(disposition)?.[1];
38
+ if (void 0 !== name) if (void 0 !== filename) files.push({
39
+ name,
40
+ filename,
41
+ data: content
42
+ });
43
+ else fields.push({
44
+ name,
45
+ value: content.toString("utf8")
46
+ });
47
+ }
48
+ idx = next;
49
+ }
50
+ return {
51
+ fields,
52
+ files
53
+ };
54
+ };
55
+ const getBoundary = (contentType)=>{
56
+ const match = /boundary=(?:"([^"]+)"|([^;]+))/i.exec(contentType);
57
+ const value = match?.[1] ?? match?.[2];
58
+ return value?.trim();
59
+ };
60
+ export { getBoundary, isPathContained, json, parseMultipart, toBuffer };
61
+
62
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes/utils.js","sources":["../../src/routes/utils.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { IHttpResponse } from \"@webiny/event-handler-core\";\n\nexport const isPathContained = (filePath: string, storagePath: string): boolean => {\n const resolved = path.resolve(filePath);\n const root = path.resolve(storagePath);\n return resolved === root || resolved.startsWith(root + path.sep);\n};\n\nexport const json = (statusCode: number, data: unknown): IHttpResponse => ({\n statusCode,\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(data)\n});\n\n/* Normalise a transport request body into a Buffer. */\nexport const toBuffer = (body: unknown): Buffer => {\n if (Buffer.isBuffer(body)) {\n return body;\n }\n if (body instanceof Uint8Array) {\n return Buffer.from(body);\n }\n if (typeof body === \"string\") {\n return Buffer.from(body, \"base64\");\n }\n return Buffer.alloc(0);\n};\n\ninterface MultipartField {\n name: string;\n value: string;\n}\n\ninterface MultipartFile {\n name: string;\n filename: string;\n data: Buffer;\n}\n\nexport interface MultipartResult {\n fields: MultipartField[];\n files: MultipartFile[];\n}\n\n/* Minimal multipart/form-data parser operating on the raw body buffer. */\nexport const parseMultipart = (buffer: Buffer, boundary: string): MultipartResult => {\n const fields: MultipartField[] = [];\n const files: MultipartFile[] = [];\n const separator = Buffer.from(`--${boundary}`);\n\n let idx = buffer.indexOf(separator);\n while (idx !== -1) {\n const next = buffer.indexOf(separator, idx + separator.length);\n if (next === -1) {\n break;\n }\n\n let part = buffer.subarray(idx + separator.length, next);\n if (part.subarray(0, 2).toString(\"latin1\") === \"\\r\\n\") {\n part = part.subarray(2);\n }\n if (part.subarray(-2).toString(\"latin1\") === \"\\r\\n\") {\n part = part.subarray(0, -2);\n }\n\n const headerEnd = part.indexOf(\"\\r\\n\\r\\n\");\n if (headerEnd !== -1) {\n const rawHeaders = part.subarray(0, headerEnd).toString(\"utf8\");\n const content = part.subarray(headerEnd + 4);\n\n const disposition = /content-disposition:[^\\r\\n]*/i.exec(rawHeaders)?.[0] ?? \"\";\n const name = /name=\"([^\"]*)\"/i.exec(disposition)?.[1];\n const filename = /filename=\"([^\"]*)\"/i.exec(disposition)?.[1];\n\n if (name !== undefined) {\n if (filename !== undefined) {\n files.push({ name, filename, data: content });\n } else {\n fields.push({ name, value: content.toString(\"utf8\") });\n }\n }\n }\n\n idx = next;\n }\n\n return { fields, files };\n};\n\nexport const getBoundary = (contentType: string): string | undefined => {\n const match = /boundary=(?:\"([^\"]+)\"|([^;]+))/i.exec(contentType);\n const value = match?.[1] ?? match?.[2];\n return value?.trim();\n};\n"],"names":["isPathContained","filePath","storagePath","resolved","path","root","json","statusCode","data","JSON","toBuffer","body","Buffer","Uint8Array","parseMultipart","buffer","boundary","fields","files","separator","idx","next","part","headerEnd","rawHeaders","content","disposition","name","filename","undefined","getBoundary","contentType","match","value"],"mappings":";AAGO,MAAMA,kBAAkB,CAACC,UAAkBC;IAC9C,MAAMC,WAAWC,UAAAA,OAAY,CAACH;IAC9B,MAAMI,OAAOD,UAAAA,OAAY,CAACF;IAC1B,OAAOC,aAAaE,QAAQF,SAAS,UAAU,CAACE,OAAOD,UAAAA,GAAQ;AACnE;AAEO,MAAME,OAAO,CAACC,YAAoBC,OAAkC;QACvED;QACA,SAAS;YAAE,gBAAgB;QAAmB;QAC9C,MAAME,KAAK,SAAS,CAACD;IACzB;AAGO,MAAME,WAAW,CAACC;IACrB,IAAIC,OAAO,QAAQ,CAACD,OAChB,OAAOA;IAEX,IAAIA,gBAAgBE,YAChB,OAAOD,OAAO,IAAI,CAACD;IAEvB,IAAI,AAAgB,YAAhB,OAAOA,MACP,OAAOC,OAAO,IAAI,CAACD,MAAM;IAE7B,OAAOC,OAAO,KAAK,CAAC;AACxB;AAmBO,MAAME,iBAAiB,CAACC,QAAgBC;IAC3C,MAAMC,SAA2B,EAAE;IACnC,MAAMC,QAAyB,EAAE;IACjC,MAAMC,YAAYP,OAAO,IAAI,CAAC,CAAC,EAAE,EAAEI,UAAU;IAE7C,IAAII,MAAML,OAAO,OAAO,CAACI;IACzB,MAAOC,AAAQ,OAARA,IAAY;QACf,MAAMC,OAAON,OAAO,OAAO,CAACI,WAAWC,MAAMD,UAAU,MAAM;QAC7D,IAAIE,AAAS,OAATA,MACA;QAGJ,IAAIC,OAAOP,OAAO,QAAQ,CAACK,MAAMD,UAAU,MAAM,EAAEE;QACnD,IAAIC,AAA2C,WAA3CA,KAAK,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,WAC7BA,OAAOA,KAAK,QAAQ,CAAC;QAEzB,IAAIA,AAAyC,WAAzCA,KAAK,QAAQ,CAAC,IAAI,QAAQ,CAAC,WAC3BA,OAAOA,KAAK,QAAQ,CAAC,GAAG;QAG5B,MAAMC,YAAYD,KAAK,OAAO,CAAC;QAC/B,IAAIC,AAAc,OAAdA,WAAkB;YAClB,MAAMC,aAAaF,KAAK,QAAQ,CAAC,GAAGC,WAAW,QAAQ,CAAC;YACxD,MAAME,UAAUH,KAAK,QAAQ,CAACC,YAAY;YAE1C,MAAMG,cAAc,gCAAgC,IAAI,CAACF,aAAa,CAAC,EAAE,IAAI;YAC7E,MAAMG,OAAO,kBAAkB,IAAI,CAACD,cAAc,CAAC,EAAE;YACrD,MAAME,WAAW,sBAAsB,IAAI,CAACF,cAAc,CAAC,EAAE;YAE7D,IAAIC,AAASE,WAATF,MACA,IAAIC,AAAaC,WAAbD,UACAV,MAAM,IAAI,CAAC;gBAAES;gBAAMC;gBAAU,MAAMH;YAAQ;iBAE3CR,OAAO,IAAI,CAAC;gBAAEU;gBAAM,OAAOF,QAAQ,QAAQ,CAAC;YAAQ;QAGhE;QAEAL,MAAMC;IACV;IAEA,OAAO;QAAEJ;QAAQC;IAAM;AAC3B;AAEO,MAAMY,cAAc,CAACC;IACxB,MAAMC,QAAQ,kCAAkC,IAAI,CAACD;IACrD,MAAME,QAAQD,OAAO,CAAC,EAAE,IAAIA,OAAO,CAAC,EAAE;IACtC,OAAOC,OAAO;AAClB"}
package/index.js.map DELETED
@@ -1 +0,0 @@
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"}
@@ -1,4 +0,0 @@
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>;
@@ -1,130 +0,0 @@
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
@@ -1 +0,0 @@
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":";;;;;;;;;AAWA,MAAMA,iBAAiB,IACZC,OAAOC,QAAQ,GAAG,CAAC,yBAAyB;AAGvD,MAAMC,kBAAkB,IACbF,OAAOC,QAAQ,GAAG,CAAC,oBAAoB;AAGlD,MAAME,kBAAkB,CAACC,UAAkBC;IACvC,MAAMC,WAAWC,UAAAA,OAAY,CAACH;IAC9B,MAAMI,OAAOD,UAAAA,OAAY,CAACF;IAC1B,IAAI,CAACC,SAAS,UAAU,CAACE,OAAOD,UAAAA,GAAQ,KAAKD,aAAaE,MACtD,OAAO;IAEX,OAAO;AACX;AAEO,MAAMC,sBAAsB,IAAIC,oBAAoBC,CAAAA;IACvDA,IAAI,QAAQ,CAAC,MAAM,CAAC,uBAAuB;QACvC,QAAQ;YACJ,UAAU;QACd;IACJ;IAEAA,IAAI,oBAAoB,CACpB,4BACA;QAAE,SAAS;IAAS,GACpB,CAACC,UAAUC,MAAMC;QACbA,KAAK,MAAMD;IACf;AAER;AAEAJ,oBAAoB,IAAI,GAAG;AAEpB,MAAMM,qBAAqB,IAAIC,YAAY,CAAC,EAAEC,MAAM,EAAEC,KAAK,EAAE;IAIhED,OAAO,uBAAuB,OAAOE,SAASC;QAC1C,MAAMf,cAAcN;QACpB,MAAMsB,SAASnB;QAEf,IAAIoB;QACJ,IAAIC;QACJ,IAAIC;QACJ,IAAIC,WAAW;QAEf,MAAMC,QAAQP,QAAQ,KAAK;QAE3B,WAAW,MAAMQ,QAAQD,MACrB,IAAIC,AAAc,YAAdA,KAAK,IAAI,EACT;YAAA,IAAIA,AAAmB,UAAnBA,KAAK,SAAS,EACdL,MAAMK,KAAK,KAAK;iBACb,IAAIA,AAAmB,YAAnBA,KAAK,SAAS,EACrBJ,QAAQI,KAAK,KAAK;QACtB,OACG,IAAIA,AAAc,WAAdA,KAAK,IAAI,EAAa;YAE7B,MAAMC,SAAmB,EAAE;YAC3B,WAAW,MAAMC,SAASF,KAAK,IAAI,CAAE;gBACjCC,OAAO,IAAI,CAACC;gBACZJ,YAAaI,MAAiB,MAAM;YACxC;YACAL,aAAaI;QACjB;QAGJ,IAAI,CAACN,OAAO,CAACC,OACT,OAAOH,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAwB;QAGjE,IAAI,CAACI,YACD,OAAOJ,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAsB;QAG/D,IAAIU;QACJ,IAAI;YACAA,UAAUC,kBAAkBR,OAAOF;QACvC,EAAE,OAAOW,KAAK;YACV,OAAOZ,MACF,IAAI,CAAC,KACL,IAAI,CAAC;gBAAE,OAAOY,eAAeC,QAAQD,IAAI,OAAO,GAAG;YAAiB;QAC7E;QAEA,IAAIF,QAAQ,GAAG,KAAKR,KAChB,OAAOF,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAsB;QAG/D,IAAIU,QAAQ,iBAAiB,GAAG,KAAKL,WAAWK,QAAQ,iBAAiB,EACrE,OAAOV,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAqC;QAG9E,IAAIU,QAAQ,iBAAiB,GAAG,KAAKL,WAAWK,QAAQ,iBAAiB,EACrE,OAAOV,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAsC;QAG/E,MAAMc,WAAW3B,UAAAA,IAAS,CAACF,aAAaiB;QAExC,IAAI,CAACnB,gBAAgB+B,UAAU7B,cAC3B,OAAOe,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAgB;QAGzD,MAAMe,UAAU5B,UAAAA,OAAY,CAAC2B;QAC7B,MAAME,MAAMD,SAAS;YAAE,WAAW;QAAK;QAEvC,MAAME,cAAcC,kBAAkBJ;QACtC,MAAMK,WAAWC,SAAS,IAAI,CAAChB;QAE/B,MAAMiB,SAASF,UAAUF;QAEzB,OAAOjB,MAAM,IAAI,CAAC,KAAK,IAAI;IAC/B;IAKAF,MAAM,6BAA6B,OAAOC,SAASC;QAC/C,MAAMf,cAAcN;QACpB,MAAMsB,SAASnB;QAEf,MAAMwC,QAAQvB,QAAQ,KAAK;QAC3B,MAAMwB,WAAWD,KAAK,CAAC,WAAW;QAClC,MAAME,gBAAgBF,KAAK,CAAC,aAAa;QACzC,MAAMnB,QAAQmB,KAAK,CAAC,QAAQ;QAE5B,IAAI,CAACC,YAAY,CAACC,iBAAiB,CAACrB,OAChC,OAAOH,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAA0C;QAGnF,MAAMyB,aAAaC,SAASF,eAAe;QAC3C,IAAIG,MAAMF,eAAeA,aAAa,GAClC,OAAOzB,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAsB;QAG/D,IAAIU;QACJ,IAAI;YACAA,UAAUC,kBAAkBR,OAAOF;QACvC,EAAE,OAAOW,KAAK;YACV,OAAOZ,MACF,IAAI,CAAC,KACL,IAAI,CAAC;gBAAE,OAAOY,eAAeC,QAAQD,IAAI,OAAO,GAAG;YAAiB;QAC7E;QAEA,MAAMgB,cAAc,CAAC,QAAQ,EAAElB,QAAQ,QAAQ,CAAC,WAAW,EAAEa,SAAS,MAAM,EAAEE,YAAY;QAE1F,IAAIf,QAAQ,GAAG,KAAKkB,aAChB,OAAO5B,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAsB;QAG/D,MAAMc,WAAW3B,UAAAA,IAAS,CAACF,aAAa2C;QAExC,IAAI,CAAC7C,gBAAgB+B,UAAU7B,cAC3B,OAAOe,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;YAAE,OAAO;QAAgB;QAGzD,MAAMe,UAAU5B,UAAAA,OAAY,CAAC2B;QAC7B,MAAME,MAAMD,SAAS;YAAE,WAAW;QAAK;QAEvC,MAAMtB,OAAOM,QAAQ,IAAI;QAEzB,MAAM8B,UAAUf,UAAUrB;QAE1B,MAAMqC,OAAOC,WAAW,OAAO,MAAM,CAACtC,MAAM,MAAM,CAAC;QAEnD,OAAOO,MAAM,IAAI,CAAC,KAAK,MAAM,CAAC,QAAQ8B,MAAM,IAAI;IACpD;AACJ;AAEAnC,mBAAmB,IAAI,GAAG"}
@@ -1,2 +0,0 @@
1
- import type { FastifyRequest } from "fastify";
2
- export declare const resolveServerUrl: (request: FastifyRequest) => Promise<string>;
@@ -1,10 +0,0 @@
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
@@ -1 +0,0 @@
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"}