refactor(storage): optimize cloud drive storage service

- Replace manual validateFileSize() with maxSizeBytes in upload()
- Add upload hooks for structured logging
- Add deleteFiles() for bulk deletion via deleteMany()
- Add deleteAllUserFiles() for account deletion via deleteByPrefix()
- Remove unused validateFileSize import

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-20 20:57:18 +01:00
parent 69eac206fa
commit e5ba591680

View file

@ -1,22 +1,29 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
createStorageStorage,
StorageClient,
generateUserFileKey,
getContentType,
validateFileSize,
} from '@manacore/shared-storage';
@Injectable()
export class StorageService {
private readonly logger = new Logger(StorageService.name);
private storage: StorageClient;
private maxFileSize: number;
constructor(private configService: ConfigService) {
const publicUrl = this.configService.get<string>('STORAGE_S3_PUBLIC_URL');
this.storage = createStorageStorage(publicUrl);
this.maxFileSize = this.configService.get<number>('STORAGE_MAX_FILE_SIZE') || 100 * 1024 * 1024; // 100MB default
this.maxFileSize = this.configService.get<number>('STORAGE_MAX_FILE_SIZE') || 100 * 1024 * 1024;
this.storage.hooks.on('upload', ({ key, sizeBytes }) => {
this.logger.debug(`Uploaded ${key} (${sizeBytes} bytes)`);
});
this.storage.hooks.on('upload:error', ({ key, error }) => {
this.logger.error(`Upload failed for ${key}: ${error.message}`);
});
}
async uploadFile(
@ -26,16 +33,11 @@ export class StorageService {
mimeType: string,
subfolder?: string
) {
if (!validateFileSize(buffer.length, this.maxFileSize / (1024 * 1024))) {
throw new Error(
`File size exceeds maximum allowed size of ${this.maxFileSize / (1024 * 1024)}MB`
);
}
const storageKey = generateUserFileKey(userId, originalName, subfolder);
const result = await this.storage.upload(storageKey, buffer, {
contentType: mimeType || getContentType(originalName),
maxSizeBytes: this.maxFileSize,
});
return {
@ -54,6 +56,10 @@ export class StorageService {
await this.storage.delete(storageKey);
}
async deleteFiles(storageKeys: string[]): Promise<void> {
await this.storage.deleteMany(storageKeys);
}
async fileExists(storageKey: string): Promise<boolean> {
return this.storage.exists(storageKey);
}
@ -69,4 +75,11 @@ export class StorageService {
getPublicUrl(storageKey: string): string | undefined {
return this.storage.getPublicUrl(storageKey);
}
/**
* Delete all files for a user (account deletion).
*/
async deleteAllUserFiles(userId: string): Promise<number> {
return this.storage.deleteByPrefix(`users/${userId}/`);
}
}