UNPKG

s3-cli-js

Version:

A TypeScript-based npm package that replaces AWS CLI for S3 operations using presigned URLs

282 lines 9.47 kB
"use strict"; /** * S3 Client implementation using presigned URLs and direct HTTP requests */ 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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.S3ClientWrapper = void 0; const client_s3_1 = require("@aws-sdk/client-s3"); const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner"); const client_s3_2 = require("@aws-sdk/client-s3"); const axios_1 = __importDefault(require("axios")); const fs = __importStar(require("fs")); const mime = __importStar(require("mime-types")); class S3ClientWrapper { client; config; constructor(config) { this.config = config; this.client = new client_s3_1.S3Client({ region: config.region, credentials: { accessKeyId: config.accessKeyId, secretAccessKey: config.secretAccessKey, sessionToken: config.sessionToken, }, endpoint: config.endpoint, }); } /** * List all buckets */ async listBuckets() { const command = new client_s3_1.ListBucketsCommand({}); const response = await this.client.send(command); return (response.Buckets || []).map(bucket => ({ name: bucket.Name, creationDate: bucket.CreationDate, })); } /** * List objects in a bucket */ async listObjects(options) { const command = new client_s3_1.ListObjectsV2Command({ Bucket: options.bucket, Prefix: options.prefix, Delimiter: options.delimiter, MaxKeys: options.maxKeys, ContinuationToken: options.continuationToken, }); const response = await this.client.send(command); const objects = (response.Contents || []).map(obj => ({ key: obj.Key, size: obj.Size || 0, lastModified: obj.LastModified, etag: obj.ETag, storageClass: obj.StorageClass, })); const commonPrefixes = (response.CommonPrefixes || []).map(cp => cp.Prefix); return { objects, commonPrefixes, isTruncated: response.IsTruncated || false, nextContinuationToken: response.NextContinuationToken, }; } /** * Create a bucket */ async createBucket(bucketName) { const command = new client_s3_1.CreateBucketCommand({ Bucket: bucketName, }); await this.client.send(command); } /** * Delete a bucket */ async deleteBucket(bucketName) { const command = new client_s3_1.DeleteBucketCommand({ Bucket: bucketName, }); await this.client.send(command); } /** * Check if object exists */ async objectExists(bucket, key) { try { const command = new client_s3_1.HeadObjectCommand({ Bucket: bucket, Key: key, }); await this.client.send(command); return true; } catch (error) { if (error.name === 'NotFound') { return false; } throw error; } } /** * Get object metadata */ async getObjectMetadata(bucket, key) { try { const command = new client_s3_1.HeadObjectCommand({ Bucket: bucket, Key: key, }); const response = await this.client.send(command); return { size: response.ContentLength || 0, lastModified: response.LastModified || new Date(), etag: response.ETag?.replace(/"/g, '') || '' }; } catch (error) { if (error.name === 'NotFound') { return null; } throw error; } } /** * Generate presigned URL for S3 operations */ async getPresignedUrl(options) { const { bucket, key, operation, expiresIn = 3600, contentType } = options; let command; switch (operation) { case 'getObject': command = new client_s3_2.GetObjectCommand({ Bucket: bucket, Key: key }); break; case 'putObject': command = new client_s3_2.PutObjectCommand({ Bucket: bucket, Key: key, ContentType: contentType }); break; case 'deleteObject': command = new client_s3_2.DeleteObjectCommand({ Bucket: bucket, Key: key }); break; default: throw new Error(`Unsupported operation: ${operation}`); } return await (0, s3_request_presigner_1.getSignedUrl)(this.client, command, { expiresIn }); } /** * Upload file using presigned URL and direct HTTP */ async uploadFile(filePath, bucket, key, onProgress) { const fileStats = fs.statSync(filePath); const contentType = mime.lookup(filePath) || 'application/octet-stream'; const presignedUrl = await this.getPresignedUrl({ bucket, key, operation: 'putObject', contentType, }); const fileStream = fs.createReadStream(filePath); await this.httpRequest({ method: 'PUT', url: presignedUrl, headers: { 'Content-Type': contentType, 'Content-Length': fileStats.size.toString(), }, data: fileStream, onProgress, }); } /** * Download file using presigned URL and direct HTTP */ async downloadFile(bucket, key, filePath, onProgress) { const presignedUrl = await this.getPresignedUrl({ bucket, key, operation: 'getObject', }); const response = await (0, axios_1.default)({ method: 'GET', url: presignedUrl, responseType: 'stream', onDownloadProgress: (progressEvent) => { if (onProgress && progressEvent.total) { onProgress({ loaded: progressEvent.loaded, total: progressEvent.total, percentage: Math.round((progressEvent.loaded / progressEvent.total) * 100), }); } }, }); const writer = fs.createWriteStream(filePath); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on('finish', resolve); writer.on('error', reject); }); } /** * Delete object using presigned URL */ async deleteObject(bucket, key) { const presignedUrl = await this.getPresignedUrl({ bucket, key, operation: 'deleteObject', }); await this.httpRequest({ method: 'DELETE', url: presignedUrl, }); } /** * Make direct HTTP request */ async httpRequest(options) { const { method, url, headers, data, onProgress } = options; const config = { method, url, headers, data, }; if (onProgress && method === 'PUT') { config.onUploadProgress = (progressEvent) => { if (progressEvent.total) { onProgress({ loaded: progressEvent.loaded, total: progressEvent.total, percentage: Math.round((progressEvent.loaded / progressEvent.total) * 100), }); } }; } const response = await (0, axios_1.default)(config); return response.data; } } exports.S3ClientWrapper = S3ClientWrapper; //# sourceMappingURL=s3-client.js.map