@maas/payload-plugin-media-cloud 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/handleDelete.d.mts +3 -3
- package/dist/adapter/handleDelete.mjs +6 -6
- package/dist/adapter/handleDelete.mjs.map +1 -1
- package/dist/adapter/handleUpload.mjs.map +1 -1
- package/dist/adapter/staticHandler.d.mts +5 -3
- package/dist/adapter/staticHandler.mjs +8 -7
- package/dist/adapter/staticHandler.mjs.map +1 -1
- package/dist/adapter/storageAdapter.d.mts +3 -3
- package/dist/adapter/storageAdapter.mjs +4 -4
- package/dist/adapter/storageAdapter.mjs.map +1 -1
- package/dist/collectionHooks/afterChange.d.mts +7 -0
- package/dist/collectionHooks/afterChange.mjs +39 -0
- package/dist/collectionHooks/afterChange.mjs.map +1 -0
- package/dist/collectionHooks/beforeChange.d.mts +7 -0
- package/dist/collectionHooks/beforeChange.mjs +33 -0
- package/dist/collectionHooks/beforeChange.mjs.map +1 -0
- package/dist/collections/mediaCollection.d.mts +3 -2
- package/dist/collections/mediaCollection.mjs +36 -187
- package/dist/collections/mediaCollection.mjs.map +1 -1
- package/dist/components/folderFileCard/folderFileCard.d.mts +13 -0
- package/dist/components/folderFileCard/folderFileCard.mjs +75 -0
- package/dist/components/folderFileCard/folderFileCard.mjs.map +1 -0
- package/dist/components/gridContext/gridContext.d.mts +51 -0
- package/dist/components/gridContext/gridContext.mjs +227 -0
- package/dist/components/gridContext/gridContext.mjs.map +1 -0
- package/dist/components/gridView/gridView.css +50 -0
- package/dist/components/gridView/gridView.d.mts +16 -0
- package/dist/components/gridView/gridView.mjs +283 -0
- package/dist/components/gridView/gridView.mjs.map +1 -0
- package/dist/components/gridView/index.d.mts +2 -0
- package/dist/components/gridView/index.mjs +3 -0
- package/dist/components/index.d.mts +9 -7
- package/dist/components/index.mjs +5 -4
- package/dist/components/itemCardGrid/itemCardGrid.css +12 -0
- package/dist/components/itemCardGrid/itemCardGrid.d.mts +18 -0
- package/dist/components/itemCardGrid/itemCardGrid.mjs +42 -0
- package/dist/components/itemCardGrid/itemCardGrid.mjs.map +1 -0
- package/dist/components/muxPreview/index.d.mts +2 -0
- package/dist/components/muxPreview/index.mjs +3 -0
- package/dist/components/{mux-preview/mux-preview.d.mts → muxPreview/muxPreview.d.mts} +2 -2
- package/dist/components/muxPreview/muxPreview.mjs +65 -0
- package/dist/components/muxPreview/muxPreview.mjs.map +1 -0
- package/dist/components/uploadHandler/index.d.mts +2 -0
- package/dist/components/uploadHandler/index.mjs +3 -0
- package/dist/components/uploadHandler/uploadHandler.d.mts +20 -0
- package/dist/components/{upload-handler/upload-handler.mjs → uploadHandler/uploadHandler.mjs} +82 -52
- package/dist/components/uploadHandler/uploadHandler.mjs.map +1 -0
- package/dist/components/uploadManager/index.d.mts +2 -0
- package/dist/components/uploadManager/index.mjs +3 -0
- package/dist/components/{upload-manager/upload-manager.d.mts → uploadManager/uploadManager.d.mts} +3 -3
- package/dist/components/uploadManager/uploadManager.mjs +363 -0
- package/dist/components/uploadManager/uploadManager.mjs.map +1 -0
- package/dist/endpoints/fileExistsHandler.d.mts +12 -0
- package/dist/endpoints/{tusFileExistsHandler.mjs → fileExistsHandler.mjs} +7 -7
- package/dist/endpoints/fileExistsHandler.mjs.map +1 -0
- package/dist/endpoints/muxAssetHandler.d.mts +1 -0
- package/dist/endpoints/muxAssetHandler.mjs +3 -3
- package/dist/endpoints/muxAssetHandler.mjs.map +1 -1
- package/dist/endpoints/muxCreateUploadHandler.mjs.map +1 -1
- package/dist/endpoints/muxWebhookHandler.d.mts +1 -0
- package/dist/endpoints/muxWebhookHandler.mjs +6 -5
- package/dist/endpoints/muxWebhookHandler.mjs.map +1 -1
- package/dist/endpoints/tusCleanupHandler.d.mts +3 -2
- package/dist/endpoints/tusCleanupHandler.mjs +4 -4
- package/dist/endpoints/tusCleanupHandler.mjs.map +1 -1
- package/dist/endpoints/tusFolderHandler.d.mts +12 -0
- package/dist/endpoints/tusFolderHandler.mjs +39 -0
- package/dist/endpoints/tusFolderHandler.mjs.map +1 -0
- package/dist/endpoints/tusPostProcessorHandler.d.mts +3 -2
- package/dist/endpoints/tusPostProcessorHandler.mjs +6 -5
- package/dist/endpoints/tusPostProcessorHandler.mjs.map +1 -1
- package/dist/fields/alt.d.mts +7 -0
- package/dist/fields/alt.mjs +10 -0
- package/dist/fields/alt.mjs.map +1 -0
- package/dist/fields/filename.d.mts +7 -0
- package/dist/fields/filename.mjs +14 -0
- package/dist/fields/filename.mjs.map +1 -0
- package/dist/fields/height.d.mts +7 -0
- package/dist/fields/height.mjs +15 -0
- package/dist/fields/height.mjs.map +1 -0
- package/dist/fields/mux.d.mts +7 -0
- package/dist/fields/mux.mjs +149 -0
- package/dist/fields/mux.mjs.map +1 -0
- package/dist/fields/path.d.mts +7 -0
- package/dist/fields/path.mjs +14 -0
- package/dist/fields/path.mjs.map +1 -0
- package/dist/fields/storage.d.mts +7 -0
- package/dist/fields/storage.mjs +18 -0
- package/dist/fields/storage.mjs.map +1 -0
- package/dist/fields/thumbnail.d.mts +7 -0
- package/dist/fields/thumbnail.mjs +17 -0
- package/dist/fields/thumbnail.mjs.map +1 -0
- package/dist/fields/width.d.mts +7 -0
- package/dist/fields/width.mjs +15 -0
- package/dist/fields/width.mjs.map +1 -0
- package/dist/hooks/useErrorHandler.d.mts +1 -1
- package/dist/hooks/useErrorHandler.mjs +26 -7
- package/dist/hooks/useErrorHandler.mjs.map +1 -1
- package/dist/hooks/useMediaCloudEmitter.mjs.map +1 -1
- package/dist/plugin.d.mts +5 -4
- package/dist/plugin.mjs +53 -29
- package/dist/plugin.mjs.map +1 -1
- package/dist/tus/stores/s3/{expiration-manager.d.mts → expirationManager.d.mts} +4 -3
- package/dist/tus/stores/s3/{expiration-manager.mjs → expirationManager.mjs} +6 -3
- package/dist/tus/stores/s3/expirationManager.mjs.map +1 -0
- package/dist/tus/stores/s3/{file-operations.d.mts → fileOperations.d.mts} +2 -2
- package/dist/tus/stores/s3/{file-operations.mjs → fileOperations.mjs} +2 -2
- package/dist/tus/stores/s3/fileOperations.mjs.map +1 -0
- package/dist/tus/stores/s3/index.d.mts +1 -1
- package/dist/tus/stores/s3/index.mjs +20 -9
- package/dist/tus/stores/s3/index.mjs.map +1 -1
- package/dist/tus/stores/s3/{metadata-manager.d.mts → metadataManager.d.mts} +4 -2
- package/dist/tus/stores/s3/{metadata-manager.mjs → metadataManager.mjs} +6 -5
- package/dist/tus/stores/s3/metadataManager.mjs.map +1 -0
- package/dist/tus/stores/s3/{parts-manager.d.mts → partsManager.d.mts} +4 -4
- package/dist/tus/stores/s3/{parts-manager.mjs → partsManager.mjs} +67 -29
- package/dist/tus/stores/s3/partsManager.mjs.map +1 -0
- package/dist/tus/stores/s3/{s3-store.d.mts → s3Store.d.mts} +38 -32
- package/dist/tus/stores/s3/{s3-store.mjs → s3Store.mjs} +104 -59
- package/dist/tus/stores/s3/s3Store.mjs.map +1 -0
- package/dist/tus/stores/s3/semaphore.mjs.map +1 -1
- package/dist/types/errors.d.mts +32 -0
- package/dist/types/errors.mjs +32 -0
- package/dist/types/errors.mjs.map +1 -1
- package/dist/types/index.d.mts +42 -4
- package/dist/utils/buildS3Path.d.mts +10 -0
- package/dist/utils/buildS3Path.mjs +16 -0
- package/dist/utils/buildS3Path.mjs.map +1 -0
- package/dist/utils/buildThumbnailURL.d.mts +14 -0
- package/dist/utils/buildThumbnailURL.mjs +10 -0
- package/dist/utils/buildThumbnailURL.mjs.map +1 -0
- package/dist/utils/defaultOptions.d.mts +7 -0
- package/dist/utils/defaultOptions.mjs +12 -0
- package/dist/utils/defaultOptions.mjs.map +1 -0
- package/dist/utils/file.d.mts +16 -2
- package/dist/utils/file.mjs +58 -6
- package/dist/utils/file.mjs.map +1 -1
- package/dist/utils/mux.mjs +19 -8
- package/dist/utils/mux.mjs.map +1 -1
- package/dist/utils/tus.d.mts +9 -6
- package/dist/utils/tus.mjs +31 -11
- package/dist/utils/tus.mjs.map +1 -1
- package/package.json +11 -4
- package/dist/components/mux-preview/index.d.mts +0 -2
- package/dist/components/mux-preview/index.mjs +0 -3
- package/dist/components/mux-preview/mux-preview.mjs +0 -29
- package/dist/components/mux-preview/mux-preview.mjs.map +0 -1
- package/dist/components/upload-handler/index.d.mts +0 -2
- package/dist/components/upload-handler/index.mjs +0 -3
- package/dist/components/upload-handler/upload-handler.d.mts +0 -22
- package/dist/components/upload-handler/upload-handler.mjs.map +0 -1
- package/dist/components/upload-manager/index.d.mts +0 -2
- package/dist/components/upload-manager/index.mjs +0 -3
- package/dist/components/upload-manager/upload-manager.mjs +0 -273
- package/dist/components/upload-manager/upload-manager.mjs.map +0 -1
- package/dist/endpoints/tusFileExistsHandler.d.mts +0 -11
- package/dist/endpoints/tusFileExistsHandler.mjs.map +0 -1
- package/dist/tus/stores/s3/expiration-manager.mjs.map +0 -1
- package/dist/tus/stores/s3/file-operations.mjs.map +0 -1
- package/dist/tus/stores/s3/metadata-manager.mjs.map +0 -1
- package/dist/tus/stores/s3/parts-manager.mjs.map +0 -1
- package/dist/tus/stores/s3/s3-store.mjs.map +0 -1
- /package/dist/components/{upload-manager/upload-manager.css → uploadManager/uploadManager.css} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import AWS from "@aws-sdk/client-s3";
|
|
2
2
|
|
|
3
|
-
//#region src/tus/stores/s3/
|
|
3
|
+
//#region src/tus/stores/s3/fileOperations.d.ts
|
|
4
4
|
type CalculateOptimalPartSizeArgs = {
|
|
5
5
|
size?: number;
|
|
6
6
|
};
|
|
@@ -63,4 +63,4 @@ declare class S3FileOperations {
|
|
|
63
63
|
declare function calculateOffsetFromParts(args: CalculateOffsetFromPartsExportArgs): number;
|
|
64
64
|
//#endregion
|
|
65
65
|
export { S3FileOperations, calculateOffsetFromParts };
|
|
66
|
-
//# sourceMappingURL=
|
|
66
|
+
//# sourceMappingURL=fileOperations.d.mts.map
|
|
@@ -5,7 +5,7 @@ import fs from "node:fs";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
|
|
8
|
-
//#region src/tus/stores/s3/
|
|
8
|
+
//#region src/tus/stores/s3/fileOperations.ts
|
|
9
9
|
const { throwError } = useErrorHandler();
|
|
10
10
|
var S3FileOperations = class {
|
|
11
11
|
constructor(maxMultipartParts, maxUploadSize, minPartSize, preferredPartSize) {
|
|
@@ -86,4 +86,4 @@ function calculateOffsetFromParts(args) {
|
|
|
86
86
|
|
|
87
87
|
//#endregion
|
|
88
88
|
export { S3FileOperations, calculateOffsetFromParts };
|
|
89
|
-
//# sourceMappingURL=
|
|
89
|
+
//# sourceMappingURL=fileOperations.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileOperations.mjs","names":["crypto","fs","os","path","useErrorHandler","MediaCloudErrors","NodeFSError","AWS","CalculateOptimalPartSizeArgs","size","CalculateOffsetFromPartsArgs","parts","Array","Part","CalculatePartNumberArgs","GenerateUniqueTmpFileNameArgs","template","CalculateOffsetFromPartsExportArgs","Size","throwError","S3FileOperations","constructor","maxMultipartParts","maxUploadSize","minPartSize","preferredPartSize","calculateOptimalPartSize","args","uploadSize","undefined","optimalPartSize","Math","ceil","max","calculateOffsetFromParts","length","reduce","a","b","calculatePartNumber","PartNumber","generateUniqueTmpFileName","Promise","tries","i","randomId","randomBytes","toString","filePath","join","tmpdir","promises","access","constants","F_OK","error","nodeError","code","S3_UNIQUE_NAME_ERROR","Error"],"sources":["../../../../src/tus/stores/s3/fileOperations.ts"],"sourcesContent":["import crypto from 'node:crypto'\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../../../types/errors'\n\nimport type { NodeFSError } from '../../../types'\nimport type AWS from '@aws-sdk/client-s3'\n\ntype CalculateOptimalPartSizeArgs = {\n size?: number\n}\n\ntype CalculateOffsetFromPartsArgs = {\n parts?: Array<AWS.Part>\n}\n\ntype CalculatePartNumberArgs = {\n parts: Array<AWS.Part>\n}\n\ntype GenerateUniqueTmpFileNameArgs = {\n template: string\n}\n\ntype CalculateOffsetFromPartsExportArgs = {\n parts?: Array<{ Size?: number }>\n}\n\nconst { throwError } = useErrorHandler()\n\nexport class S3FileOperations {\n constructor(\n private maxMultipartParts: number,\n private maxUploadSize: number,\n private minPartSize: number,\n private preferredPartSize: number\n ) {}\n\n /**\n * Calculates the optimal part size for S3 multipart upload\n * @param args - The function arguments\n * @param args.size - The upload size in bytes (optional)\n * @returns The optimal part size in bytes\n */\n calculateOptimalPartSize(args: CalculateOptimalPartSizeArgs): number {\n const { size } = args\n // When upload size is not known, default to preferredPartSize\n // Assuming maxUploadSize can lead to extremely large chunks (hundreds of GB),\n // which breaks streaming/resumable uploads in practice\n let uploadSize = size\n if (uploadSize === undefined) {\n uploadSize = this.preferredPartSize\n }\n\n let optimalPartSize: number\n\n // When upload is smaller or equal to preferredPartSize, upload in just one part\n if (uploadSize <= this.preferredPartSize) {\n optimalPartSize = uploadSize\n }\n // Does the upload fit in maxMultipartParts parts or less with preferredPartSize\n else if (uploadSize <= this.preferredPartSize * this.maxMultipartParts) {\n optimalPartSize = this.preferredPartSize\n // The upload is too big for the preferred size\n // We divide the size with the max amount of parts and round it up\n } else {\n optimalPartSize = Math.ceil(uploadSize / this.maxMultipartParts)\n }\n\n // Always ensure the part size is at least minPartSize\n return Math.max(optimalPartSize, this.minPartSize)\n }\n\n /**\n * Calculates the offset based on uploaded parts\n * @param args - The function arguments\n * @param args.parts - Array of uploaded parts (optional)\n * @returns The total offset in bytes\n */\n calculateOffsetFromParts(args: CalculateOffsetFromPartsArgs): number {\n const { parts } = args\n return parts && parts.length > 0\n ? parts.reduce((a, b) => a + (b.Size ?? 0), 0)\n : 0\n }\n\n /**\n * Calculates the next part number based on existing parts\n * @param args - The function arguments\n * @param args.parts - Array of uploaded parts\n * @returns The next part number to use\n */\n calculatePartNumber(args: CalculatePartNumberArgs): number {\n const { parts } = args\n return parts.length > 0 ? parts[parts.length - 1].PartNumber! + 1 : 1\n }\n\n /**\n * Generates a unique temporary file name\n * @param args - The function arguments\n * @param args.template - The template string for the file name\n * @returns Promise that resolves to the unique file path\n * @throws Error if unable to find unique name after max tries\n */\n async generateUniqueTmpFileName(\n args: GenerateUniqueTmpFileNameArgs\n ): Promise<string> {\n const { template } = args\n const tries = 5\n for (let i = 0; i < tries; i++) {\n const randomId = crypto.randomBytes(16).toString('hex')\n const filePath = path.join(os.tmpdir(), `${template}${randomId}`)\n try {\n await fs.promises.access(filePath, fs.constants.F_OK)\n } catch (error) {\n const nodeError = error as NodeFSError\n if (nodeError.code === 'ENOENT') {\n return filePath\n }\n }\n }\n throwError(MediaCloudErrors.S3_UNIQUE_NAME_ERROR)\n throw new Error() // This will never execute but satisfies TypeScript\n }\n}\n\n/**\n * Calculates the total offset from uploaded parts\n * @param args - The function arguments\n * @param args.parts - Array of parts with size information (optional)\n * @returns The total offset in bytes\n */\nexport function calculateOffsetFromParts(\n args: CalculateOffsetFromPartsExportArgs\n) {\n const { parts } = args\n return parts && parts.length > 0\n ? parts.reduce((a, b) => a + (b.Size ?? 0), 0)\n : 0\n}\n"],"mappings":";;;;;;;;AA+BA,MAAM,EAAEmB,eAAef,iBAAiB;AAExC,IAAagB,mBAAb,MAA8B;CAC5BC,YACE,AAAQC,mBACR,AAAQC,eACR,AAAQC,aACR,AAAQC,mBACR;EAJQH;EACAC;EACAC;EACAC;;;;;;;;CASVC,yBAAyBC,MAA4C;EACnE,MAAM,EAAElB,SAASkB;EAIjB,IAAIC,aAAanB;AACjB,MAAImB,eAAeC,OACjBD,cAAa,KAAKH;EAGpB,IAAIK;AAGJ,MAAIF,cAAc,KAAKH,kBACrBK,mBAAkBF;WAGXA,cAAc,KAAKH,oBAAoB,KAAKH,kBACnDQ,mBAAkB,KAAKL;MAIvBK,mBAAkBC,KAAKC,KAAKJ,aAAa,KAAKN,kBAAkB;AAIlE,SAAOS,KAAKE,IAAIH,iBAAiB,KAAKN,YAAY;;;;;;;;CASpDU,yBAAyBP,MAA4C;EACnE,MAAM,EAAEhB,UAAUgB;AAClB,SAAOhB,SAASA,MAAMwB,SAAS,IAC3BxB,MAAMyB,QAAQC,GAAGC,MAAMD,KAAKC,EAAEpB,QAAQ,IAAI,EAAE,GAC5C;;;;;;;;CASNqB,oBAAoBZ,MAAuC;EACzD,MAAM,EAAEhB,UAAUgB;AAClB,SAAOhB,MAAMwB,SAAS,IAAIxB,MAAMA,MAAMwB,SAAS,GAAGK,aAAc,IAAI;;;;;;;;;CAUtE,MAAMC,0BACJd,MACiB;EACjB,MAAM,EAAEX,aAAaW;EACrB,MAAMgB,QAAQ;AACd,OAAK,IAAIC,IAAI,GAAGA,IAAID,OAAOC,KAAK;GAC9B,MAAMC,WAAW7C,OAAO8C,YAAY,GAAG,CAACC,SAAS,MAAM;GACvD,MAAMC,WAAW7C,KAAK8C,KAAK/C,GAAGgD,QAAQ,EAAE,GAAGlC,WAAW6B,WAAW;AACjE,OAAI;AACF,UAAM5C,GAAGkD,SAASC,OAAOJ,UAAU/C,GAAGoD,UAAUC,KAAK;YAC9CC,OAAO;AAEd,QADkBA,MACJE,SAAS,SACrB,QAAOT;;;AAIb7B,aAAWd,iBAAiBqD,qBAAqB;AACjD,QAAM,IAAIC,OAAO;;;;;;;;;AAUrB,SAAgBzB,yBACdP,MACA;CACA,MAAM,EAAEhB,UAAUgB;AAClB,QAAOhB,SAASA,MAAMwB,SAAS,IAC3BxB,MAAMyB,QAAQC,GAAGC,MAAMD,KAAKC,EAAEpB,QAAQ,IAAI,EAAE,GAC5C"}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { MediaCloudErrors } from "../../../types/errors.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { S3Store } from "./
|
|
2
|
+
import { useMagicError } from "../../../error-handler/dist/index.mjs";
|
|
3
|
+
import { S3Store } from "./s3Store.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/tus/stores/s3/index.ts
|
|
6
|
-
const {
|
|
6
|
+
const magicError = useMagicError({ prefix: "PLUGIN-MEDIA-CLOUD" });
|
|
7
|
+
/**
|
|
8
|
+
* Validates S3 configuration options
|
|
9
|
+
* @param s3Config - The S3 configuration to validate
|
|
10
|
+
* @throws {Error} When required S3 configuration is missing
|
|
11
|
+
*/
|
|
12
|
+
function validateS3Config(s3Config) {
|
|
13
|
+
magicError.assert(s3Config?.bucket, MediaCloudErrors.S3_CONFIG_MISSING);
|
|
14
|
+
magicError.assert(s3Config?.region, MediaCloudErrors.S3_CONFIG_MISSING);
|
|
15
|
+
magicError.assert(s3Config?.accessKeyId, MediaCloudErrors.S3_CONFIG_MISSING);
|
|
16
|
+
magicError.assert(s3Config?.secretAccessKey, MediaCloudErrors.S3_CONFIG_MISSING);
|
|
17
|
+
}
|
|
7
18
|
/**
|
|
8
19
|
* Creates and configures an S3Store instance
|
|
9
20
|
* @param s3Config - The S3 configuration options
|
|
@@ -11,17 +22,17 @@ const { throwError } = useErrorHandler();
|
|
|
11
22
|
* @throws {Error} When required S3 configuration is missing
|
|
12
23
|
*/
|
|
13
24
|
function createS3Store(s3Config) {
|
|
14
|
-
|
|
25
|
+
validateS3Config(s3Config);
|
|
15
26
|
return new S3Store({ s3ClientConfig: {
|
|
16
27
|
acl: "public-read",
|
|
17
|
-
bucket: s3Config
|
|
28
|
+
bucket: s3Config?.bucket ?? "",
|
|
18
29
|
credentials: {
|
|
19
|
-
accessKeyId: s3Config
|
|
20
|
-
secretAccessKey: s3Config
|
|
30
|
+
accessKeyId: s3Config?.accessKeyId ?? "",
|
|
31
|
+
secretAccessKey: s3Config?.secretAccessKey ?? ""
|
|
21
32
|
},
|
|
22
|
-
endpoint: s3Config
|
|
33
|
+
endpoint: s3Config?.endpoint ?? "",
|
|
23
34
|
forcePathStyle: true,
|
|
24
|
-
region: s3Config
|
|
35
|
+
region: s3Config?.region ?? ""
|
|
25
36
|
} });
|
|
26
37
|
}
|
|
27
38
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/tus/stores/s3/index.ts"],"sourcesContent":["import { MediaCloudErrors } from '../../../types/errors'\nimport { S3Store } from './
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["MediaCloudErrors","S3Store","useMagicError","MediaCloudPluginOptions","UseMagicErrorReturn","magicError","prefix","validateS3Config","s3Config","assert","bucket","S3_CONFIG_MISSING","region","accessKeyId","secretAccessKey","createS3Store","s3ClientConfig","acl","credentials","endpoint","forcePathStyle"],"sources":["../../../../src/tus/stores/s3/index.ts"],"sourcesContent":["import { MediaCloudErrors } from '../../../types/errors'\nimport { S3Store } from './s3Store'\nimport { useMagicError } from '@maas/error-handler'\n\nimport type { MediaCloudPluginOptions } from '../../../types'\nimport type { UseMagicErrorReturn } from '@maas/error-handler'\n\nconst magicError: UseMagicErrorReturn = useMagicError({\n prefix: 'PLUGIN-MEDIA-CLOUD',\n})\n\n/**\n * Validates S3 configuration options\n * @param s3Config - The S3 configuration to validate\n * @throws {Error} When required S3 configuration is missing\n */\nfunction validateS3Config(s3Config: MediaCloudPluginOptions['s3']): void {\n magicError.assert(s3Config?.bucket, MediaCloudErrors.S3_CONFIG_MISSING)\n magicError.assert(s3Config?.region, MediaCloudErrors.S3_CONFIG_MISSING)\n magicError.assert(s3Config?.accessKeyId, MediaCloudErrors.S3_CONFIG_MISSING)\n magicError.assert(\n s3Config?.secretAccessKey,\n MediaCloudErrors.S3_CONFIG_MISSING\n )\n}\n\n/**\n * Creates and configures an S3Store instance\n * @param s3Config - The S3 configuration options\n * @returns A configured S3Store instance\n * @throws {Error} When required S3 configuration is missing\n */\nexport function createS3Store(\n s3Config: MediaCloudPluginOptions['s3']\n): S3Store {\n validateS3Config(s3Config)\n\n return new S3Store({\n s3ClientConfig: {\n acl: 'public-read',\n bucket: s3Config?.bucket ?? '',\n credentials: {\n accessKeyId: s3Config?.accessKeyId ?? '',\n secretAccessKey: s3Config?.secretAccessKey ?? '',\n },\n endpoint: s3Config?.endpoint ?? '',\n forcePathStyle: true,\n region: s3Config?.region ?? '',\n },\n })\n}\n"],"mappings":";;;;;AAOA,MAAMK,aAAkCH,cAAc,EACpDI,QAAQ,sBACT,CAAC;;;;;;AAOF,SAASC,iBAAiBC,UAA+C;AACvEH,YAAWI,OAAOD,UAAUE,QAAQV,iBAAiBW,kBAAkB;AACvEN,YAAWI,OAAOD,UAAUI,QAAQZ,iBAAiBW,kBAAkB;AACvEN,YAAWI,OAAOD,UAAUK,aAAab,iBAAiBW,kBAAkB;AAC5EN,YAAWI,OACTD,UAAUM,iBACVd,iBAAiBW,kBAClB;;;;;;;;AASH,SAAgBI,cACdP,UACS;AACTD,kBAAiBC,SAAS;AAE1B,QAAO,IAAIP,QAAQ,EACjBe,gBAAgB;EACdC,KAAK;EACLP,QAAQF,UAAUE,UAAU;EAC5BQ,aAAa;GACXL,aAAaL,UAAUK,eAAe;GACtCC,iBAAiBN,UAAUM,mBAAmB;GAC/C;EACDK,UAAUX,UAAUW,YAAY;EAChCC,gBAAgB;EAChBR,QAAQJ,UAAUI,UAAU;EAC9B,EACD,CAAC"}
|
|
@@ -2,12 +2,13 @@ import { TusUploadMetadata } from "../../../types/index.mjs";
|
|
|
2
2
|
import { S3 } from "@aws-sdk/client-s3";
|
|
3
3
|
import { Upload } from "@tus/utils";
|
|
4
4
|
|
|
5
|
-
//#region src/tus/stores/s3/
|
|
5
|
+
//#region src/tus/stores/s3/metadataManager.d.ts
|
|
6
6
|
type GenerateInfoKeyArgs = {
|
|
7
7
|
id: string;
|
|
8
8
|
};
|
|
9
9
|
type GeneratePartKeyArgs = {
|
|
10
10
|
id: string;
|
|
11
|
+
prefix?: string;
|
|
11
12
|
isIncomplete?: boolean;
|
|
12
13
|
};
|
|
13
14
|
type GetMetadataArgs = {
|
|
@@ -37,6 +38,7 @@ declare class S3MetadataManager {
|
|
|
37
38
|
* Generates the S3 key for part files
|
|
38
39
|
* @param args - The function arguments
|
|
39
40
|
* @param args.id - The file ID
|
|
41
|
+
* @param args.prefix - Path prefix for nested media items
|
|
40
42
|
* @param args.isIncomplete - Whether this is an incomplete part
|
|
41
43
|
* @returns The S3 key for the part file
|
|
42
44
|
*/
|
|
@@ -68,4 +70,4 @@ declare class S3MetadataManager {
|
|
|
68
70
|
}
|
|
69
71
|
//#endregion
|
|
70
72
|
export { S3MetadataManager };
|
|
71
|
-
//# sourceMappingURL=
|
|
73
|
+
//# sourceMappingURL=metadataManager.d.mts.map
|
|
@@ -2,7 +2,7 @@ import { MediaCloudLogs } from "../../../types/errors.mjs";
|
|
|
2
2
|
import { useErrorHandler } from "../../../hooks/useErrorHandler.mjs";
|
|
3
3
|
import { TUS_RESUMABLE, Upload } from "@tus/utils";
|
|
4
4
|
|
|
5
|
-
//#region src/tus/stores/s3/
|
|
5
|
+
//#region src/tus/stores/s3/metadataManager.ts
|
|
6
6
|
const { log } = useErrorHandler();
|
|
7
7
|
var S3MetadataManager = class {
|
|
8
8
|
constructor(client, bucket, shouldUseExpirationTags, generateCompleteTag) {
|
|
@@ -18,19 +18,20 @@ var S3MetadataManager = class {
|
|
|
18
18
|
* @returns The S3 key for the metadata file
|
|
19
19
|
*/
|
|
20
20
|
generateInfoKey(args) {
|
|
21
|
-
|
|
22
|
-
return `${id}.info`;
|
|
21
|
+
return `${args.id}.info`;
|
|
23
22
|
}
|
|
24
23
|
/**
|
|
25
24
|
* Generates the S3 key for part files
|
|
26
25
|
* @param args - The function arguments
|
|
27
26
|
* @param args.id - The file ID
|
|
27
|
+
* @param args.prefix - Path prefix for nested media items
|
|
28
28
|
* @param args.isIncomplete - Whether this is an incomplete part
|
|
29
29
|
* @returns The S3 key for the part file
|
|
30
30
|
*/
|
|
31
31
|
generatePartKey(args) {
|
|
32
|
-
const { id, isIncomplete = false } = args;
|
|
32
|
+
const { id, prefix, isIncomplete = false } = args;
|
|
33
33
|
let key = id;
|
|
34
|
+
if (prefix) key = `${prefix}/${key}`;
|
|
34
35
|
if (isIncomplete) key += ".part";
|
|
35
36
|
return key;
|
|
36
37
|
}
|
|
@@ -110,4 +111,4 @@ var S3MetadataManager = class {
|
|
|
110
111
|
|
|
111
112
|
//#endregion
|
|
112
113
|
export { S3MetadataManager };
|
|
113
|
-
//# sourceMappingURL=
|
|
114
|
+
//# sourceMappingURL=metadataManager.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadataManager.mjs","names":["TUS_RESUMABLE","Upload","useErrorHandler","MediaCloudLogs","S3","TusUploadMetadata","GenerateInfoKeyArgs","id","GeneratePartKeyArgs","prefix","isIncomplete","GetMetadataArgs","SaveMetadataArgs","upload","uploadId","CompleteMetadataArgs","log","S3MetadataManager","constructor","client","bucket","shouldUseExpirationTags","generateCompleteTag","value","generateInfoKey","args","generatePartKey","key","getMetadata","Promise","Body","Metadata","getObject","Bucket","Key","file","JSON","parse","transformToString","parsedSize","size","undefined","Number","metadata","creation_date","offset","parseInt","isFinite","storage","saveMetadata","S3_STORE_METADATA_SAVING","putObject","stringify","Tagging","S3_STORE_METADATA_SAVED","completeMetadata"],"sources":["../../../../src/tus/stores/s3/metadataManager.ts"],"sourcesContent":["import { TUS_RESUMABLE, Upload } from '@tus/utils'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { MediaCloudLogs } from '../../../types/errors'\n\nimport type { S3 } from '@aws-sdk/client-s3'\nimport type { TusUploadMetadata } from '../../../types'\n\ntype GenerateInfoKeyArgs = {\n id: string\n}\n\ntype GeneratePartKeyArgs = {\n id: string\n prefix?: string\n isIncomplete?: boolean\n}\n\ntype GetMetadataArgs = {\n id: string\n}\n\ntype SaveMetadataArgs = {\n upload: Upload\n uploadId: string\n}\n\ntype CompleteMetadataArgs = {\n upload: Upload\n}\n\nconst { log } = useErrorHandler()\n\nexport class S3MetadataManager {\n constructor(\n private client: S3,\n private bucket: string,\n private shouldUseExpirationTags: () => boolean,\n private generateCompleteTag: (value: 'false' | 'true') => string | undefined\n ) {}\n /**\n * Generates the S3 key for metadata info files\n * @param args - The function arguments\n * @param args.id - The file ID\n * @returns The S3 key for the metadata file\n */\n generateInfoKey(args: GenerateInfoKeyArgs): string {\n return `${args.id}.info`\n }\n\n /**\n * Generates the S3 key for part files\n * @param args - The function arguments\n * @param args.id - The file ID\n * @param args.prefix - Path prefix for nested media items\n * @param args.isIncomplete - Whether this is an incomplete part\n * @returns The S3 key for the part file\n */\n generatePartKey(args: GeneratePartKeyArgs): string {\n const { id, prefix, isIncomplete = false } = args\n let key = id\n\n if (prefix) {\n key = `${prefix}/${key}`\n }\n\n if (isIncomplete) {\n key += '.part'\n }\n\n // TODO: introduce ObjectPrefixing for parts and incomplete parts\n // ObjectPrefix is prepended to the name of each S3 object that is created\n // to store uploaded files. It can be used to create a pseudo-directory\n // structure in the bucket, e.g. \"path/to/my/uploads\".\n return key\n }\n\n /**\n * Retrieves upload metadata previously saved in `${file_id}.info`.\n * Always fetches fresh data from S3 to ensure correctness.\n * @param args - The function arguments\n * @param args.id - The file ID to retrieve metadata for\n * @returns Promise that resolves to the upload metadata\n */\n async getMetadata(args: GetMetadataArgs): Promise<TusUploadMetadata> {\n const { id } = args\n\n const { Body, Metadata } = await this.client.getObject({\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id }),\n })\n\n const file = JSON.parse((await Body?.transformToString()) as string)\n\n const parsedSize =\n file.size === undefined || file.size === null\n ? undefined\n : Number(file.size)\n\n const metadata: TusUploadMetadata = {\n file: new Upload({\n id,\n creation_date: file.creation_date,\n metadata: file.metadata,\n offset: Number.parseInt(file.offset, 10),\n size: Number.isFinite(parsedSize) ? parsedSize : undefined,\n storage: file.storage,\n }),\n 'tus-version': Metadata?.['tus-version'] as string,\n 'upload-id': (Metadata?.['upload-id'] || file['upload-id']) as string,\n }\n\n return metadata\n }\n\n /**\n * Saves upload metadata to a `${file_id}.info` file on S3.\n * The upload-id is stored in the S3 object metadata.\n * @param args - The function arguments\n * @param args.upload - The upload object containing metadata\n * @param args.uploadId - The upload ID for the multipart upload\n * @returns Promise that resolves when metadata is saved\n */\n async saveMetadata(args: SaveMetadataArgs): Promise<void> {\n const { upload, uploadId } = args\n log(MediaCloudLogs.S3_STORE_METADATA_SAVING)\n\n await this.client.putObject({\n Body: JSON.stringify(upload),\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id: upload.id }),\n Metadata: {\n 'tus-version': TUS_RESUMABLE,\n 'upload-id': uploadId,\n },\n Tagging: this.generateCompleteTag('false'),\n })\n log(MediaCloudLogs.S3_STORE_METADATA_SAVED)\n }\n\n /**\n * Completes metadata by updating it with completion tags\n * @param args - The function arguments\n * @param args.upload - The completed upload object\n * @returns Promise that resolves when metadata is updated\n */\n async completeMetadata(args: CompleteMetadataArgs): Promise<void> {\n const { upload } = args\n\n if (!this.shouldUseExpirationTags()) {\n return\n }\n\n const metadata = await this.getMetadata({ id: upload.id })\n const { 'upload-id': uploadId } = metadata\n\n await this.client.putObject({\n Body: JSON.stringify(upload),\n Bucket: this.bucket,\n Key: this.generateInfoKey({ id: upload.id }),\n Metadata: {\n 'tus-version': TUS_RESUMABLE,\n 'upload-id': uploadId,\n },\n Tagging: this.generateCompleteTag('true'),\n })\n }\n}\n"],"mappings":";;;;;AA+BA,MAAM,EAAEgB,QAAQd,iBAAiB;AAEjC,IAAae,oBAAb,MAA+B;CAC7BC,YACE,AAAQC,QACR,AAAQC,QACR,AAAQC,yBACR,AAAQC,qBACR;EAJQH;EACAC;EACAC;EACAC;;;;;;;;CAQVE,gBAAgBC,MAAmC;AACjD,SAAO,GAAGA,KAAKlB,GAAE;;;;;;;;;;CAWnBmB,gBAAgBD,MAAmC;EACjD,MAAM,EAAElB,IAAIE,QAAQC,eAAe,UAAUe;EAC7C,IAAIE,MAAMpB;AAEV,MAAIE,OACFkB,OAAM,GAAGlB,OAAM,GAAIkB;AAGrB,MAAIjB,aACFiB,QAAO;AAOT,SAAOA;;;;;;;;;CAUT,MAAMC,YAAYH,MAAmD;EACnE,MAAM,EAAElB,OAAOkB;EAEf,MAAM,EAAEK,MAAMC,aAAa,MAAM,KAAKZ,OAAOa,UAAU;GACrDC,QAAQ,KAAKb;GACbc,KAAK,KAAKV,gBAAgB,EAAEjB,IAAI,CAAA;GACjC,CAAC;EAEF,MAAM4B,OAAOC,KAAKC,MAAO,MAAMP,MAAMQ,mBAAmB,CAAY;EAEpE,MAAMC,aACJJ,KAAKK,SAASC,UAAaN,KAAKK,SAAS,OACrCC,SACAC,OAAOP,KAAKK,KAAK;AAevB,SAboC;GAClCL,MAAM,IAAIlC,OAAO;IACfM;IACAqC,eAAeT,KAAKS;IACpBD,UAAUR,KAAKQ;IACfE,QAAQH,OAAOI,SAASX,KAAKU,QAAQ,GAAG;IACxCL,MAAME,OAAOK,SAASR,WAAW,GAAGA,aAAaE;IACjDO,SAASb,KAAKa;IACf,CAAC;GACF,eAAejB,WAAW;GAC1B,aAAcA,WAAW,gBAAgBI,KAAK;GAC/C;;;;;;;;;;CAaH,MAAMc,aAAaxB,MAAuC;EACxD,MAAM,EAAEZ,QAAQC,aAAaW;AAC7BT,MAAIb,eAAe+C,yBAAyB;AAE5C,QAAM,KAAK/B,OAAOgC,UAAU;GAC1BrB,MAAMM,KAAKgB,UAAUvC,OAAO;GAC5BoB,QAAQ,KAAKb;GACbc,KAAK,KAAKV,gBAAgB,EAAEjB,IAAIM,OAAON,IAAI,CAAC;GAC5CwB,UAAU;IACR,eAAe/B;IACf,aAAac;IACd;GACDuC,SAAS,KAAK/B,oBAAoB,QAAO;GAC1C,CAAC;AACFN,MAAIb,eAAemD,wBAAwB;;;;;;;;CAS7C,MAAMC,iBAAiB9B,MAA2C;EAChE,MAAM,EAAEZ,WAAWY;AAEnB,MAAI,CAAC,KAAKJ,yBAAyB,CACjC;EAIF,MAAM,EAAE,aAAaP,aADJ,MAAM,KAAKc,YAAY,EAAErB,IAAIM,OAAON,IAAI,CAAC;AAG1D,QAAM,KAAKY,OAAOgC,UAAU;GAC1BrB,MAAMM,KAAKgB,UAAUvC,OAAO;GAC5BoB,QAAQ,KAAKb;GACbc,KAAK,KAAKV,gBAAgB,EAAEjB,IAAIM,OAAON,IAAI,CAAC;GAC5CwB,UAAU;IACR,eAAe/B;IACf,aAAac;IACd;GACDuC,SAAS,KAAK/B,oBAAoB,OAAM;GACzC,CAAC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Semaphore } from "./semaphore.mjs";
|
|
2
|
-
import { S3FileOperations } from "./file-operations.mjs";
|
|
3
2
|
import { IncompletePartInfo, TusUploadMetadata } from "../../../types/index.mjs";
|
|
4
|
-
import { S3MetadataManager } from "./
|
|
3
|
+
import { S3MetadataManager } from "./metadataManager.mjs";
|
|
4
|
+
import { S3FileOperations } from "./fileOperations.mjs";
|
|
5
5
|
import stream, { Readable } from "node:stream";
|
|
6
6
|
import AWS, { S3 } from "@aws-sdk/client-s3";
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
|
|
9
|
-
//#region src/tus/stores/s3/
|
|
9
|
+
//#region src/tus/stores/s3/partsManager.d.ts
|
|
10
10
|
type RetrievePartsArgs = {
|
|
11
11
|
id: string;
|
|
12
12
|
partNumberMarker?: string;
|
|
@@ -127,4 +127,4 @@ declare class S3PartsManager {
|
|
|
127
127
|
}
|
|
128
128
|
//#endregion
|
|
129
129
|
export { S3PartsManager };
|
|
130
|
-
//# sourceMappingURL=
|
|
130
|
+
//# sourceMappingURL=partsManager.d.mts.map
|
|
@@ -6,7 +6,7 @@ import { StreamSplitter } from "@tus/utils";
|
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import os from "node:os";
|
|
8
8
|
|
|
9
|
-
//#region src/tus/stores/s3/
|
|
9
|
+
//#region src/tus/stores/s3/partsManager.ts
|
|
10
10
|
const { log, throwError } = useErrorHandler();
|
|
11
11
|
var S3PartsManager = class {
|
|
12
12
|
constructor(client, bucket, minPartSize, partUploadSemaphore, metadataManager, fileOperations, generateCompleteTag) {
|
|
@@ -35,7 +35,10 @@ var S3PartsManager = class {
|
|
|
35
35
|
}
|
|
36
36
|
const params = {
|
|
37
37
|
Bucket: this.bucket,
|
|
38
|
-
Key:
|
|
38
|
+
Key: this.metadataManager.generatePartKey({
|
|
39
|
+
id,
|
|
40
|
+
prefix: metadata.file.metadata?.prefix ?? void 0
|
|
41
|
+
}),
|
|
39
42
|
PartNumberMarker: partNumberMarker,
|
|
40
43
|
UploadId: metadata["upload-id"]
|
|
41
44
|
};
|
|
@@ -62,8 +65,11 @@ var S3PartsManager = class {
|
|
|
62
65
|
async finishMultipartUpload(args) {
|
|
63
66
|
const { metadata, parts } = args;
|
|
64
67
|
const params = {
|
|
68
|
+
Key: this.metadataManager.generatePartKey({
|
|
69
|
+
id: metadata.file.id,
|
|
70
|
+
prefix: metadata.file.metadata?.prefix ?? void 0
|
|
71
|
+
}),
|
|
65
72
|
Bucket: this.bucket,
|
|
66
|
-
Key: metadata.file.id,
|
|
67
73
|
MultipartUpload: { Parts: parts.map((part) => {
|
|
68
74
|
return {
|
|
69
75
|
ETag: part.ETag,
|
|
@@ -74,8 +80,11 @@ var S3PartsManager = class {
|
|
|
74
80
|
};
|
|
75
81
|
try {
|
|
76
82
|
return (await this.client.completeMultipartUpload(params)).Location;
|
|
77
|
-
} catch (
|
|
78
|
-
throwError(
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throwError({
|
|
85
|
+
...MediaCloudErrors.TUS_UPLOAD_ERROR,
|
|
86
|
+
cause: error
|
|
87
|
+
});
|
|
79
88
|
throw new Error();
|
|
80
89
|
}
|
|
81
90
|
}
|
|
@@ -88,10 +97,12 @@ var S3PartsManager = class {
|
|
|
88
97
|
async getIncompletePart(args) {
|
|
89
98
|
const { id } = args;
|
|
90
99
|
try {
|
|
100
|
+
const { file } = await this.metadataManager.getMetadata({ id });
|
|
91
101
|
return (await this.client.getObject({
|
|
92
102
|
Bucket: this.bucket,
|
|
93
103
|
Key: this.metadataManager.generatePartKey({
|
|
94
104
|
id,
|
|
105
|
+
prefix: file?.metadata?.prefix ?? void 0,
|
|
95
106
|
isIncomplete: true
|
|
96
107
|
})
|
|
97
108
|
})).Body;
|
|
@@ -109,11 +120,13 @@ var S3PartsManager = class {
|
|
|
109
120
|
async getIncompletePartSize(args) {
|
|
110
121
|
const { id } = args;
|
|
111
122
|
try {
|
|
123
|
+
const { file } = await this.metadataManager.getMetadata({ id });
|
|
112
124
|
return (await this.client.headObject({
|
|
113
125
|
Bucket: this.bucket,
|
|
114
126
|
Key: this.metadataManager.generatePartKey({
|
|
115
127
|
id,
|
|
116
|
-
isIncomplete: true
|
|
128
|
+
isIncomplete: true,
|
|
129
|
+
prefix: file?.metadata?.prefix ?? void 0
|
|
117
130
|
})
|
|
118
131
|
})).ContentLength;
|
|
119
132
|
} catch (error) {
|
|
@@ -129,13 +142,22 @@ var S3PartsManager = class {
|
|
|
129
142
|
*/
|
|
130
143
|
async deleteIncompletePart(args) {
|
|
131
144
|
const { id } = args;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
try {
|
|
146
|
+
const { file } = await this.metadataManager.getMetadata({ id });
|
|
147
|
+
await this.client.deleteObject({
|
|
148
|
+
Bucket: this.bucket,
|
|
149
|
+
Key: this.metadataManager.generatePartKey({
|
|
150
|
+
id,
|
|
151
|
+
isIncomplete: true,
|
|
152
|
+
prefix: file?.metadata?.prefix ?? void 0
|
|
153
|
+
})
|
|
154
|
+
});
|
|
155
|
+
} catch (error) {
|
|
156
|
+
throwError({
|
|
157
|
+
...MediaCloudErrors.S3_DELETE_ERROR,
|
|
158
|
+
cause: error
|
|
159
|
+
});
|
|
160
|
+
}
|
|
139
161
|
}
|
|
140
162
|
/**
|
|
141
163
|
* Downloads incomplete part to temporary file
|
|
@@ -187,17 +209,27 @@ var S3PartsManager = class {
|
|
|
187
209
|
*/
|
|
188
210
|
async uploadIncompletePart(args) {
|
|
189
211
|
const { id, readStream } = args;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
212
|
+
try {
|
|
213
|
+
const { file } = await this.metadataManager.getMetadata({ id });
|
|
214
|
+
const data = await this.client.putObject({
|
|
215
|
+
Body: readStream,
|
|
216
|
+
Bucket: this.bucket,
|
|
217
|
+
Key: this.metadataManager.generatePartKey({
|
|
218
|
+
id,
|
|
219
|
+
isIncomplete: true,
|
|
220
|
+
prefix: file?.metadata?.prefix ?? void 0
|
|
221
|
+
}),
|
|
222
|
+
Tagging: this.generateCompleteTag("false")
|
|
223
|
+
});
|
|
224
|
+
log(MediaCloudLogs.S3_STORE_INCOMPLETE_PART_UPLOADED);
|
|
225
|
+
return data.ETag;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
throwError({
|
|
228
|
+
...MediaCloudErrors.TUS_UPLOAD_ERROR,
|
|
229
|
+
cause: error
|
|
230
|
+
});
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
201
233
|
}
|
|
202
234
|
/**
|
|
203
235
|
* Uploads a single part
|
|
@@ -217,7 +249,10 @@ var S3PartsManager = class {
|
|
|
217
249
|
const params = {
|
|
218
250
|
Body: readStream,
|
|
219
251
|
Bucket: this.bucket,
|
|
220
|
-
Key:
|
|
252
|
+
Key: this.metadataManager.generatePartKey({
|
|
253
|
+
id: metadata.file.id,
|
|
254
|
+
prefix: metadata.file.metadata?.prefix ?? void 0
|
|
255
|
+
}),
|
|
221
256
|
PartNumber: partNumber,
|
|
222
257
|
UploadId: metadata["upload-id"]
|
|
223
258
|
};
|
|
@@ -226,9 +261,12 @@ var S3PartsManager = class {
|
|
|
226
261
|
ETag: (await this.client.uploadPart(params)).ETag,
|
|
227
262
|
PartNumber: partNumber
|
|
228
263
|
};
|
|
229
|
-
} catch (
|
|
230
|
-
throwError(
|
|
231
|
-
|
|
264
|
+
} catch (error) {
|
|
265
|
+
throwError({
|
|
266
|
+
...MediaCloudErrors.TUS_UPLOAD_ERROR,
|
|
267
|
+
cause: error
|
|
268
|
+
});
|
|
269
|
+
throw error;
|
|
232
270
|
} finally {
|
|
233
271
|
permit();
|
|
234
272
|
}
|
|
@@ -320,4 +358,4 @@ var S3PartsManager = class {
|
|
|
320
358
|
|
|
321
359
|
//#endregion
|
|
322
360
|
export { S3PartsManager };
|
|
323
|
-
//# sourceMappingURL=
|
|
361
|
+
//# sourceMappingURL=partsManager.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"partsManager.mjs","names":["fs","os","stream","NoSuchKey","NotFound","S3","StreamSplitter","useErrorHandler","MediaCloudErrors","MediaCloudLogs","AWS","Readable","IncompletePartInfo","TusUploadMetadata","S3FileOperations","S3MetadataManager","Semaphore","SemaphorePermit","RetrievePartsArgs","id","partNumberMarker","FinishMultipartUploadArgs","metadata","parts","Array","Part","GetIncompletePartArgs","GetIncompletePartSizeArgs","DeleteIncompletePartArgs","DownloadIncompletePartArgs","UploadIncompletePartArgs","readStream","ReadStream","UploadPartArgs","partNumber","UploadPartsArgs","currentPartNumber","offset","log","throwError","S3PartsManager","constructor","client","bucket","minPartSize","partUploadSemaphore","metadataManager","fileOperations","generateCompleteTag","value","retrieveParts","args","Promise","getMetadata","MUX_UPLOAD_ID_MISSING","Error","params","ListPartsCommandInput","Bucket","Key","generatePartKey","prefix","file","undefined","PartNumberMarker","UploadId","data","listParts","Parts","IsTruncated","rest","NextPartNumberMarker","sort","a","b","PartNumber","finishMultipartUpload","MultipartUpload","map","part","ETag","result","completeMultipartUpload","Location","error","TUS_UPLOAD_ERROR","cause","getIncompletePart","getObject","isIncomplete","Body","getIncompletePartSize","headObject","ContentLength","deleteIncompletePart","deleteObject","S3_DELETE_ERROR","downloadIncompletePart","incompletePart","filePath","generateUniqueTmpFileName","template","incompletePartSize","byteCounterTransform","Transform","transform","chunk","_","callback","length","promises","pipeline","createWriteStream","createReadStream","options","cleanUpOnEnd","fileReader","on","unlink","err","destroy","createReader","path","size","rm","catch","uploadIncompletePart","putObject","Tagging","S3_STORE_INCOMPLETE_PART_UPLOADED","uploadPart","permit","acquire","UploadPartCommandInput","uploadParts","initialOffset","pendingChunkFilepath","bytesUploaded","splitterStream","chunkSize","calculateOptimalPartSize","directory","tmpdir","filepath","partSize","acquiredPermit","isFinalPart","uploadChunk","readable","mappedError","String","deferred","push","S3_STORE_CHUNK_REMOVAL_FAILED","reject","allSettled","all"],"sources":["../../../../src/tus/stores/s3/partsManager.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport stream from 'node:stream'\n\nimport { NoSuchKey, NotFound, type S3 } from '@aws-sdk/client-s3'\nimport { StreamSplitter } from '@tus/utils'\n\nimport { useErrorHandler } from '../../../hooks/useErrorHandler'\nimport { MediaCloudErrors, MediaCloudLogs } from '../../../types/errors'\n\nimport type AWS from '@aws-sdk/client-s3'\nimport type { Readable } from 'node:stream'\nimport type { IncompletePartInfo, TusUploadMetadata } from '../../../types'\nimport type { S3FileOperations } from './fileOperations'\nimport type { S3MetadataManager } from './metadataManager'\nimport type { Semaphore, SemaphorePermit } from './semaphore'\n\ntype RetrievePartsArgs = {\n id: string\n partNumberMarker?: string\n}\n\ntype FinishMultipartUploadArgs = {\n metadata: TusUploadMetadata\n parts: Array<AWS.Part>\n}\n\ntype GetIncompletePartArgs = {\n id: string\n}\n\ntype GetIncompletePartSizeArgs = {\n id: string\n}\n\ntype DeleteIncompletePartArgs = {\n id: string\n}\n\ntype DownloadIncompletePartArgs = {\n id: string\n}\n\ntype UploadIncompletePartArgs = {\n id: string\n readStream: fs.ReadStream | Readable\n}\n\ntype UploadPartArgs = {\n metadata: TusUploadMetadata\n readStream: fs.ReadStream | Readable\n partNumber: number\n}\n\ntype UploadPartsArgs = {\n metadata: TusUploadMetadata\n readStream: stream.Readable\n currentPartNumber: number\n offset: number\n}\n\nconst { log, throwError } = useErrorHandler()\n\nexport class S3PartsManager {\n constructor(\n private client: S3,\n private bucket: string,\n private minPartSize: number,\n private partUploadSemaphore: Semaphore,\n private metadataManager: S3MetadataManager,\n private fileOperations: S3FileOperations,\n private generateCompleteTag: (value: 'false' | 'true') => string | undefined\n ) {}\n\n /**\n * Gets the number of complete parts/chunks already uploaded to S3.\n * Retrieves only consecutive parts.\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @param args.partNumberMarker - Marker for pagination (optional)\n * @returns Promise that resolves to array of uploaded parts\n */\n async retrieveParts(args: RetrievePartsArgs): Promise<Array<AWS.Part>> {\n const { id, partNumberMarker } = args\n const metadata = await this.metadataManager.getMetadata({ id })\n\n if (!metadata['upload-id']) {\n throwError(MediaCloudErrors.MUX_UPLOAD_ID_MISSING)\n throw new Error() // This will never execute but satisfies TypeScript\n }\n\n const params: AWS.ListPartsCommandInput = {\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n prefix: metadata.file.metadata?.prefix ?? undefined,\n }),\n PartNumberMarker: partNumberMarker,\n UploadId: metadata['upload-id'],\n }\n\n const data = await this.client.listParts(params)\n\n let parts = data.Parts ?? []\n\n if (data.IsTruncated) {\n const rest = await this.retrieveParts({\n id,\n partNumberMarker: data.NextPartNumberMarker,\n })\n parts = [...parts, ...rest]\n }\n\n if (!partNumberMarker) {\n parts.sort((a, b) => (a.PartNumber || 0) - (b.PartNumber || 0))\n }\n\n return parts\n }\n\n /**\n * Completes a multipart upload on S3.\n * This is where S3 concatenates all the uploaded parts.\n * @param args - The function arguments\n * @param args.metadata - The upload metadata\n * @param args.parts - Array of uploaded parts to complete\n * @returns Promise that resolves to the location URL (optional)\n */\n async finishMultipartUpload(\n args: FinishMultipartUploadArgs\n ): Promise<string | undefined> {\n const { metadata, parts } = args\n\n const params = {\n Key: this.metadataManager.generatePartKey({\n id: metadata.file.id,\n prefix: metadata.file.metadata?.prefix ?? undefined,\n }),\n Bucket: this.bucket,\n MultipartUpload: {\n Parts: parts.map((part) => {\n return {\n ETag: part.ETag,\n PartNumber: part.PartNumber,\n }\n }),\n },\n UploadId: metadata['upload-id'],\n }\n\n try {\n const result = await this.client.completeMultipartUpload(params)\n return result.Location\n } catch (error) {\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n throw new Error() // This will never execute but satisfies TypeScript\n }\n }\n\n /**\n * Gets incomplete part from S3\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves to readable stream or undefined if not found\n */\n async getIncompletePart(\n args: GetIncompletePartArgs\n ): Promise<Readable | undefined> {\n const { id } = args\n\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n const data = await this.client.getObject({\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n prefix: file?.metadata?.prefix ?? undefined,\n isIncomplete: true,\n }),\n })\n return data.Body as Readable\n } catch (error) {\n if (error instanceof NoSuchKey) {\n return undefined\n }\n throw error\n }\n }\n\n /**\n * Gets the size of an incomplete part\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves to part size or undefined if not found\n */\n async getIncompletePartSize(\n args: GetIncompletePartSizeArgs\n ): Promise<number | undefined> {\n const { id } = args\n\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n const data = await this.client.headObject({\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n prefix: file?.metadata?.prefix ?? undefined,\n }),\n })\n return data.ContentLength\n } catch (error) {\n if (error instanceof NotFound) {\n return undefined\n }\n throw error\n }\n }\n\n /**\n * Deletes an incomplete part\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves when deletion is complete\n */\n async deleteIncompletePart(args: DeleteIncompletePartArgs): Promise<void> {\n const { id } = args\n\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n await this.client.deleteObject({\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n prefix: file?.metadata?.prefix ?? undefined,\n }),\n })\n } catch (error) {\n throwError({\n ...MediaCloudErrors.S3_DELETE_ERROR,\n cause: error,\n })\n }\n }\n\n /**\n * Downloads incomplete part to temporary file\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @returns Promise that resolves to incomplete part info or undefined if not found\n */\n async downloadIncompletePart(\n args: DownloadIncompletePartArgs\n ): Promise<IncompletePartInfo | undefined> {\n const { id } = args\n const incompletePart = await this.getIncompletePart({ id })\n\n if (!incompletePart) {\n return\n }\n const filePath = await this.fileOperations.generateUniqueTmpFileName({\n template: 'tus-s3-incomplete-part-',\n })\n\n try {\n let incompletePartSize = 0\n\n const byteCounterTransform = new stream.Transform({\n transform(chunk, _, callback) {\n incompletePartSize += chunk.length\n callback(null, chunk)\n },\n })\n\n // Write to temporary file\n await stream.promises.pipeline(\n incompletePart,\n byteCounterTransform,\n fs.createWriteStream(filePath)\n )\n\n const createReadStream = (options: { cleanUpOnEnd: boolean }) => {\n const fileReader = fs.createReadStream(filePath)\n\n if (options.cleanUpOnEnd) {\n fileReader.on('end', () => {\n fs.unlink(filePath, () => {})\n })\n\n fileReader.on('error', (err) => {\n fileReader.destroy(err)\n fs.unlink(filePath, () => {})\n })\n }\n\n return fileReader\n }\n\n return {\n createReader: createReadStream,\n path: filePath,\n size: incompletePartSize,\n }\n } catch (err) {\n fs.promises.rm(filePath).catch(() => {})\n throw err\n }\n }\n\n /**\n * Uploads an incomplete part\n * @param args - The function arguments\n * @param args.id - The upload ID\n * @param args.readStream - The stream to read data from\n * @returns Promise that resolves to the ETag of the uploaded part\n */\n async uploadIncompletePart(args: UploadIncompletePartArgs): Promise<string> {\n const { id, readStream } = args\n try {\n const { file } = await this.metadataManager.getMetadata({ id })\n\n const data = await this.client.putObject({\n Body: readStream,\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id,\n isIncomplete: true,\n prefix: file?.metadata?.prefix ?? undefined,\n }),\n Tagging: this.generateCompleteTag('false'),\n })\n log(MediaCloudLogs.S3_STORE_INCOMPLETE_PART_UPLOADED)\n return data.ETag as string\n } catch (error) {\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n throw error\n }\n }\n\n /**\n * Uploads a single part\n * @param args - The function arguments\n * @param args.metadata - The upload metadata\n * @param args.readStream - The stream to read data from\n * @param args.partNumber - The part number to upload\n * @returns Promise that resolves to the ETag of the uploaded part\n */\n async uploadPart(args: UploadPartArgs): Promise<AWS.Part> {\n const { metadata, readStream, partNumber } = args\n const permit = await this.partUploadSemaphore.acquire()\n\n if (!metadata['upload-id']) {\n throwError(MediaCloudErrors.MUX_UPLOAD_ID_MISSING)\n throw new Error() // This will never execute but satisfies TypeScript\n }\n\n const params: AWS.UploadPartCommandInput = {\n Body: readStream,\n Bucket: this.bucket,\n Key: this.metadataManager.generatePartKey({\n id: metadata.file.id,\n prefix: metadata.file.metadata?.prefix ?? undefined,\n }),\n PartNumber: partNumber,\n UploadId: metadata['upload-id'],\n }\n\n try {\n const data = await this.client.uploadPart(params)\n return { ETag: data.ETag, PartNumber: partNumber }\n } catch (error) {\n throwError({ ...MediaCloudErrors.TUS_UPLOAD_ERROR, cause: error })\n throw error\n } finally {\n permit()\n }\n }\n\n /**\n * Uploads a stream to s3 using multiple parts\n * @param args - The function arguments\n * @param args.metadata - The upload metadata\n * @param args.readStream - The stream to read data from\n * @param args.currentPartNumber - The current part number to start from\n * @param args.offset - The byte offset to start from\n * @returns Promise that resolves to the number of bytes uploaded\n */\n async uploadParts(args: UploadPartsArgs): Promise<number> {\n const { metadata, readStream, offset: initialOffset } = args\n let { currentPartNumber } = args\n let offset = initialOffset\n const size = metadata.file.size\n const promises: Promise<void>[] = []\n let pendingChunkFilepath: null | string = null\n let bytesUploaded = 0\n let permit: SemaphorePermit | undefined = undefined\n\n const splitterStream = new StreamSplitter({\n chunkSize: this.fileOperations.calculateOptimalPartSize({ size }),\n directory: os.tmpdir(),\n })\n .on('beforeChunkStarted', async () => {\n permit = await this.partUploadSemaphore.acquire()\n })\n .on('chunkStarted', (filepath) => {\n pendingChunkFilepath = filepath\n })\n .on('chunkFinished', ({ path, size: partSize }) => {\n pendingChunkFilepath = null\n\n const acquiredPermit = permit\n const partNumber = currentPartNumber++\n\n offset += partSize\n\n const isFinalPart = size === offset\n\n const uploadChunk = async () => {\n try {\n // Only the first chunk of each PATCH request can prepend\n // an incomplete part (last chunk) from the previous request.\n const readable = fs.createReadStream(path)\n readable.on('error', function (error) {\n throw error\n })\n\n switch (true) {\n case partSize >= this.minPartSize || isFinalPart:\n await this.uploadPart({\n metadata,\n readStream: readable,\n partNumber,\n })\n break\n default:\n await this.uploadIncompletePart({\n id: metadata.file.id,\n readStream: readable,\n })\n break\n }\n\n bytesUploaded += partSize\n } catch (error) {\n // Destroy the splitter to stop processing more chunks\n const mappedError =\n error instanceof Error ? error : new Error(String(error))\n splitterStream.destroy(mappedError)\n throw mappedError\n } finally {\n fs.promises.rm(path).catch(function () {})\n acquiredPermit?.()\n }\n }\n\n const deferred = uploadChunk()\n\n promises.push(deferred)\n })\n .on('chunkError', () => {\n permit?.()\n })\n\n try {\n await stream.promises.pipeline(readStream, splitterStream)\n } catch (error) {\n if (pendingChunkFilepath !== null) {\n try {\n await fs.promises.rm(pendingChunkFilepath)\n } catch {\n log(MediaCloudLogs.S3_STORE_CHUNK_REMOVAL_FAILED)\n }\n }\n const mappedError =\n error instanceof Error ? error : new Error(String(error))\n promises.push(Promise.reject(mappedError))\n } finally {\n // Wait for all promises\n await Promise.allSettled(promises)\n // Reject the promise if any of the promises reject\n await Promise.all(promises)\n }\n\n return bytesUploaded\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Semaphore } from "./semaphore.mjs";
|
|
2
|
-
import { S3ExpirationManager } from "./expiration-manager.mjs";
|
|
3
|
-
import { S3FileOperations } from "./file-operations.mjs";
|
|
4
2
|
import { S3StoreConfig } from "../../../types/index.mjs";
|
|
5
|
-
import { S3MetadataManager } from "./
|
|
6
|
-
import {
|
|
3
|
+
import { S3MetadataManager } from "./metadataManager.mjs";
|
|
4
|
+
import { S3ExpirationManager } from "./expirationManager.mjs";
|
|
5
|
+
import { S3FileOperations } from "./fileOperations.mjs";
|
|
6
|
+
import { S3PartsManager } from "./partsManager.mjs";
|
|
7
7
|
import stream, { Readable } from "node:stream";
|
|
8
8
|
import { S3 } from "@aws-sdk/client-s3";
|
|
9
9
|
import { DataStore, Upload } from "@tus/utils";
|
|
10
10
|
|
|
11
|
-
//#region src/tus/stores/s3/
|
|
11
|
+
//#region src/tus/stores/s3/s3Store.d.ts
|
|
12
12
|
declare class S3Store extends DataStore {
|
|
13
13
|
client: S3;
|
|
14
14
|
bucket: string;
|
|
@@ -26,19 +26,6 @@ declare class S3Store extends DataStore {
|
|
|
26
26
|
protected expirationManager: S3ExpirationManager;
|
|
27
27
|
protected customEndpoint: string;
|
|
28
28
|
constructor(options: S3StoreConfig);
|
|
29
|
-
/**
|
|
30
|
-
* Generate the key name for the info file
|
|
31
|
-
* @param id - The upload ID
|
|
32
|
-
* @returns The info file key
|
|
33
|
-
*/
|
|
34
|
-
protected generateInfoKey(id: string): string;
|
|
35
|
-
/**
|
|
36
|
-
* Generate the key name for a part file
|
|
37
|
-
* @param id - The upload ID
|
|
38
|
-
* @param isIncompletePart - Whether this is an incomplete part (default: false)
|
|
39
|
-
* @returns The part file key
|
|
40
|
-
*/
|
|
41
|
-
protected generatePartKey(id: string, isIncompletePart?: boolean): string;
|
|
42
29
|
/**
|
|
43
30
|
* Helper method to check if expiration tags should be used
|
|
44
31
|
* @returns True if expiration tags should be used
|
|
@@ -47,7 +34,7 @@ declare class S3Store extends DataStore {
|
|
|
47
34
|
/**
|
|
48
35
|
* Generates a tag for marking complete/incomplete uploads
|
|
49
36
|
* @param value - Either 'false' or 'true' to mark completion status
|
|
50
|
-
* @returns The tag string or undefined if tags shouldn
|
|
37
|
+
* @returns The tag string or undefined if tags shouldn’t be used
|
|
51
38
|
*/
|
|
52
39
|
protected generateCompleteTag(value: 'false' | 'true'): string | undefined;
|
|
53
40
|
/**
|
|
@@ -55,55 +42,74 @@ declare class S3Store extends DataStore {
|
|
|
55
42
|
* Also, a `${file_id}.info` file is created which holds some information
|
|
56
43
|
* about the upload itself like: `upload-id`, `upload-length`, etc.
|
|
57
44
|
* @param upload - The upload object to create
|
|
58
|
-
* @
|
|
45
|
+
* @return Promise that resolves to the created upload object with storage information
|
|
59
46
|
*/
|
|
60
47
|
create(upload: Upload): Promise<Upload>;
|
|
61
48
|
/**
|
|
62
|
-
*
|
|
63
|
-
* @param file_id - The file ID
|
|
64
|
-
* @param upload_length - The length of the upload
|
|
65
|
-
* @returns Promise that resolves when length is declared
|
|
66
|
-
*/
|
|
67
|
-
declareUploadLength(file_id: string, upload_length: number): Promise<void>;
|
|
68
|
-
/**
|
|
69
|
-
* Writes `buffer` to the file specified by the upload's `id` at `offset`
|
|
49
|
+
* Writes `buffer` to the file specified by the upload’s `id` at `offset`
|
|
70
50
|
* @param readable - The readable stream to write
|
|
71
51
|
* @param id - The upload ID
|
|
72
52
|
* @param offset - The byte offset to write at
|
|
73
|
-
* @
|
|
53
|
+
* @return Promise that resolves to the new offset after writing the data
|
|
74
54
|
*/
|
|
75
55
|
write(readable: stream.Readable, id: string, offset: number): Promise<number>;
|
|
76
56
|
/**
|
|
77
57
|
* Returns the current state of the upload, i.e how much data has been
|
|
78
58
|
* uploaded and if the upload is complete.
|
|
59
|
+
* @param id - The upload ID to retrieve
|
|
60
|
+
* @returns Promise that resolves to the upload object with current offset and storage information
|
|
79
61
|
*/
|
|
80
62
|
getUpload(id: string): Promise<Upload>;
|
|
81
63
|
/**
|
|
82
|
-
* Reads the file specified by the upload
|
|
64
|
+
* Reads the file specified by the upload’s `id` and returns a readable stream
|
|
65
|
+
* @param id - The upload ID to read
|
|
66
|
+
* @returns Promise that resolves to a readable stream of the file's contents
|
|
83
67
|
*/
|
|
84
68
|
read(id: string): Promise<Readable>;
|
|
85
69
|
/**
|
|
86
|
-
*
|
|
70
|
+
*
|
|
71
|
+
* Moves the file specified by its `oldKey` to `newKey`.
|
|
72
|
+
* @param oldKey - The current S3 key of the file to be moved
|
|
73
|
+
* @param newKey - The new S3 key to move the file to
|
|
74
|
+
* @return Promise that resolves when the file has been successfully moved, or rejects with an error if the move operation fails
|
|
75
|
+
*/
|
|
76
|
+
copy(oldKey: string, newKey: string): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Removes files specified by the upload’s `id`
|
|
79
|
+
* @param id - The upload ID to remove
|
|
80
|
+
* @returns Promise that resolves when the file and its metadata have been removed
|
|
87
81
|
*/
|
|
88
82
|
remove(id: string): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Removes the .info file specified by the upload’s `id`
|
|
85
|
+
* @param id - The upload ID to clean up
|
|
86
|
+
* @returns Promise that resolves when the metadata file has been removed
|
|
87
|
+
*/
|
|
88
|
+
cleanup(id: string): Promise<void>;
|
|
89
89
|
/**
|
|
90
90
|
* Combine all multipart uploads into a single object
|
|
91
|
+
* @param id - The upload ID to complete
|
|
92
|
+
* @returns Promise that resolves to the completed upload object with storage information
|
|
91
93
|
*/
|
|
92
94
|
completeMultipartUpload(id: string): Promise<Upload>;
|
|
93
95
|
/**
|
|
94
96
|
* Get the full S3 URL for an uploaded file
|
|
97
|
+
* @param id - The upload ID to get the URL for
|
|
98
|
+
* @returns The full URL to access the uploaded file on S3
|
|
95
99
|
*/
|
|
96
100
|
getUrl(id: string): string;
|
|
97
101
|
/**
|
|
98
102
|
* Deletes expired incomplete uploads.
|
|
99
103
|
* Returns the number of deleted uploads.
|
|
104
|
+
* @returns Promise that resolves to the number of deleted uploads
|
|
100
105
|
*/
|
|
101
106
|
deleteExpired(): Promise<number>;
|
|
102
107
|
/**
|
|
103
108
|
* Returns the expiration period in milliseconds
|
|
109
|
+
* @return The expiration period in milliseconds
|
|
104
110
|
*/
|
|
105
111
|
getExpiration(): number;
|
|
106
112
|
}
|
|
107
113
|
//#endregion
|
|
108
114
|
export { S3Store };
|
|
109
|
-
//# sourceMappingURL=
|
|
115
|
+
//# sourceMappingURL=s3Store.d.mts.map
|