Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 1x 1x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 9x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 9x 9x 9x 9x 9x 9x 9x 9x 9x 1x | import { type Readable } from 'node:stream';
import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { DataExport, Download } from '@amalia/core/models';
import { assert } from '@amalia/ext/typescript';
import { DataExportStatus, DownloadType } from '@amalia/reporting/exports/shared';
import { CloudStorageDownloadService } from '@amalia/vendors/google-cloud/cloud-storage';
import { configuration } from '../../configuration';
const FIVE_MINUTES_IN_MS = 5 * 60 * 1000;
const DATA_EXPORT_FOLDER = 'data-exports';
@Injectable()
export class DownloadsService {
public constructor(
@InjectRepository(Download)
private readonly downloadRepository: Repository<Download>,
@InjectRepository(DataExport)
private readonly dataExportRepository: Repository<DataExport>,
private readonly cloudStorageDownloadService: CloudStorageDownloadService,
) {}
public async streamFile(token: string): Promise<{
stream: Readable;
filenameWithoutExtension: string;
extension: string;
contentType: string;
}> {
const download = await this.downloadRepository.findOneBy({ token });
assert(download, new NotFoundException('Token not found'));
// Validity check
const now = Date.now();
const createdAt = download.createdAt.getTime();
const isExpired = Math.abs(now - createdAt) > FIVE_MINUTES_IN_MS;
assert(!isExpired, new BadRequestException('Token is expired. Please retry your download'));
assert(
(download.type as string) === DownloadType.DATA_EXPORT,
new BadRequestException(`Unknown type ${download.type}`),
);
const dataExport = await this.dataExportRepository.findOne({
where: { id: download.dataId },
relations: ['company', 'creator'],
});
assert(dataExport, new NotFoundException('The data export was not found and could not be downloaded'));
assert(dataExport.status === DataExportStatus.SUCCESS, new ConflictException('This export was not successful'));
// Get the file from cloudStorage
const stream = this.cloudStorageDownloadService.streamFile(
dataExport.company,
DATA_EXPORT_FOLDER,
`${dataExport.id}.${dataExport.properties.extension}`,
configuration.gcp.storage.api,
);
assert(stream?.pipe, new NotFoundException('File not retrieved'));
return {
stream,
filenameWithoutExtension: dataExport.properties.filename ?? 'export',
extension: dataExport.properties.extension,
contentType: dataExport.properties.contentType,
};
}
}
|