vintasend-aws-s3-attachments 0.4.3 → 0.4.17
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.
|
@@ -58,6 +58,19 @@ export declare class S3AttachmentManager extends BaseAttachmentManager {
|
|
|
58
58
|
* @returns AttachmentFileRecord with S3 storage metadata
|
|
59
59
|
*/
|
|
60
60
|
uploadFile(file: FileAttachment, filename: string, contentType?: string): Promise<AttachmentFileRecord>;
|
|
61
|
+
/**
|
|
62
|
+
* Get file metadata by ID.
|
|
63
|
+
*
|
|
64
|
+
* For S3, this requires fetching the object metadata from S3.
|
|
65
|
+
* The fileId is used to reconstruct the S3 key.
|
|
66
|
+
*
|
|
67
|
+
* Note: This implementation retrieves basic metadata from S3.
|
|
68
|
+
* The checksum is retrieved from object metadata if available.
|
|
69
|
+
*
|
|
70
|
+
* @param fileId - The unique identifier of the file
|
|
71
|
+
* @returns The file metadata or null if not found
|
|
72
|
+
*/
|
|
73
|
+
getFile(fileId: string): Promise<AttachmentFileRecord | null>;
|
|
61
74
|
/**
|
|
62
75
|
* Reconstruct an AttachmentFile accessor from storage metadata.
|
|
63
76
|
*
|
|
@@ -98,3 +111,4 @@ export declare class S3AttachmentManager extends BaseAttachmentManager {
|
|
|
98
111
|
*/
|
|
99
112
|
private buildS3Key;
|
|
100
113
|
}
|
|
114
|
+
//# sourceMappingURL=aws-s3-attachment-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-s3-attachment-manager.d.ts","sourceRoot":"","sources":["../src/aws-s3-attachment-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAC;AAC3G,OAAO,KAAK,EACX,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,MAAM,iCAAiC,CAAC;AAWzC,MAAM,WAAW,yBAAyB;IACzC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,WAAW,CAAC,EAAE;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB;IAC7D,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,yBAAyB;IAc7C;;;;;;;OAOG;IACG,UAAU,CACf,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,CAAC;IA6ChC;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAyDnE;;;;;;;;OAQG;IACH,yBAAyB,CACxB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC,cAAc;IAcjB;;;;;;;;;;OAUG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/C;;;;;;;OAOG;IACG,sBAAsB,CAC3B,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC;IAehB;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;CAMlB"}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
|
|
7
|
-
const node_stream_1 = require("node:stream");
|
|
8
|
-
const node_crypto_1 = require("node:crypto");
|
|
1
|
+
import { BaseAttachmentManager } from 'vintasend/dist/services/attachment-manager/base-attachment-manager';
|
|
2
|
+
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, } from '@aws-sdk/client-s3';
|
|
3
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
4
|
+
import { Readable } from 'node:stream';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
9
6
|
/**
|
|
10
7
|
* AWS S3 AttachmentManager implementation for VintaSend.
|
|
11
8
|
*
|
|
@@ -18,12 +15,12 @@ const node_crypto_1 = require("node:crypto");
|
|
|
18
15
|
* is stored by the Backend in its database. The Backend calls this manager's
|
|
19
16
|
* uploadFile() method and stores the returned metadata.
|
|
20
17
|
*/
|
|
21
|
-
class S3AttachmentManager extends
|
|
18
|
+
export class S3AttachmentManager extends BaseAttachmentManager {
|
|
22
19
|
constructor(config) {
|
|
23
20
|
super();
|
|
24
21
|
this.bucket = config.bucket;
|
|
25
22
|
this.keyPrefix = config.keyPrefix || '';
|
|
26
|
-
this.s3Client = new
|
|
23
|
+
this.s3Client = new S3Client({
|
|
27
24
|
region: config.region,
|
|
28
25
|
credentials: config.credentials,
|
|
29
26
|
endpoint: config.endpoint,
|
|
@@ -46,10 +43,10 @@ class S3AttachmentManager extends base_attachment_manager_1.BaseAttachmentManage
|
|
|
46
43
|
// Detect content type if not provided
|
|
47
44
|
const finalContentType = contentType || this.detectContentType(filename);
|
|
48
45
|
// Generate unique S3 key
|
|
49
|
-
const fileId =
|
|
46
|
+
const fileId = randomUUID();
|
|
50
47
|
const key = this.buildS3Key(fileId, filename);
|
|
51
48
|
// Upload to S3
|
|
52
|
-
const putCommand = new
|
|
49
|
+
const putCommand = new PutObjectCommand({
|
|
53
50
|
Bucket: this.bucket,
|
|
54
51
|
Key: key,
|
|
55
52
|
Body: buffer,
|
|
@@ -76,6 +73,66 @@ class S3AttachmentManager extends base_attachment_manager_1.BaseAttachmentManage
|
|
|
76
73
|
updatedAt: new Date(),
|
|
77
74
|
};
|
|
78
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Get file metadata by ID.
|
|
78
|
+
*
|
|
79
|
+
* For S3, this requires fetching the object metadata from S3.
|
|
80
|
+
* The fileId is used to reconstruct the S3 key.
|
|
81
|
+
*
|
|
82
|
+
* Note: This implementation retrieves basic metadata from S3.
|
|
83
|
+
* The checksum is retrieved from object metadata if available.
|
|
84
|
+
*
|
|
85
|
+
* @param fileId - The unique identifier of the file
|
|
86
|
+
* @returns The file metadata or null if not found
|
|
87
|
+
*/
|
|
88
|
+
async getFile(fileId) {
|
|
89
|
+
try {
|
|
90
|
+
// For S3, we need to list objects with the fileId prefix to find the actual key
|
|
91
|
+
const prefix = `${this.keyPrefix}${fileId}/`;
|
|
92
|
+
// Import HeadObjectCommand for metadata retrieval
|
|
93
|
+
const { HeadObjectCommand } = await import('@aws-sdk/client-s3');
|
|
94
|
+
const { ListObjectsV2Command } = await import('@aws-sdk/client-s3');
|
|
95
|
+
// List objects with the fileId prefix
|
|
96
|
+
const listCommand = new ListObjectsV2Command({
|
|
97
|
+
Bucket: this.bucket,
|
|
98
|
+
Prefix: prefix,
|
|
99
|
+
MaxKeys: 1,
|
|
100
|
+
});
|
|
101
|
+
const listResponse = await this.s3Client.send(listCommand);
|
|
102
|
+
if (!listResponse.Contents || listResponse.Contents.length === 0) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const key = listResponse.Contents[0].Key;
|
|
106
|
+
if (!key) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
// Get detailed metadata
|
|
110
|
+
const headCommand = new HeadObjectCommand({
|
|
111
|
+
Bucket: this.bucket,
|
|
112
|
+
Key: key,
|
|
113
|
+
});
|
|
114
|
+
const headResponse = await this.s3Client.send(headCommand);
|
|
115
|
+
const filename = headResponse.Metadata?.originalFilename || key.split('/').pop() || 'unknown';
|
|
116
|
+
const checksum = headResponse.Metadata?.checksum || '';
|
|
117
|
+
return {
|
|
118
|
+
id: fileId,
|
|
119
|
+
filename,
|
|
120
|
+
contentType: headResponse.ContentType || 'application/octet-stream',
|
|
121
|
+
size: headResponse.ContentLength || 0,
|
|
122
|
+
checksum,
|
|
123
|
+
storageMetadata: {
|
|
124
|
+
bucket: this.bucket,
|
|
125
|
+
key,
|
|
126
|
+
region: this.s3Client.config.region,
|
|
127
|
+
},
|
|
128
|
+
createdAt: headResponse.LastModified || new Date(),
|
|
129
|
+
updatedAt: headResponse.LastModified || new Date(),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
79
136
|
/**
|
|
80
137
|
* Reconstruct an AttachmentFile accessor from storage metadata.
|
|
81
138
|
*
|
|
@@ -117,7 +174,7 @@ class S3AttachmentManager extends base_attachment_manager_1.BaseAttachmentManage
|
|
|
117
174
|
if (!storageMetadata.key) {
|
|
118
175
|
throw new Error('Storage metadata must contain key for S3 file deletion');
|
|
119
176
|
}
|
|
120
|
-
const deleteCommand = new
|
|
177
|
+
const deleteCommand = new DeleteObjectCommand({
|
|
121
178
|
Bucket: this.bucket,
|
|
122
179
|
Key: storageMetadata.key,
|
|
123
180
|
});
|
|
@@ -137,7 +194,6 @@ class S3AttachmentManager extends base_attachment_manager_1.BaseAttachmentManage
|
|
|
137
194
|
return `${this.keyPrefix}${fileId}/${sanitizedFilename}`;
|
|
138
195
|
}
|
|
139
196
|
}
|
|
140
|
-
exports.S3AttachmentManager = S3AttachmentManager;
|
|
141
197
|
/**
|
|
142
198
|
* S3 AttachmentFile implementation.
|
|
143
199
|
*
|
|
@@ -158,7 +214,7 @@ class S3AttachmentFile {
|
|
|
158
214
|
* Use this for small files. For large files, prefer streaming.
|
|
159
215
|
*/
|
|
160
216
|
async read() {
|
|
161
|
-
const getCommand = new
|
|
217
|
+
const getCommand = new GetObjectCommand({
|
|
162
218
|
Bucket: this.bucket,
|
|
163
219
|
Key: this.key,
|
|
164
220
|
});
|
|
@@ -175,7 +231,7 @@ class S3AttachmentFile {
|
|
|
175
231
|
* Use this for large files to avoid loading everything into memory.
|
|
176
232
|
*/
|
|
177
233
|
async stream() {
|
|
178
|
-
const getCommand = new
|
|
234
|
+
const getCommand = new GetObjectCommand({
|
|
179
235
|
Bucket: this.bucket,
|
|
180
236
|
Key: this.key,
|
|
181
237
|
});
|
|
@@ -185,7 +241,7 @@ class S3AttachmentFile {
|
|
|
185
241
|
}
|
|
186
242
|
// Convert Node.js Readable to Web ReadableStream
|
|
187
243
|
const nodeStream = response.Body;
|
|
188
|
-
return
|
|
244
|
+
return Readable.toWeb(nodeStream);
|
|
189
245
|
}
|
|
190
246
|
/**
|
|
191
247
|
* Generate a presigned URL for accessing the file.
|
|
@@ -194,11 +250,11 @@ class S3AttachmentFile {
|
|
|
194
250
|
* @returns A presigned URL for secure file access
|
|
195
251
|
*/
|
|
196
252
|
async url(expiresIn = 3600) {
|
|
197
|
-
const getCommand = new
|
|
253
|
+
const getCommand = new GetObjectCommand({
|
|
198
254
|
Bucket: this.bucket,
|
|
199
255
|
Key: this.key,
|
|
200
256
|
});
|
|
201
|
-
return
|
|
257
|
+
return getSignedUrl(this.s3Client, getCommand, { expiresIn });
|
|
202
258
|
}
|
|
203
259
|
/**
|
|
204
260
|
* Delete this file from S3.
|
|
@@ -206,7 +262,7 @@ class S3AttachmentFile {
|
|
|
206
262
|
* Note: This is typically called by the AttachmentManager, not directly.
|
|
207
263
|
*/
|
|
208
264
|
async delete() {
|
|
209
|
-
const deleteCommand = new
|
|
265
|
+
const deleteCommand = new DeleteObjectCommand({
|
|
210
266
|
Bucket: this.bucket,
|
|
211
267
|
Key: this.key,
|
|
212
268
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,mBAAmB,EACnB,KAAK,yBAAyB,GAC9B,MAAM,6BAA6B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.S3AttachmentManager = void 0;
|
|
4
|
-
var aws_s3_attachment_manager_1 = require("./aws-s3-attachment-manager");
|
|
5
|
-
Object.defineProperty(exports, "S3AttachmentManager", { enumerable: true, get: function () { return aws_s3_attachment_manager_1.S3AttachmentManager; } });
|
|
1
|
+
export { S3AttachmentManager, } from './aws-s3-attachment-manager';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vintasend-aws-s3-attachments",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.17",
|
|
4
4
|
"description": "Implementation of AWS S3 Attachments for VintaSend",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
"author": "Hugo Bessa",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@aws-sdk/client-s3": "^3.
|
|
20
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
21
|
-
"vintasend": "^0.4.
|
|
19
|
+
"@aws-sdk/client-s3": "^3.981.0",
|
|
20
|
+
"@aws-sdk/s3-request-presigner": "^3.981.0",
|
|
21
|
+
"vintasend": "^0.4.17"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@types/jest": "^
|
|
25
|
-
"jest": "^
|
|
26
|
-
"ts-jest": "^29.
|
|
27
|
-
"typescript": "^5.
|
|
24
|
+
"@types/jest": "^30.0.0",
|
|
25
|
+
"jest": "^30.2.0",
|
|
26
|
+
"ts-jest": "^29.4.6",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
28
|
}
|
|
29
29
|
}
|