UNPKG

safeer-pdf-generator

Version:

Framework-agnostic PDF generation library with chunking, merging, S3 upload, and email delivery

297 lines 12.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultS3Uploader = void 0; exports.createS3Uploader = createS3Uploader; const errors_js_1 = require("../../core/errors.js"); const guards_js_1 = require("../../utils/guards.js"); const defaults_js_1 = require("../../config/defaults.js"); /** * Default S3 uploader implementation */ class DefaultS3Uploader { constructor(config, logger = defaults_js_1.noOpLogger) { this.config = config; this.logger = logger; } /** * Initialize S3 client (lazy loading to avoid requiring AWS SDK) */ async initS3Client() { if (this.s3Client) { return this.s3Client; } try { // Dynamic import to avoid bundling AWS SDK if not used const { S3Client } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); this.s3Client = new S3Client({ region: this.config.region, credentials: this.config.accessKeyId && this.config.secretAccessKey ? { accessKeyId: this.config.accessKeyId, secretAccessKey: this.config.secretAccessKey, } : undefined, // Use default credential provider chain if not provided endpoint: this.config.endpoint, }); this.logger.debug('S3 client initialized successfully'); return this.s3Client; } catch (error) { throw new errors_js_1.S3UploadError('Failed to initialize S3 client. Make sure @aws-sdk/client-s3 is installed.', this.config.bucket, undefined, error); } } /** * Upload buffer to S3 */ async upload(buffer, filename, mimeType = 'application/pdf', options = {}) { const s3Client = await this.initS3Client(); const sanitizedFilename = (0, guards_js_1.sanitizeFilename)(filename); const folder = options.folder || this.config.folder || 'pdf-reports'; const key = folder ? `${folder}/${sanitizedFilename}` : sanitizedFilename; const acl = options.acl || this.config.acl || 'public-read'; this.logger.info(`Uploading to S3: s3://${this.config.bucket}/${key}`); try { // Dynamic import of PutObjectCommand const { PutObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); const command = new PutObjectCommand({ Bucket: this.config.bucket, Key: key, Body: buffer, ContentType: mimeType, ContentDisposition: 'inline', ACL: acl, // Type assertion for S3 ACL }); const result = await s3Client.send(command); const url = this.config.endpoint ? `${this.config.endpoint}/${this.config.bucket}/${key}` : `https://${this.config.bucket}.s3.${this.config.region}.amazonaws.com/${key}`; this.logger.info(`Upload successful: ${key} (${(buffer.length / 1024 / 1024).toFixed(2)}MB, ETag: ${result.ETag})`); return { url, key }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; this.logger.error(`S3 upload failed: ${errorMessage}`); throw new errors_js_1.S3UploadError(`Failed to upload to S3: ${errorMessage}`, this.config.bucket, key, error); } } /** * Check if object exists in S3 */ async exists(key) { const s3Client = await this.initS3Client(); try { const { HeadObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); const command = new HeadObjectCommand({ Bucket: this.config.bucket, Key: key, }); await s3Client.send(command); return true; } catch (error) { if (error.name === 'NotFound' || error.$metadata?.httpStatusCode === 404) { return false; } throw new errors_js_1.S3UploadError(`Failed to check object existence: ${error.message}`, this.config.bucket, key, error); } } /** * Delete object from S3 */ async delete(key) { const s3Client = await this.initS3Client(); try { const { DeleteObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); const command = new DeleteObjectCommand({ Bucket: this.config.bucket, Key: key, }); await s3Client.send(command); this.logger.info(`Deleted from S3: ${key}`); } catch (error) { throw new errors_js_1.S3UploadError(`Failed to delete from S3: ${error instanceof Error ? error.message : 'Unknown error'}`, this.config.bucket, key, error); } } /** * Get object metadata */ async getMetadata(key) { const s3Client = await this.initS3Client(); try { const { HeadObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); const command = new HeadObjectCommand({ Bucket: this.config.bucket, Key: key, }); const result = await s3Client.send(command); return { size: result.ContentLength || 0, lastModified: result.LastModified || new Date(), contentType: result.ContentType, etag: result.ETag, }; } catch (error) { throw new errors_js_1.S3UploadError(`Failed to get metadata: ${error instanceof Error ? error.message : 'Unknown error'}`, this.config.bucket, key, error); } } /** * Generate presigned URL for temporary access */ async generatePresignedUrl(key, expiresInSeconds = 3600) { const s3Client = await this.initS3Client(); try { const { GetObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); // Try to import the presigner, fallback if not available let getSignedUrl; try { // Use eval to bypass TypeScript module resolution const presignerModuleName = '@aws-sdk/s3-request-presigner'; const presigner = await eval(`import('${presignerModuleName}')`); getSignedUrl = presigner.getSignedUrl; } catch (importError) { throw new Error('The @aws-sdk/s3-request-presigner package is required for generating presigned URLs. ' + 'Please install it with: npm install @aws-sdk/s3-request-presigner'); } const command = new GetObjectCommand({ Bucket: this.config.bucket, Key: key, }); const url = await getSignedUrl(s3Client, command, { expiresIn: expiresInSeconds, }); return url; } catch (error) { throw new errors_js_1.S3UploadError(`Failed to generate presigned URL: ${error instanceof Error ? error.message : 'Unknown error'}`, this.config.bucket, key, error); } } /** * List objects with prefix */ async listObjects(prefix, maxKeys = 1000) { const s3Client = await this.initS3Client(); try { const { ListObjectsV2Command } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); const command = new ListObjectsV2Command({ Bucket: this.config.bucket, Prefix: prefix, MaxKeys: maxKeys, }); const result = await s3Client.send(command); return (result.Contents || []).map((obj) => ({ key: obj.Key || '', size: obj.Size || 0, lastModified: obj.LastModified || new Date(), etag: obj.ETag || '', })); } catch (error) { throw new errors_js_1.S3UploadError(`Failed to list objects: ${error instanceof Error ? error.message : 'Unknown error'}`, this.config.bucket, undefined, error); } } /** * Get bucket region */ async getBucketRegion() { const s3Client = await this.initS3Client(); try { const { GetBucketLocationCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3'))); const command = new GetBucketLocationCommand({ Bucket: this.config.bucket, }); const result = await s3Client.send(command); return result.LocationConstraint || 'us-east-1'; } catch (error) { throw new errors_js_1.S3UploadError(`Failed to get bucket region: ${error instanceof Error ? error.message : 'Unknown error'}`, this.config.bucket, undefined, error); } } /** * Test S3 connectivity and permissions */ async testConnection() { try { // Test basic connectivity by getting bucket location await this.getBucketRegion(); // Test write permissions with a small test file const testKey = `test-${Date.now()}.txt`; const testBuffer = Buffer.from('test'); let canWrite = false; try { await this.upload(testBuffer, testKey, 'text/plain'); canWrite = true; // Clean up test file await this.delete(testKey); } catch (writeError) { this.logger.warn(`Write test failed: ${writeError}`); } // Test read permissions by listing objects let canRead = false; try { await this.listObjects('', 1); canRead = true; } catch (readError) { this.logger.warn(`Read test failed: ${readError}`); } return { success: true, canRead, canWrite, }; } catch (error) { return { success: false, canRead: false, canWrite: false, error: error instanceof Error ? error.message : 'Unknown error', }; } } } exports.DefaultS3Uploader = DefaultS3Uploader; /** * Create S3 uploader from configuration */ function createS3Uploader(config, logger) { return new DefaultS3Uploader(config, logger); } //# sourceMappingURL=S3Uploader.js.map