@onivoro/server-aws-firehose 24.33.9

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Onivoro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,292 @@
1
+ # @onivoro/server-aws-firehose
2
+
3
+ AWS S3 integration for NestJS applications with file upload/download capabilities.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @onivoro/server-aws-firehose
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ This library provides AWS S3 integration for NestJS applications, offering file upload, download, deletion, and pre-signed URL generation.
14
+
15
+ ## Module Setup
16
+
17
+ ```typescript
18
+ import { Module } from '@nestjs/common';
19
+ import { ServerAwsS3Module } from '@onivoro/server-aws-firehose';
20
+
21
+ @Module({
22
+ imports: [
23
+ ServerAwsS3Module.configure()
24
+ ]
25
+ })
26
+ export class AppModule {}
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ The module uses environment-based configuration:
32
+
33
+ ```typescript
34
+ export class ServerAwsS3Config {
35
+ AWS_REGION: string;
36
+ AWS_PROFILE?: string; // Optional AWS profile
37
+ AWS_S3_BUCKET: string; // Default bucket name
38
+ AWS_S3_PREFIX?: string; // Optional prefix for all keys
39
+ }
40
+ ```
41
+
42
+ ## Service
43
+
44
+ ### S3Service
45
+
46
+ The service provides file operations:
47
+
48
+ ```typescript
49
+ import { Injectable } from '@nestjs/common';
50
+ import { S3Service } from '@onivoro/server-aws-firehose';
51
+
52
+ @Injectable()
53
+ export class FileStorageService {
54
+ constructor(private readonly s3Service: S3Service) {}
55
+
56
+ // Upload a file
57
+ async uploadFile(key: string, body: Buffer | string, contentType: string) {
58
+ const result = await this.s3Service.upload(key, body, contentType);
59
+ return result;
60
+ }
61
+
62
+ // Upload a public file
63
+ async uploadPublicFile(key: string, body: Buffer | string, contentType: string) {
64
+ const result = await this.s3Service.uploadPublic(key, body, contentType);
65
+ return result;
66
+ }
67
+
68
+ // Download a file
69
+ async downloadFile(key: string) {
70
+ const fileContent = await this.s3Service.download(key);
71
+ return fileContent;
72
+ }
73
+
74
+ // Get a pre-signed download URL
75
+ async getDownloadUrl(key: string, expiresIn: number = 3600) {
76
+ const url = await this.s3Service.getPresignedDownloadUrl(key, expiresIn);
77
+ return url;
78
+ }
79
+
80
+ // Delete a single file
81
+ async deleteFile(key: string) {
82
+ await this.s3Service.delete(key);
83
+ }
84
+
85
+ // Delete files by prefix
86
+ async deleteFilesByPrefix(prefix: string) {
87
+ await this.s3Service.deleteByPrefix(prefix);
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## Available Methods
93
+
94
+ ### Upload Operations
95
+ - **upload(key: string, body: Buffer | string, contentType: string, bucket?: string)** - Upload a private file
96
+ - **uploadPublic(key: string, body: Buffer | string, contentType: string, bucket?: string)** - Upload a publicly accessible file
97
+
98
+ ### Download Operations
99
+ - **download(key: string, bucket?: string)** - Download file content
100
+ - **getPresignedDownloadUrl(key: string, expiresInSeconds?: number, bucket?: string)** - Generate pre-signed download URL
101
+
102
+ ### Delete Operations
103
+ - **delete(key: string, bucket?: string)** - Delete a single object
104
+ - **deleteByPrefix(prefix: string, bucket?: string)** - Delete all objects with a specific prefix
105
+
106
+ ## Direct Client Access
107
+
108
+ The service exposes the underlying S3 client for advanced operations:
109
+
110
+ ```typescript
111
+ import {
112
+ ListObjectsV2Command,
113
+ CopyObjectCommand,
114
+ HeadObjectCommand,
115
+ GetObjectTaggingCommand
116
+ } from '@aws-sdk/client-firehose';
117
+
118
+ @Injectable()
119
+ export class AdvancedS3Service {
120
+ constructor(private readonly s3Service: S3Service) {}
121
+
122
+ // List objects in bucket
123
+ async listObjects(prefix?: string) {
124
+ const command = new ListObjectsV2Command({
125
+ Bucket: process.env.AWS_S3_BUCKET,
126
+ Prefix: prefix
127
+ });
128
+
129
+ return await this.s3Service.s3Client.send(command);
130
+ }
131
+
132
+ // Copy object
133
+ async copyObject(sourceKey: string, destinationKey: string) {
134
+ const command = new CopyObjectCommand({
135
+ Bucket: process.env.AWS_S3_BUCKET,
136
+ CopySource: `${process.env.AWS_S3_BUCKET}/${sourceKey}`,
137
+ Key: destinationKey
138
+ });
139
+
140
+ return await this.s3Service.s3Client.send(command);
141
+ }
142
+
143
+ // Get object metadata
144
+ async getObjectMetadata(key: string) {
145
+ const command = new HeadObjectCommand({
146
+ Bucket: process.env.AWS_S3_BUCKET,
147
+ Key: key
148
+ });
149
+
150
+ return await this.s3Service.s3Client.send(command);
151
+ }
152
+ }
153
+ ```
154
+
155
+ ## Complete Example
156
+
157
+ ```typescript
158
+ import { Module, Injectable, Controller, Post, Get, Delete, Param, UploadedFile, UseInterceptors } from '@nestjs/common';
159
+ import { FileInterceptor } from '@nestjs/platform-express';
160
+ import { ServerAwsS3Module, S3Service } from '@onivoro/server-aws-firehose';
161
+
162
+ @Module({
163
+ imports: [ServerAwsS3Module.configure()],
164
+ controllers: [DocumentController],
165
+ providers: [DocumentService]
166
+ })
167
+ export class DocumentModule {}
168
+
169
+ @Injectable()
170
+ export class DocumentService {
171
+ constructor(private readonly s3Service: S3Service) {}
172
+
173
+ async uploadDocument(userId: string, file: Express.Multer.File) {
174
+ const key = `documents/${userId}/${Date.now()}-${file.originalname}`;
175
+
176
+ try {
177
+ // Upload to S3
178
+ const result = await this.s3Service.upload(
179
+ key,
180
+ file.buffer,
181
+ file.mimetype
182
+ );
183
+
184
+ return {
185
+ key,
186
+ location: result.Location,
187
+ etag: result.ETag
188
+ };
189
+ } catch (error) {
190
+ console.error('Upload failed:', error);
191
+ throw error;
192
+ }
193
+ }
194
+
195
+ async getDocumentUrl(key: string) {
196
+ // Generate URL valid for 1 hour
197
+ const url = await this.s3Service.getPresignedDownloadUrl(key, 3600);
198
+ return { url };
199
+ }
200
+
201
+ async deleteUserDocuments(userId: string) {
202
+ const prefix = `documents/${userId}/`;
203
+ await this.s3Service.deleteByPrefix(prefix);
204
+ }
205
+
206
+ async uploadPublicAvatar(userId: string, imageBuffer: Buffer) {
207
+ const key = `avatars/${userId}.jpg`;
208
+
209
+ const result = await this.s3Service.uploadPublic(
210
+ key,
211
+ imageBuffer,
212
+ 'image/jpeg'
213
+ );
214
+
215
+ return {
216
+ key,
217
+ publicUrl: result.Location
218
+ };
219
+ }
220
+ }
221
+
222
+ @Controller('documents')
223
+ export class DocumentController {
224
+ constructor(private readonly documentService: DocumentService) {}
225
+
226
+ @Post('upload')
227
+ @UseInterceptors(FileInterceptor('file'))
228
+ async uploadDocument(
229
+ @UploadedFile() file: Express.Multer.File
230
+ ) {
231
+ const userId = 'user123'; // Get from auth context
232
+ return await this.documentService.uploadDocument(userId, file);
233
+ }
234
+
235
+ @Get(':key/url')
236
+ async getDocumentUrl(@Param('key') key: string) {
237
+ return await this.documentService.getDocumentUrl(key);
238
+ }
239
+
240
+ @Delete('user/:userId')
241
+ async deleteUserDocuments(@Param('userId') userId: string) {
242
+ await this.documentService.deleteUserDocuments(userId);
243
+ return { message: 'Documents deleted successfully' };
244
+ }
245
+ }
246
+ ```
247
+
248
+ ## Environment Variables
249
+
250
+ ```bash
251
+ # Required
252
+ AWS_REGION=us-east-1
253
+ AWS_S3_BUCKET=my-bucket-name
254
+
255
+ # Optional
256
+ AWS_PROFILE=my-profile
257
+ AWS_S3_PREFIX=my-app/ # Prefix for all keys
258
+ ```
259
+
260
+ ## Error Handling
261
+
262
+ ```typescript
263
+ try {
264
+ await s3Service.download('non-existent-key');
265
+ } catch (error) {
266
+ if (error.name === 'NoSuchKey') {
267
+ console.error('File not found');
268
+ } else if (error.name === 'AccessDenied') {
269
+ console.error('Permission denied');
270
+ }
271
+ }
272
+ ```
273
+
274
+ ## Limitations
275
+
276
+ - No multipart upload support (large files must be handled manually)
277
+ - No built-in bucket management operations
278
+ - No object tagging or versioning support
279
+ - Limited to basic CRUD operations
280
+ - For advanced features, use the exposed `s3Client` directly
281
+
282
+ ## Best Practices
283
+
284
+ 1. **Key Naming**: Use a consistent key naming strategy (e.g., `type/userId/timestamp-filename`)
285
+ 2. **Content Types**: Always specify correct content types for proper browser handling
286
+ 3. **Security**: Use pre-signed URLs for temporary access instead of public uploads when possible
287
+ 4. **Cleanup**: Implement lifecycle policies for automatic object expiration
288
+ 5. **Error Handling**: Always handle S3 errors appropriately
289
+
290
+ ## License
291
+
292
+ MIT
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@onivoro/server-aws-firehose",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "24.33.9",
7
+ "license": "MIT",
8
+ "type": "commonjs",
9
+ "main": "./src/index.js",
10
+ "types": "./src/index.d.ts",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/onivoro/monorepo.git"
14
+ },
15
+ "dependencies": {
16
+ "@onivoro/server-aws-credential-providers": "24.33.9",
17
+ "@onivoro/server-common": "24.33.9",
18
+ "tslib": "^2.3.0"
19
+ },
20
+ "peerDependencies": {
21
+ "@aws-sdk/client-firehose": "*",
22
+ "@aws-sdk/s3-request-presigner": "*",
23
+ "@nestjs/common": "*"
24
+ },
25
+ "files": [
26
+ "**/*.js",
27
+ "**/*.d.ts",
28
+ "**/*.js.map",
29
+ "README.md"
30
+ ]
31
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './lib/server-aws-firehose-config.class';
2
+ export * from './lib/services/firehose.service';
3
+ export * from './lib/server-aws-firehose.module';
4
+ //# sourceMappingURL=index.d.ts.map
package/src/index.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/server-aws-firehose-config.class"), exports);
5
+ tslib_1.__exportStar(require("./lib/services/firehose.service"), exports);
6
+ tslib_1.__exportStar(require("./lib/server-aws-firehose.module"), exports);
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/server/aws-firehose/src/index.ts"],"names":[],"mappings":";;;AAAA,iFAAuD;AAEvD,0EAAgD;AAEhD,2EAAiD"}
@@ -0,0 +1,6 @@
1
+ export declare class ServerAwsFirehoseConfig {
2
+ AWS_FIREHOSE_NAME: string;
3
+ AWS_PROFILE?: string;
4
+ AWS_REGION: string;
5
+ }
6
+ //# sourceMappingURL=server-aws-firehose-config.class.d.ts.map
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServerAwsFirehoseConfig = void 0;
4
+ class ServerAwsFirehoseConfig {
5
+ AWS_FIREHOSE_NAME;
6
+ AWS_PROFILE;
7
+ AWS_REGION;
8
+ }
9
+ exports.ServerAwsFirehoseConfig = ServerAwsFirehoseConfig;
10
+ //# sourceMappingURL=server-aws-firehose-config.class.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-aws-firehose-config.class.js","sourceRoot":"","sources":["../../../../../../libs/server/aws-firehose/src/lib/server-aws-firehose-config.class.ts"],"names":[],"mappings":";;;AAAA,MAAa,uBAAuB;IAChC,iBAAiB,CAAS;IAC1B,WAAW,CAAU;IACrB,UAAU,CAAS;CACtB;AAJD,0DAIC"}
@@ -0,0 +1,5 @@
1
+ import { ServerAwsFirehoseConfig } from './server-aws-firehose-config.class';
2
+ export declare class ServerAwsFirehoseModule {
3
+ static configure(config: ServerAwsFirehoseConfig): any;
4
+ }
5
+ //# sourceMappingURL=server-aws-firehose.module.d.ts.map
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var ServerAwsFirehoseModule_1;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ServerAwsFirehoseModule = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const common_1 = require("@nestjs/common");
7
+ const client_firehose_1 = require("@aws-sdk/client-firehose");
8
+ const server_common_1 = require("@onivoro/server-common");
9
+ const server_aws_firehose_config_class_1 = require("./server-aws-firehose-config.class");
10
+ const server_aws_credential_providers_1 = require("@onivoro/server-aws-credential-providers");
11
+ const firehose_service_1 = require("./services/firehose.service");
12
+ let firehoseClient = null;
13
+ let ServerAwsFirehoseModule = ServerAwsFirehoseModule_1 = class ServerAwsFirehoseModule {
14
+ static configure(config) {
15
+ return (0, server_common_1.moduleFactory)({
16
+ imports: [server_aws_credential_providers_1.ServerAwsCredentialProvidersModule.configure(config)],
17
+ module: ServerAwsFirehoseModule_1,
18
+ providers: [
19
+ firehose_service_1.FirehoseService,
20
+ { provide: server_aws_firehose_config_class_1.ServerAwsFirehoseConfig, useValue: config },
21
+ {
22
+ provide: client_firehose_1.FirehoseClient,
23
+ useFactory: (credentials) => firehoseClient ||
24
+ (firehoseClient = new client_firehose_1.FirehoseClient({
25
+ region: config.AWS_REGION,
26
+ credentials
27
+ })),
28
+ inject: [server_aws_credential_providers_1.AwsCredentials]
29
+ },
30
+ ]
31
+ });
32
+ }
33
+ };
34
+ exports.ServerAwsFirehoseModule = ServerAwsFirehoseModule;
35
+ exports.ServerAwsFirehoseModule = ServerAwsFirehoseModule = ServerAwsFirehoseModule_1 = tslib_1.__decorate([
36
+ (0, common_1.Module)({})
37
+ ], ServerAwsFirehoseModule);
38
+ //# sourceMappingURL=server-aws-firehose.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-aws-firehose.module.js","sourceRoot":"","sources":["../../../../../../libs/server/aws-firehose/src/lib/server-aws-firehose.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAAwC;AACxC,8DAA0D;AAC1D,0DAAuD;AACvD,yFAA6E;AAC7E,8FAA8G;AAC9G,kEAA8D;AAE9D,IAAI,cAAc,GAA0B,IAAI,CAAC;AAG1C,IAAM,uBAAuB,+BAA7B,MAAM,uBAAuB;IAClC,MAAM,CAAC,SAAS,CAAC,MAA+B;QAC9C,OAAO,IAAA,6BAAa,EAAC;YACnB,OAAO,EAAE,CAAC,oEAAkC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAC/D,MAAM,EAAE,yBAAuB;YAC/B,SAAS,EAAE;gBACT,kCAAe;gBACf,EAAE,OAAO,EAAE,0DAAuB,EAAE,QAAQ,EAAE,MAAM,EAAE;gBACtD;oBACE,OAAO,EAAE,gCAAc;oBACvB,UAAU,EAAE,CAAC,WAA2B,EAAE,EAAE,CAC1C,cAAc;wBACd,CAAC,cAAc,GAAG,IAAI,gCAAc,CAAC;4BACnC,MAAM,EAAE,MAAM,CAAC,UAAU;4BACzB,WAAW;yBACZ,CAAC,CAAC;oBACL,MAAM,EAAE,CAAC,gDAAc,CAAC;iBACzB;aACF;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AArBY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,uBAAuB,CAqBnC"}
@@ -0,0 +1,9 @@
1
+ import { FirehoseClient } from '@aws-sdk/client-firehose';
2
+ import { ServerAwsFirehoseConfig } from '../server-aws-firehose-config.class';
3
+ export declare class FirehoseService {
4
+ private config;
5
+ private firehoseClient;
6
+ constructor(config: ServerAwsFirehoseConfig, firehoseClient: FirehoseClient);
7
+ putRecord(data: any, eventId: string): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=firehose.service.d.ts.map
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FirehoseService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const client_firehose_1 = require("@aws-sdk/client-firehose");
7
+ const server_aws_firehose_config_class_1 = require("../server-aws-firehose-config.class");
8
+ let FirehoseService = class FirehoseService {
9
+ config;
10
+ firehoseClient;
11
+ constructor(config, firehoseClient) {
12
+ this.config = config;
13
+ this.firehoseClient = firehoseClient;
14
+ }
15
+ async putRecord(data, eventId) {
16
+ const record = JSON.stringify(data) + '\n';
17
+ try {
18
+ const command = new client_firehose_1.PutRecordCommand({
19
+ DeliveryStreamName: this.config.AWS_FIREHOSE_NAME,
20
+ Record: {
21
+ Data: Buffer.from(record),
22
+ },
23
+ });
24
+ await this.firehoseClient.send(command);
25
+ }
26
+ catch (error) {
27
+ console.error({
28
+ detail: 'failed to put record to Firehose',
29
+ error: error?.message,
30
+ });
31
+ throw error;
32
+ }
33
+ }
34
+ };
35
+ exports.FirehoseService = FirehoseService;
36
+ exports.FirehoseService = FirehoseService = tslib_1.__decorate([
37
+ (0, common_1.Injectable)(),
38
+ tslib_1.__metadata("design:paramtypes", [server_aws_firehose_config_class_1.ServerAwsFirehoseConfig,
39
+ client_firehose_1.FirehoseClient])
40
+ ], FirehoseService);
41
+ //# sourceMappingURL=firehose.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firehose.service.js","sourceRoot":"","sources":["../../../../../../../libs/server/aws-firehose/src/lib/services/firehose.service.ts"],"names":[],"mappings":";;;;AAAA,2CAA4C;AAC5C,8DAA4E;AAC5E,0FAA8E;AAGvE,IAAM,eAAe,GAArB,MAAM,eAAe;IAEhB;IACA;IAFV,YACU,MAA+B,EAC/B,cAA8B;QAD9B,WAAM,GAAN,MAAM,CAAyB;QAC/B,mBAAc,GAAd,cAAc,CAAgB;IACrC,CAAC;IAEJ,KAAK,CAAC,SAAS,CACb,IAAS,EACT,OAAe;QAEf,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAE3C,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,kCAAgB,CAAC;gBACnC,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB;gBACjD,MAAM,EAAE;oBACN,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;iBAC1B;aACF,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC;gBACZ,MAAM,EAAE,kCAAkC;gBAC1C,KAAK,EAAE,KAAK,EAAE,OAAO;aACtB,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAA;AA7BY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;6CAGO,0DAAuB;QACf,gCAAc;GAH7B,eAAe,CA6B3B"}