nestjs-minio-backend
Version:
NestJS module for MinIO integration
178 lines • 7.89 kB
JavaScript
;
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); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MinioService = void 0;
const common_1 = require("@nestjs/common");
const crypto = require("crypto");
const Minio = require("minio");
const constants_1 = require("./constants");
let MinioService = class MinioService {
constructor(config) {
this.config = config;
this.bucketInitialized = false;
}
async onModuleInit() {
this.minioClient = new Minio.Client({
endPoint: this.config.endPoint,
port: this.config.port,
useSSL: this.config.useSSL,
accessKey: this.config.accessKey,
secretKey: this.config.secretKey,
region: this.config.region,
});
await this.initializeBuckets();
}
async initializeBuckets() {
if (this.bucketInitialized)
return;
for (const bucket of this.config.buckets.private) {
const exists = await this.minioClient.bucketExists(bucket);
if (!exists) {
await this.minioClient.makeBucket(bucket, this.config.region || 'us-east-1');
await this.minioClient.setBucketPolicy(bucket, JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Effect: 'Deny',
Principal: { AWS: ['*'] },
Action: ['s3:GetObject'],
Resource: [`arn:aws:s3:::${bucket}/*`],
},
],
}));
}
}
for (const bucket of this.config.buckets.public) {
const exists = await this.minioClient.bucketExists(bucket);
if (!exists) {
await this.minioClient.makeBucket(bucket, this.config.region || 'us-east-1');
await this.minioClient.setBucketPolicy(bucket, JSON.stringify({
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Principal: { AWS: ['*'] },
Action: ['s3:GetObject'],
Resource: [`arn:aws:s3:::${bucket}/*`],
},
],
}));
}
}
this.bucketInitialized = true;
}
sha256(data) {
return crypto.createHash('sha256').update(data).digest('hex');
}
hmacSha256(key, data) {
return crypto.createHmac('sha256', key).update(data).digest();
}
getSigningKey(secretKey, dateStamp, region, serviceName = 's3') {
const kSecret = 'AWS4' + secretKey;
const kDate = this.hmacSha256(kSecret, dateStamp);
const kRegion = this.hmacSha256(kDate, region);
const kService = this.hmacSha256(kRegion, serviceName);
const kSigning = this.hmacSha256(kService, 'aws4_request');
return kSigning;
}
getAmzDate(date) {
return date.toISOString().replace(/[:-]|\.\d{3}/g, '');
}
getDateStamp(date) {
return date.toISOString().slice(0, 10).replace(/-/g, '');
}
async calculatePresignedGetUrl(endPoint, bucketName, objectName, expirySeconds) {
const accessKey = this.config.accessKey;
const secretKey = this.config.secretKey;
const region = this.config.region || 'us-east-1';
const externalHost = endPoint;
const port = this.config.port;
const signingHost = port ? `${externalHost}:${port}` : externalHost;
const currentDate = new Date();
const amzDate = this.getAmzDate(currentDate);
const dateStamp = this.getDateStamp(currentDate);
const credentialScope = `${dateStamp}/${region}/s3/aws4_request`;
const canonicalURI = '/' +
encodeURIComponent(bucketName) +
'/' +
objectName
.split('/')
.map((segment) => encodeURIComponent(segment))
.join('/');
const params = {
'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
'X-Amz-Credential': `${accessKey}/${credentialScope}`,
'X-Amz-Date': amzDate,
'X-Amz-Expires': expirySeconds.toString(),
'X-Amz-SignedHeaders': 'host',
};
const canonicalQueryString = Object.keys(params)
.sort()
.map((key) => {
return `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
})
.join('&');
const canonicalHeaders = `host:${signingHost.toLowerCase()}\n`;
const canonicalRequest = [
'GET',
canonicalURI,
canonicalQueryString,
canonicalHeaders,
'host',
'UNSIGNED-PAYLOAD',
].join('\n');
const stringToSign = [
'AWS4-HMAC-SHA256',
amzDate,
credentialScope,
this.sha256(canonicalRequest),
].join('\n');
const signingKey = this.getSigningKey(secretKey, dateStamp, region);
const signature = this.hmacSha256(signingKey, stringToSign).toString('hex');
const protocol = (this.config.externalUseSSL ?? this.config.useSSL) ? 'https' : 'http';
const finalUrl = `${protocol}://${signingHost}${canonicalURI}?${canonicalQueryString}&X-Amz-Signature=${signature}`;
return finalUrl;
}
async uploadFile(file, bucketName, objectName) {
if (!this.bucketInitialized) {
await this.initializeBuckets();
}
const fileName = objectName || `${Date.now()}-${file.originalname.replace(/\s/g, '-')}`;
await this.minioClient.putObject(bucketName, fileName, file.buffer, file.size, {
'Content-Type': file.mimetype,
});
return `${bucketName}/${fileName}`;
}
async getPresignedUrl(bucketName, objectName) {
if (!this.config.buckets.private.includes(bucketName)) {
const endpoint = this.config.externalEndPoint || this.config.endPoint;
const protocol = (this.config.externalUseSSL ?? this.config.useSSL) ? 'https' : 'http';
return `${protocol}://${endpoint}${this.config.port ? `:${this.config.port}` : ''}/${bucketName}/${objectName}`;
}
return await this.calculatePresignedGetUrl(this.config.externalEndPoint || this.config.endPoint, bucketName, objectName, (this.config.urlExpiryHours || 1) * 60 * 60);
}
async deleteFile(bucketName, objectName) {
await this.minioClient.removeObject(bucketName, objectName);
}
getMinioClient() {
return this.minioClient;
}
};
exports.MinioService = MinioService;
exports.MinioService = MinioService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)(constants_1.MINIO_CONFIG)),
__metadata("design:paramtypes", [Object])
], MinioService);
//# sourceMappingURL=minio.service.js.map