UNPKG

@russ-b/nestjs-common-tools

Version:
221 lines 9.77 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var S3Service_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.S3Service = void 0; const common_1 = require("@nestjs/common"); const client_s3_1 = require("@aws-sdk/client-s3"); const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner"); const stream_1 = require("stream"); const lib_storage_1 = require("@aws-sdk/lib-storage"); const s3_constants_1 = require("./s3.constants"); let S3Service = S3Service_1 = class S3Service { constructor(options, s3Client) { this.options = options; this.s3Client = s3Client; this.logger = options.logger === true ? new common_1.Logger(S3Service_1.name) : options.logger || undefined; } async upload(key, body, options) { const upload = new lib_storage_1.Upload({ client: this.s3Client, params: this.createPutObjectParams(key, body, options, true), }); const response = await upload.done(); this.log(`Uploaded object with multipart upload: ${key}`); return response; } async putObject(key, body, options) { const command = new client_s3_1.PutObjectCommand(this.createPutObjectParams(key, body, options, true)); try { const response = await this.s3Client.send(command); this.log(`Uploaded object: ${key}`); return response; } catch (error) { this.logError(`Failed to upload object: ${key}`, error); throw error; } } async uploadObject(key, body, options) { return this.putObject(key, body, options); } async deleteObject(key, options) { const command = new client_s3_1.DeleteObjectCommand({ Bucket: this.resolveBucket(options?.bucket), Key: key, }); try { const response = await this.s3Client.send(command); this.log(`Deleted object: ${key}`); return response; } catch (error) { this.logError(`Failed to delete object: ${key}`, error); throw error; } } async copyObject(sourceKey, destinationKey, options) { const command = new client_s3_1.CopyObjectCommand(this.createCopyObjectParams(sourceKey, destinationKey, options)); try { const response = await this.s3Client.send(command); this.log(`Copied object: ${sourceKey} -> ${destinationKey}`); return response; } catch (error) { this.logError(`Failed to copy object: ${sourceKey} -> ${destinationKey}`, error); throw error; } } async getObject(key, options) { const command = new client_s3_1.GetObjectCommand({ Bucket: this.resolveBucket(options?.bucket), Key: key, }); try { const response = await this.s3Client.send(command); this.log(`Fetched object: ${key}`); if (!response.Body) { throw new Error(`S3 object "${key}" returned an empty body.`); } if (!(response.Body instanceof stream_1.Readable)) { throw new Error(`S3 object "${key}" did not return a Node.js readable stream.`); } return { body: response.Body, cacheControl: response.CacheControl, contentLength: response.ContentLength, contentType: response.ContentType, eTag: response.ETag, lastModified: response.LastModified, metadata: response.Metadata, }; } catch (error) { this.logError(`Failed to get object: ${key}`, error); throw error; } } async listObjects(options) { const command = new client_s3_1.ListObjectsV2Command({ Bucket: this.resolveBucket(options?.bucket), ...(options?.continuationToken ? { ContinuationToken: options.continuationToken } : {}), ...(options?.delimiter ? { Delimiter: options.delimiter } : {}), ...(options?.maxKeys ? { MaxKeys: options.maxKeys } : {}), ...(options?.prefix ? { Prefix: options.prefix } : {}), }); try { const response = await this.s3Client.send(command); this.log(`Listed objects for bucket: ${command.input.Bucket}`); return response; } catch (error) { this.logError(`Failed to list objects for bucket: ${command.input.Bucket}`, error); throw error; } } async getSignedUrl(key, options) { const expiresIn = options?.expiresIn ?? 900; const operation = options?.operation ?? 'getObject'; const s3Client = this.resolveSigningClient(options?.endpoint); const command = operation === 'putObject' ? new client_s3_1.PutObjectCommand(this.createPutObjectParams(key, undefined, options)) : new client_s3_1.GetObjectCommand({ Bucket: this.resolveBucket(options?.bucket), Key: key, }); return (0, s3_request_presigner_1.getSignedUrl)(s3Client, command, { expiresIn }); } createCopyObjectParams(sourceKey, destinationKey, options) { const destinationBucket = this.resolveBucket(options?.bucket); const sourceBucket = this.resolveBucket(options?.sourceBucket ?? options?.bucket); const shouldReplaceMetadata = !!(options?.cacheControl || options?.contentType || options?.metadata); const metadataDirective = options?.metadataDirective ?? (shouldReplaceMetadata ? 'REPLACE' : undefined); return { Bucket: destinationBucket, CopySource: this.buildCopySource(sourceBucket, sourceKey), Key: destinationKey, ...(options?.acl ? { ACL: options.acl } : {}), ...(options?.cacheControl ? { CacheControl: options.cacheControl } : {}), ...(options?.contentType ? { ContentType: options.contentType } : {}), ...(metadataDirective ? { MetadataDirective: metadataDirective } : {}), ...(options?.metadata ? { Metadata: options.metadata } : {}), }; } createPutObjectParams(key, body, options, useDefaultContentType = false) { const contentType = options?.contentType ?? (useDefaultContentType ? 'application/octet-stream' : undefined); return { Bucket: this.resolveBucket(options?.bucket), Key: key, ...(body !== undefined ? { Body: body } : {}), ...(contentType ? { ContentType: contentType } : {}), ...(options?.acl ? { ACL: options.acl } : {}), ...(options?.cacheControl ? { CacheControl: options.cacheControl } : {}), ...(options?.metadata ? { Metadata: options.metadata } : {}), }; } buildCopySource(bucket, key) { return `${encodeURIComponent(bucket)}/${key .split('/') .map((part) => encodeURIComponent(part)) .join('/')}`; } resolveBucket(bucket) { const resolvedBucket = bucket ?? this.options.defaultBucket ?? this.options.bucket; if (!resolvedBucket) { throw new Error('S3 bucket is not configured. Pass a bucket to the method call or set S3ModuleOptions.defaultBucket.'); } return resolvedBucket; } resolveSigningClient(endpoint) { if (!endpoint || endpoint === this.options.endpoint) { return this.s3Client; } const clientConfig = this.s3Client.config; return new client_s3_1.S3Client({ region: this.options.region ?? 'us-east-1', endpoint, forcePathStyle: this.options.forcePathStyle ?? true, requestChecksumCalculation: this.options.requestChecksumCalculation, responseChecksumValidation: this.options.responseChecksumValidation, ...(clientConfig?.credentials ? { credentials: clientConfig.credentials } : {}), }); } log(message) { this.logger?.log(message); } logError(message, error) { if (!this.logger) { return; } if (error instanceof Error) { this.logger.error(message, error.stack ?? error.message); return; } this.logger.error(message, error); } }; exports.S3Service = S3Service; exports.S3Service = S3Service = S3Service_1 = __decorate([ (0, common_1.Injectable)(), __param(0, (0, common_1.Inject)(s3_constants_1.S3_MODULE_OPTIONS)), __param(1, (0, common_1.Inject)(s3_constants_1.S3_CLIENT)), __metadata("design:paramtypes", [Object, client_s3_1.S3Client]) ], S3Service); //# sourceMappingURL=s3.service.js.map