velody/backend/src/modules/storage/storage.service.ts
2026-05-31 01:20:56 +02:00

89 lines
2.2 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { mkdir, access } from 'node:fs/promises';
import { constants } from 'node:fs';
import { dirname, join } from 'node:path';
import { AppConfigService } from '../config/config.service';
export interface StorageStatus {
root: string;
writable: boolean;
}
@Injectable()
export class LocalFilesystemStorageService {
constructor(private readonly configService: AppConfigService) {}
get root(): string {
return this.configService.storageRoot;
}
resolve(relativePath: string): string {
return join(this.root, relativePath);
}
userAudioAssetStorageKey(userId: string, sha256: string): string {
return join('users', userId, 'audio', `${sha256}.mp3`);
}
userAudioAssetPath(userId: string, sha256: string): string {
return this.resolve(this.userAudioAssetStorageKey(userId, sha256));
}
userArtworkAssetStorageKey(
userId: string,
sha256: string,
fileExtension: string,
): string {
return join('users', userId, 'artwork', `${sha256}.${fileExtension}`);
}
userArtworkAssetPath(
userId: string,
sha256: string,
fileExtension: string,
): string {
return this.resolve(
this.userArtworkAssetStorageKey(userId, sha256, fileExtension),
);
}
tempUploadStorageKey(uploadId: string): string {
return join('temp', 'uploads', `${uploadId}.part`);
}
tempUploadPath(uploadId: string): string {
return this.resolve(this.tempUploadStorageKey(uploadId));
}
async ensureDirectory(path: string): Promise<void> {
await mkdir(path, { recursive: true });
}
async ensureParentDirectory(path: string): Promise<void> {
await this.ensureDirectory(dirname(path));
}
async checkReadiness(): Promise<StorageStatus> {
const paths = [
this.root,
join(this.root, 'users'),
join(this.root, 'temp'),
join(this.root, 'temp', 'uploads'),
join(this.root, 'incoming'),
join(this.root, 'quarantine'),
join(this.root, 'library', 'audio'),
join(this.root, 'library', 'artwork'),
];
for (const path of paths) {
await mkdir(path, { recursive: true });
await access(path, constants.R_OK | constants.W_OK);
}
return {
root: this.root,
writable: true,
};
}
}