core: fix deletion of referenced files

This commit is contained in:
undefined committed 2024-10-03 13:45:54 +08:00
1 parent 856f586d65
commit 57b708b99e
1 file changed
+30 -14
+30 -14
View File
@@ -4,6 +4,7 @@ import moment from 'moment-timezone';
import { nanoid } from 'nanoid';
import type { Readable } from 'stream';
import { Context } from '../context';
import { FileNode } from '../interface';
import mime from '../lib/mime';
import db from '../service/db';
import storage from '../service/storage';
@@ -49,25 +50,40 @@ export class StorageModel {
);
}
private static async _swapId(fileA: FileNode, fileB: FileNode) {
// try to swap the two file with same content to make sure the first file is no longer being referenced
// Example
// _id: A, path: C, meta: R
// _id: B, path: D, link: A, meta: P
// and we want to delete path=B, the result should be
// _id: A, path: D, meta: P
// _id: B, path: C, link: A, meta: R
await Promise.all([
StorageModel.coll.updateOne({ _id: fileA._id }, { $set: omit(fileB, ['_id', 'link']) }),
StorageModel.coll.updateOne({ _id: fileB._id }, { $set: omit(fileA, ['_id', 'link']) }),
]);
}
static async del(path: string[], operator = 1) {
if (!path.length) return;
const affected = await StorageModel.coll.find({ path: { $in: path } }).toArray();
if (!affected.length) return;
const linked = await StorageModel.coll.find({ link: { $in: affected.map((i) => i._id) }, path: { $nin: path } }).toArray();
const processedIds = [];
for (const i of linked || []) {
if (processedIds.includes(i.link)) continue;
const current = affected.find((t) => t._id === i.link); // to be deleted
// eslint-disable-next-line no-await-in-loop
await Promise.all([
StorageModel.coll.updateOne({ _id: current._id }, { $set: omit(i, ['_id']) }),
StorageModel.coll.updateOne({ _id: i._id }, { $set: omit(current, ['_id']), $unset: { link: '' } }),
]);
processedIds.push(i.link);
// files pending to be deleted
const pendingDelete = await StorageModel.coll.find({ path: { $in: path }, autoDelete: null }).toArray();
if (!pendingDelete.length) return;
// all files not going to be deleted but affected (referencing to the files to be deleted)
const linked = await StorageModel.coll.find({ link: { $in: pendingDelete.map((i) => i._id) }, path: { $nin: path } }).toArray();
const fileIds = [];
for (const i of pendingDelete) {
const affected = linked.filter((t) => t.link === i._id);
if (!affected.length) {
fileIds.push(i._id);
continue;
}
await StorageModel._swapId(i, affected[0]); // eslint-disable-line no-await-in-loop
fileIds.push(affected[0]._id); // we already swapped the two files, so we need to delete the other one
}
const autoDelete = moment().add(7, 'day').toDate();
await StorageModel.coll.updateMany(
{ path: { $in: path }, autoDelete: null },
{ _id: { $in: fileIds } },
{ $set: { autoDelete }, $push: { operator } },
);
}