"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 StorageService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.StorageService = void 0; const common_1 = require("@nestjs/common"); const config_1 = require("@nestjs/config"); const crypto_1 = require("crypto"); const Minio = require("minio"); let StorageService = StorageService_1 = class StorageService { configService; logger = new common_1.Logger(StorageService_1.name); client; publicBucket; privateBucket; publicUrl; constructor(configService) { this.configService = configService; this.client = new Minio.Client({ endPoint: this.configService.getOrThrow('minio.endpoint'), port: this.configService.get('minio.port', 9000), useSSL: this.configService.get('minio.useSsl', false), accessKey: this.configService.getOrThrow('minio.accessKey'), secretKey: this.configService.getOrThrow('minio.secretKey'), }); this.publicBucket = this.configService.getOrThrow('minio.publicBucket'); this.privateBucket = this.configService.getOrThrow('minio.privateBucket'); this.publicUrl = this.configService.get('minio.publicUrl'); } async onModuleInit() { await this.ensureBucket(this.publicBucket, true); await this.ensureBucket(this.privateBucket, false); } async uploadPublicFile(file, folder = 'products') { return this.upload(file, this.publicBucket, folder); } async uploadPrivateFile(file, folder = 'products') { return this.upload(file, this.privateBucket, folder); } async deleteFile(bucket, objectName) { try { await this.client.removeObject(bucket, objectName); } catch (error) { this.logger.warn(`Failed to delete object ${bucket}/${objectName}: ${error instanceof Error ? error.message : 'unknown error'}`); } } async deletePublicFileByUrl(fileUrl) { if (!fileUrl) { return; } const objectName = this.extractObjectName(fileUrl, this.publicBucket); if (objectName) { await this.deleteFile(this.publicBucket, objectName); } } extractObjectName(fileUrl, bucket) { if (!fileUrl) { return null; } try { const parsedUrl = new URL(fileUrl); const prefix = `/${bucket}/`; const path = parsedUrl.pathname.startsWith(prefix) ? parsedUrl.pathname.slice(prefix.length) : parsedUrl.pathname.replace(/^\//, ''); return decodeURIComponent(path); } catch { return null; } } async upload(file, bucket, folder) { if (!file) { throw new common_1.InternalServerErrorException('File upload payload is empty'); } const objectName = `${folder}/${(0, crypto_1.randomUUID)()}-${file.originalname.replace(/\s+/g, '-')}`; try { await this.client.putObject(bucket, objectName, file.buffer, file.size, { 'Content-Type': file.mimetype, }); } catch (error) { throw new common_1.InternalServerErrorException(`File upload failed: ${error instanceof Error ? error.message : 'unknown error'}`); } return { bucket, objectName, url: this.buildPublicUrl(bucket, objectName), }; } async ensureBucket(bucketName, makePublic) { const exists = await this.client.bucketExists(bucketName); if (!exists) { await this.client.makeBucket(bucketName); } if (makePublic) { await this.client.setBucketPolicy(bucketName, JSON.stringify({ Version: '2012-10-17', Statement: [ { Effect: 'Allow', Principal: { AWS: ['*'] }, Action: ['s3:GetObject'], Resource: [`arn:aws:s3:::${bucketName}/*`], }, ], })); } } buildPublicUrl(bucket, objectName) { if (this.publicUrl) { return `${this.publicUrl.replace(/\/$/, '')}/${bucket}/${objectName}`; } const protocol = this.configService.get('minio.useSsl', false) ? 'https' : 'http'; const endpoint = this.configService.getOrThrow('minio.endpoint'); const port = this.configService.get('minio.port', 9000); return `${protocol}://${endpoint}:${port}/${bucket}/${objectName}`; } }; exports.StorageService = StorageService; exports.StorageService = StorageService = StorageService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [config_1.ConfigService]) ], StorageService); //# sourceMappingURL=storage.service.js.map