Add UploadCleanupService for cron purge and delete uploads.
Move token delete and storage purge off UploadService; enforce expiry and max views on GET without purging until the scheduled cleanup job runs.
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { hashDeleteToken } from '../../../utils/hashDeleteToken';
|
||||
import { isErrorEnoent } from '../../../utils/isErrorEnoent';
|
||||
import { StorageService } from '../../storage/services/StorageService';
|
||||
import { Upload } from '../entities/Upload';
|
||||
|
||||
@Injectable()
|
||||
export class UploadCleanupService {
|
||||
constructor(
|
||||
@InjectDataSource()
|
||||
private readonly dataSource: DataSource,
|
||||
private readonly storageService: StorageService,
|
||||
@InjectRepository(Upload)
|
||||
private readonly uploadRepository: Repository<Upload>
|
||||
) {}
|
||||
|
||||
async deleteUpload(id: string, deleteToken: string): Promise<void> {
|
||||
await this.dataSource.transaction(async manager => {
|
||||
const uploadRepository = manager.getRepository(Upload);
|
||||
|
||||
const upload = await uploadRepository.findOne({
|
||||
where: { id, deleteTokenHash: hashDeleteToken(deleteToken) }
|
||||
});
|
||||
|
||||
if (!upload) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await this.purgeUpload(upload, uploadRepository);
|
||||
});
|
||||
}
|
||||
|
||||
@Cron(CronExpression.EVERY_MINUTE)
|
||||
async purgeUploads(): Promise<void> {
|
||||
const purgeBatchSize = 100;
|
||||
const now = new Date();
|
||||
|
||||
const uploads = await this.uploadRepository
|
||||
.createQueryBuilder('upload')
|
||||
.where('upload.expiresAt IS NOT NULL AND upload.expiresAt < :now', { now })
|
||||
.orWhere('upload.maxViews IS NOT NULL AND upload.viewCount >= upload.maxViews')
|
||||
.take(purgeBatchSize)
|
||||
.getMany();
|
||||
|
||||
for (const upload of uploads) {
|
||||
await this.purgeUpload(upload, this.uploadRepository);
|
||||
}
|
||||
}
|
||||
|
||||
async purgeUpload(upload: Upload, uploadRepository: Repository<Upload>): Promise<void> {
|
||||
try {
|
||||
await this.storageService.unlink(upload.storagePath);
|
||||
} catch (error: unknown) {
|
||||
if (!isErrorEnoent(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
await uploadRepository.remove(upload);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user