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
|
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { FileMetadata } from '../entity/file.entity';
import { FilePurpose } from '../entity/purpose.entity';
import { DataSource, Repository } from 'typeorm';
import { Session } from '../../session/entity/session.entity';
import { StorageService } from 'src/storage/service/storage.service';
@Injectable()
export class FileService {
constructor(
@InjectRepository(FileMetadata)
private fileMetadataRepository: Repository<FileMetadata>,
@InjectRepository(FilePurpose)
private filePurposeRepository: Repository<FilePurpose>,
private readonly storageService: StorageService,
private readonly dataSource: DataSource,
) {}
async createFile(
b64Contents: string,
purpose: string,
validTo: Date,
session: Session,
): Promise<FileMetadata | null> {
const filePurpose = await this.filePurposeRepository.findOneBy({ name: purpose });
if (!filePurpose) {
throw new NotFoundException(`Unknown file purpose: ${filePurpose}`);
}
const file = new FileMetadata();
file.purpose = filePurpose;
file.session = session;
file.validUntil = validTo;
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
await this.fileMetadataRepository.save(file);
await this.storageService.createFile(b64Contents, file);
await queryRunner.commitTransaction();
} catch (err) {
await queryRunner.rollbackTransaction();
throw err;
} finally {
queryRunner.release();
}
return file;
}
async markFileAsUsed(fileKey: string): Promise<boolean> {
const update = await this.fileMetadataRepository
.createQueryBuilder()
.update({
used: true,
})
.where('validUntil > :date', {
date: new Date(Date.now()),
})
.andWhere({
key: fileKey,
used: false,
})
.execute();
return update.affected > 0;
}
async fetchFileContents(fileKey: string): Promise<string | null> {
const file = await this.fileMetadataRepository.findOne({
relations: {
contents: true,
},
where: {
key: fileKey,
},
});
return file?.contents?.b64Contents;
}
async findManyValid(sessionToken: string): Promise<FileMetadata[]> {
return this.fileMetadataRepository
.createQueryBuilder('file')
.innerJoinAndSelect('file.session', 'session', 'session.token = :token', { token: sessionToken })
.leftJoinAndSelect('file.purpose', 'purpose')
.where('file.used = :used', { used: false })
.getMany();
}
}
|