1 import * as bluebird from 'bluebird'
2 import { readdir, remove, stat } from 'fs-extra'
3 import { logger, loggerTagsFactory } from '@server/helpers/logger'
4 import { getResumableUploadPath } from '@server/helpers/upload'
5 import { SCHEDULER_INTERVALS_MS } from '@server/initializers/constants'
6 import { METAFILE_EXTNAME } from '@uploadx/core'
7 import { AbstractScheduler } from './abstract-scheduler'
9 const lTags = loggerTagsFactory('scheduler', 'resumable-upload', 'cleaner')
11 export class RemoveDanglingResumableUploadsScheduler extends AbstractScheduler {
13 private static instance: AbstractScheduler
14 private lastExecutionTimeMs: number
16 protected schedulerIntervalMs = SCHEDULER_INTERVALS_MS.removeDanglingResumableUploads
18 private constructor () {
21 this.lastExecutionTimeMs = new Date().getTime()
24 protected async internalExecute () {
25 const path = getResumableUploadPath()
26 const files = await readdir(path)
28 const metafiles = files.filter(f => f.endsWith(METAFILE_EXTNAME))
30 if (metafiles.length === 0) return
32 logger.debug('Reading resumable video upload folder %s with %d files', path, metafiles.length, lTags())
35 await bluebird.map(metafiles, metafile => {
36 return this.deleteIfOlderThan(metafile, this.lastExecutionTimeMs)
37 }, { concurrency: 5 })
39 logger.error('Failed to handle file during resumable video upload folder cleanup', { error, ...lTags() })
41 this.lastExecutionTimeMs = new Date().getTime()
45 private async deleteIfOlderThan (metafile: string, olderThan: number) {
46 const metafilePath = getResumableUploadPath(metafile)
47 const statResult = await stat(metafilePath)
49 // Delete uploads that started since a long time
50 if (statResult.ctimeMs < olderThan) {
51 await remove(metafilePath)
53 const datafile = metafilePath.replace(new RegExp(`${METAFILE_EXTNAME}$`), '')
54 await remove(datafile)
58 static get Instance () {
59 return this.instance || (this.instance = new this())