s3-cli-js
Version:
A TypeScript-based npm package that replaces AWS CLI for S3 operations using presigned URLs
282 lines • 9.47 kB
JavaScript
;
/**
* 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