75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
|
|
import * as fs from 'node:fs'
|
||
|
|
import { db } from '@/utils/database'
|
||
|
|
import redis from '@/utils/redis'
|
||
|
|
import { uploadVideo } from '@/utils/upload'
|
||
|
|
import { checkRestoreStatus, downloadRestoredObjectToFile } from '@/utils/glacier'
|
||
|
|
import { sendRestoreCompleteEmail } from '@/utils/mail'
|
||
|
|
|
||
|
|
const POLL_INTERVAL_MS = 10 * 60000
|
||
|
|
|
||
|
|
async function processRestoringRow(row: { uuid: string, videoId: string, requester_email: string }) {
|
||
|
|
const { ongoing, expiry } = await checkRestoreStatus(row.videoId)
|
||
|
|
if (ongoing) return
|
||
|
|
|
||
|
|
await db.updateTable('restore_requests')
|
||
|
|
.set({ status: 'reuploading', aws_restore_expiry: expiry, updated_at: new Date() })
|
||
|
|
.where('uuid', '=', row.uuid)
|
||
|
|
.execute()
|
||
|
|
|
||
|
|
const filePath = `./videos/${row.videoId}.mp4`
|
||
|
|
try {
|
||
|
|
await downloadRestoredObjectToFile(row.videoId, filePath)
|
||
|
|
const videoUrl = await uploadVideo(filePath)
|
||
|
|
|
||
|
|
await db.updateTable('videos')
|
||
|
|
.set({ deletion_stage: null, source: videoUrl })
|
||
|
|
.where('id', '=', row.videoId)
|
||
|
|
.execute()
|
||
|
|
|
||
|
|
await redis.del(`watch:${row.videoId}:html`)
|
||
|
|
await redis.del('deletion:html')
|
||
|
|
|
||
|
|
await db.updateTable('restore_requests')
|
||
|
|
.set({ status: 'reuploaded', updated_at: new Date() })
|
||
|
|
.where('uuid', '=', row.uuid)
|
||
|
|
.execute()
|
||
|
|
|
||
|
|
const video = await db.selectFrom('videos')
|
||
|
|
.select(['title'])
|
||
|
|
.where('id', '=', row.videoId)
|
||
|
|
.executeTakeFirst()
|
||
|
|
|
||
|
|
await sendRestoreCompleteEmail(row.requester_email, row.videoId, video?.title)
|
||
|
|
} finally {
|
||
|
|
if (fs.existsSync(filePath)) fs.unlinkSync(filePath)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function pollRestores() {
|
||
|
|
const restoringRows = await db.selectFrom('restore_requests')
|
||
|
|
.select(['uuid', 'videoId', 'requester_email'])
|
||
|
|
.where('status', '=', 'restoring')
|
||
|
|
.execute()
|
||
|
|
|
||
|
|
for (const row of restoringRows) {
|
||
|
|
try {
|
||
|
|
await processRestoringRow(row)
|
||
|
|
} catch (error: unknown) {
|
||
|
|
const err = error as Error
|
||
|
|
console.log(`[glacier-poller] failed to process restore ${row.uuid} (${row.videoId}): ${err.message}`)
|
||
|
|
await db.updateTable('restore_requests')
|
||
|
|
.set({ status: 'failed', error_message: err.message, updated_at: new Date() })
|
||
|
|
.where('uuid', '=', row.uuid)
|
||
|
|
.execute()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function startRestorePoller() {
|
||
|
|
pollRestores()
|
||
|
|
setInterval(pollRestores, POLL_INTERVAL_MS).unref()
|
||
|
|
console.log('glacier restore poller started (this server is primary)')
|
||
|
|
}
|
||
|
|
|
||
|
|
export { startRestorePoller, pollRestores }
|