Improved media cleanup task

This commit is contained in:
Laura Hausmann 2023-08-01 23:02:11 +02:00
parent 8404480e57
commit 079942eda4
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 71 additions and 25 deletions

View file

@ -190,6 +190,13 @@ reservedUsernames: [
# Proxy remote files (default: false) # Proxy remote files (default: false)
#proxyRemoteFiles: true #proxyRemoteFiles: true
# Media cleanup settings (defaults: false, 0, false, false)
#mediaCleanup:
# cron: true
# maxAgeDays: 30
# cleanAvatars: false
# cleanHeaders: false
#allowedPrivateNetworks: [ #allowedPrivateNetworks: [
# '127.0.0.1/32' # '127.0.0.1/32'
#] #]

View file

@ -59,6 +59,13 @@ export type Source = {
ssl: boolean; ssl: boolean;
}; };
mediaCleanup?: {
cron?: boolean;
maxAgeDays?: number;
keepAvatars?: boolean;
keepHeaders?: boolean;
};
proxy?: string; proxy?: string;
proxySmtp?: string; proxySmtp?: string;
proxyBypassHosts?: string[]; proxyBypassHosts?: string[];

View file

@ -522,6 +522,21 @@ export default function () {
endedPollNotificationQueue.process(endedPollNotification); endedPollNotificationQueue.process(endedPollNotification);
webhookDeliverQueue.process(64, processWebhookDeliver); webhookDeliverQueue.process(64, processWebhookDeliver);
processDb(dbQueue); processDb(dbQueue);
if (config.mediaCleanup?.cron) {
objectStorageQueue.add(
"cleanRemoteFiles",
{},
{
repeat: {
cron: "0 0 * * *",
},
removeOnComplete: true,
removeOnFail: true,
},
);
}
processObjectStorage(objectStorageQueue); processObjectStorage(objectStorageQueue);
processBackground(backgroundQueue); processBackground(backgroundQueue);

View file

@ -2,8 +2,10 @@ import type Bull from "bull";
import { queueLogger } from "../../logger.js"; import { queueLogger } from "../../logger.js";
import { deleteFileSync } from "@/services/drive/delete-file.js"; import { deleteFileSync } from "@/services/drive/delete-file.js";
import { DriveFiles } from "@/models/index.js"; import { DriveFiles, Users } from "@/models/index.js";
import { MoreThan, Not, IsNull } from "typeorm"; import { MoreThan, Not, IsNull } from "typeorm";
import { User } from "@/models/entities/user.js";
import config from "@/config/index.js";
const logger = queueLogger.createSubLogger("clean-remote-files"); const logger = queueLogger.createSubLogger("clean-remote-files");
@ -11,43 +13,58 @@ export default async function cleanRemoteFiles(
job: Bull.Job<Record<string, unknown>>, job: Bull.Job<Record<string, unknown>>,
done: any, done: any,
): Promise<void> { ): Promise<void> {
logger.info("Deleting cached remote files..."); let progress = 0;
let until = new Date();
until.setDate(until.getDate() - (config.mediaCleanup?.maxAgeDays ?? 0));
const avatars = config.mediaCleanup?.avatars ?? false;
const headers = config.mediaCleanup?.headers ?? false;
let deletedCount = 0; until = until.toISOString().replace("T", " ").replace("Z", "");
let cursor: any = null;
let target = "files";
if (avatars)
if (headers) target += ", avatars & headers";
else target += " & avatars";
else if (headers) target += " & headers";
logger.info(`Deleting cached remote ${target} created before ${until}...`);
let query = DriveFiles.createQueryBuilder("file")
.where(`file.isLink = FALSE`)
.andWhere(`file.userHost IS NOT NULL`)
.andWhere("file.createdAt < :until", { until });
if (!avatars || !headers) {
query = query.andWhere((qb) => {
let sq = qb.subQuery().from(User, "user");
if (!avatars) sq = sq.where("file.id = user.avatarId");
if (!headers) sq = sq.orWhere("file.id = user.bannerId");
return `NOT EXISTS ${sq.getQuery()}`;
});
}
query = query.take(8);
const total = await query.getCount();
logger.info(`Deleting ${total} files, please wait...`);
while (true) { while (true) {
const files = await DriveFiles.find({ const files = await query.getMany();
where: {
userHost: Not(IsNull()),
isLink: false,
...(cursor ? { id: MoreThan(cursor) } : {}),
},
take: 8,
order: {
id: 1,
},
});
if (files.length === 0) { if (files.length === 0) {
job.progress(100); job.progress(100);
break; break;
} }
cursor = files[files.length - 1].id;
await Promise.all(files.map((file) => deleteFileSync(file, true))); await Promise.all(files.map((file) => deleteFileSync(file, true)));
deletedCount += 8; progress += files.length;
const total = await DriveFiles.countBy({ job.progress((progress / total) * 100);
userHost: Not(IsNull()),
isLink: false,
});
job.progress(deletedCount / total);
} }
logger.succ("All cahced remote files has been deleted."); logger.succ(`Remote media cleanup job completed successfully.`);
done(); done();
} }