75 lines
1.8 KiB
TypeScript
75 lines
1.8 KiB
TypeScript
import {
|
|
ForbiddenException,
|
|
Injectable,
|
|
NotFoundException,
|
|
} from '@nestjs/common';
|
|
import { stat } from 'node:fs/promises';
|
|
import { PrismaService } from '../../infrastructure/database/prisma.service';
|
|
import { LocalFilesystemStorageService } from '../storage/storage.service';
|
|
|
|
export interface AudioAssetDownload {
|
|
filePath: string;
|
|
contentLength: number;
|
|
}
|
|
|
|
@Injectable()
|
|
export class AssetsService {
|
|
constructor(
|
|
private readonly prismaService: PrismaService,
|
|
private readonly storageService: LocalFilesystemStorageService,
|
|
) {}
|
|
|
|
async getOwnedAudioAssetDownload(
|
|
assetId: string,
|
|
deviceId: string,
|
|
): Promise<AudioAssetDownload> {
|
|
const device = await this.prismaService.device.findUnique({
|
|
where: { id: deviceId },
|
|
select: {
|
|
userId: true,
|
|
},
|
|
});
|
|
|
|
if (!device) {
|
|
throw new NotFoundException('Device not found');
|
|
}
|
|
|
|
const asset = await this.prismaService.audioAsset.findUnique({
|
|
where: { id: assetId },
|
|
select: {
|
|
userId: true,
|
|
storageKey: true,
|
|
},
|
|
});
|
|
|
|
if (!asset) {
|
|
throw new NotFoundException('Audio asset not found');
|
|
}
|
|
|
|
if (asset.userId !== device.userId) {
|
|
throw new ForbiddenException('Audio asset does not belong to this device user.');
|
|
}
|
|
|
|
const filePath = this.storageService.resolve(asset.storageKey);
|
|
|
|
try {
|
|
const fileStats = await stat(filePath);
|
|
|
|
if (!fileStats.isFile()) {
|
|
throw new NotFoundException('Audio asset file not found');
|
|
}
|
|
|
|
return {
|
|
filePath,
|
|
contentLength: fileStats.size,
|
|
};
|
|
} catch (error) {
|
|
if (error instanceof NotFoundException) {
|
|
throw error;
|
|
}
|
|
|
|
throw new NotFoundException('Audio asset file not found');
|
|
}
|
|
}
|
|
}
|