From 1dcbb6b414a40e66849395dc2c2e1a632a98bf81 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 30 Mar 2021 16:31:23 +0200 Subject: [PATCH] Add old file deletion daily job --- config/default.json5 | 3 ++ config/production.json5 | 3 ++ src/App.ts | 6 ++- src/DeleteOldFilesJobComponent.ts | 62 +++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/DeleteOldFilesJobComponent.ts diff --git a/config/default.json5 b/config/default.json5 index d98ccf6..e4751fb 100644 --- a/config/default.json5 +++ b/config/default.json5 @@ -54,4 +54,7 @@ ], default_url_domain_for_files: 0, default_url_domain_for_urls: 1, + + // 1 hour in ms + delete_old_files_interval: 3600000, } diff --git a/config/production.json5 b/config/production.json5 index 2dd81b7..2cd1c25 100644 --- a/config/production.json5 +++ b/config/production.json5 @@ -26,4 +26,7 @@ ], default_url_domain_for_files: 0, default_url_domain_for_urls: 0, + + // 1 day in ms + delete_old_files_interval: 86400000 } diff --git a/src/App.ts b/src/App.ts index 85674c4..931fbba 100644 --- a/src/App.ts +++ b/src/App.ts @@ -42,7 +42,7 @@ import MagicLinkController from "swaf/auth/magic_link/MagicLinkController"; import AddUsedToMagicLinksMigration from "swaf/auth/magic_link/AddUsedToMagicLinksMigration"; import MakeMagicLinksSessionNotUniqueMigration from "swaf/auth/magic_link/MakeMagicLinksSessionNotUniqueMigration"; import AddPasswordToUsersMigration from "swaf/auth/password/AddPasswordToUsersMigration"; -import DropNameFromUsers from "swaf/auth/migrations/DropNameFromUsers"; +import DeleteOldFilesJobComponent from "./DeleteOldFilesJobComponent"; import packageJson = require('./package.json'); import ReplaceTtlWithExpiresAtFilesTable from "./migrations/ReplaceTtlWithExpiresAtFilesTable"; @@ -117,6 +117,10 @@ export default class App extends Application { // WebSocket server this.use(new WebSocketServerComponent(this, this.as(ExpressAppComponent), this.as(RedisComponent))); + + + // Jobs + this.use(new DeleteOldFilesJobComponent()); } private registerWebSocketListeners() { diff --git a/src/DeleteOldFilesJobComponent.ts b/src/DeleteOldFilesJobComponent.ts new file mode 100644 index 0000000..f4587a3 --- /dev/null +++ b/src/DeleteOldFilesJobComponent.ts @@ -0,0 +1,62 @@ +import ApplicationComponent from "swaf/ApplicationComponent"; +import FileModel from "./models/FileModel"; +import {logger} from "swaf/Logger"; +import config from "config"; +import {WhereTest} from "swaf/db/ModelQuery"; +import MysqlComponent from "swaf/components/MysqlComponent"; +import FileController from "./controllers/FileController"; +import Timeout = NodeJS.Timeout; + +export default class DeleteOldFilesJobComponent extends ApplicationComponent { + private timeout?: Timeout; + private readonly interval: number = config.get('delete_old_files_interval'); + + public async start(): Promise { + if (this.getApp().as(MysqlComponent).canServe()) { + await this.run(); + } + + this.schedule(); + } + + public async stop(): Promise { + if (this.timeout) { + clearInterval(this.timeout); + this.timeout = undefined; + } + } + + private schedule(): void { + if (this.timeout) { + clearInterval(this.timeout); + this.timeout = undefined; + } + + this.timeout = setInterval(() => { + if (this.getApp().as(MysqlComponent).canServe()) { + this.run().catch(err => logger.error(err)); + } + }, this.interval); + logger.info(`Scheduled old file deletion job every ${this.interval}ms`); + + } + + private async run(): Promise { + const filesToDelete = await FileModel.select() + .where('expires_at', new Date(), WhereTest.LE) + .get(); + + if (filesToDelete.length > 0) { + logger.info('Deleting old files...'); + } else { + logger.info('No old file to delete.'); + return; + } + + for (const file of filesToDelete) { + logger.info(`Deleting ${file.id}`); + if (!file.shouldBeDeleted()) throw new Error('File should not be deleted.'); + await FileController.deleteFile(file); + } + } +}