Spaces:
Runtime error
Runtime error
File size: 4,328 Bytes
51c6e04 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
S3Client,
PutObjectCommand,
DeleteObjectCommand,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
@Injectable()
export class RustfsStorageService {
private readonly logger = new Logger(RustfsStorageService.name);
private s3Client: S3Client;
private bucket: string;
private endpoint: string;
constructor(private configService: ConfigService) {
this.endpoint =
this.configService.get<string>('rustfsStorage.endpoint') || '';
const accessKeyId =
this.configService.get<string>('rustfsStorage.accessKey') || '';
const secretAccessKey =
this.configService.get<string>('rustfsStorage.secretKey') || '';
this.bucket =
this.configService.get<string>('rustfsStorage.bucket') || 'streamflix';
const region =
this.configService.get<string>('rustfsStorage.region') || 'us-east-1';
this.s3Client = new S3Client({
region,
endpoint: this.endpoint,
credentials: {
accessKeyId,
secretAccessKey,
},
forcePathStyle: true,
});
}
/**
* Get folder path based on file type
*/
private getFolderPath(fileType: string): string {
const folderMap: Record<string, string> = {
IMAGE: 'images',
DOCUMENT: 'documents',
VIDEO: 'videos',
};
return folderMap[fileType.toUpperCase()] || 'others';
}
/**
* Generate presigned upload URL using S3
*/
async getPresignedUploadUrl(fileName: string, fileType?: string) {
try {
const folder = fileType ? this.getFolderPath(fileType) : 'others';
const timestamp = Date.now();
const filePath = `${folder}/${timestamp}-${fileName}`;
const command = new PutObjectCommand({
Bucket: this.bucket,
Key: filePath,
});
const uploadUrl = await getSignedUrl(this.s3Client, command, {
expiresIn: 3600,
});
const fileUrl = `${this.endpoint}/${this.bucket}/${filePath}`;
this.logger.log(
`Generated presigned upload URL for: ${filePath}${fileType ? ` (type: ${fileType})` : ''}`,
);
return {
uploadUrl,
fileUrl,
filePath,
expiresIn: 3600,
...(fileType && { fileType }),
};
} catch (error) {
this.logger.error('Failed to generate presigned upload URL', error);
throw error;
}
}
/**
* Delete a file from RustFS Storage
*/
async deleteFile(filePath: string): Promise<any> {
try {
const command = new DeleteObjectCommand({
Bucket: this.bucket,
Key: filePath,
});
const result = await this.s3Client.send(command);
this.logger.log(`Successfully deleted file: ${filePath}`);
return {
success: true,
deletedPath: filePath,
result,
};
} catch (error) {
this.logger.error(`Failed to delete file: ${filePath}`, error);
throw new NotFoundException('File not found or already deleted');
}
}
/**
* Delete a file by URL
*/
async deleteFileByUrl(url: string): Promise<any> {
try {
const filePath = this.extractFilePathFromUrl(url);
if (!filePath) {
throw new NotFoundException('Could not extract file path from URL');
}
const result = await this.deleteFile(filePath);
this.logger.log(`Successfully deleted file from URL: ${url}`);
return {
...result,
deletedUrl: url,
};
} catch (error) {
if (error instanceof NotFoundException) {
throw error;
}
this.logger.error(`Failed to delete file by URL: ${url}`, error);
throw new NotFoundException('File not found or already deleted');
}
}
/**
* Extract file path from RustFS Storage URL
*/
private extractFilePathFromUrl(url: string): string | null {
try {
const urlObj = new URL(url);
const pathParts = urlObj.pathname.split('/');
const bucketIndex = pathParts.indexOf(this.bucket);
if (bucketIndex !== -1 && bucketIndex < pathParts.length - 1) {
return pathParts.slice(bucketIndex + 1).join('/');
}
return pathParts.slice(1).join('/');
} catch {
return null;
}
}
}
|