refactor(plugin-file-manager): move destroy to collection event (#6127)

This commit is contained in:
Junyi 2025-01-23 22:27:11 +08:00 committed by GitHub
parent 19ffa45ee9
commit 55a7d9a828
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 73 deletions

View File

@ -129,69 +129,3 @@ export async function createMiddleware(ctx: Context, next: Next) {
await multipart(ctx, next);
}
export async function destroyMiddleware(ctx: Context, next: Next) {
const { resourceName, actionName, sourceId } = ctx.action;
const collection = ctx.db.getCollection(resourceName);
if (collection?.options?.template !== 'file' || actionName !== 'destroy') {
return next();
}
const repository = ctx.db.getRepository(resourceName, sourceId);
const { filterByTk, filter } = ctx.action.params;
const records = await repository.find({
filterByTk,
filter,
context: ctx,
});
const storageIds = new Set(records.map((record) => record.storageId));
const storageGroupedRecords = records.reduce((result, record) => {
const storageId = record.storageId;
if (!result[storageId]) {
result[storageId] = [];
}
result[storageId].push(record);
return result;
}, {});
const storages = await ctx.db.getRepository('storages').find({
filter: {
id: [...storageIds] as any[],
paranoid: {
$ne: true,
},
},
});
let count = 0;
const undeleted = [];
await storages.reduce(
(promise, storage) =>
promise.then(async () => {
const storageConfig = ctx.app.pm.get(Plugin).storageTypes.get(storage.type);
const result = await storageConfig.delete(storage, storageGroupedRecords[storage.id]);
count += result[0];
undeleted.push(...result[1]);
}),
Promise.resolve(),
);
if (undeleted.length) {
const ids = undeleted.map((record) => record.id);
ctx.action.mergeParams({
filter: {
id: {
$notIn: ids,
},
},
});
ctx.logger.error('[file-manager] some of attachment files are not successfully deleted: ', { ids });
}
await next();
}

View File

@ -8,7 +8,7 @@
*/
import actions from '@nocobase/actions';
import { createMiddleware, destroyMiddleware } from './attachments';
import { createMiddleware } from './attachments';
import * as storageActions from './storages';
export default function ({ app }) {
@ -18,6 +18,4 @@ export default function ({ app }) {
});
app.resourcer.use(createMiddleware, { tag: 'createMiddleware', after: 'auth' });
app.resourcer.registerActionHandler('upload', actions.create);
app.resourcer.use(destroyMiddleware);
}

View File

@ -12,14 +12,14 @@ import { Registry } from '@nocobase/utils';
import { basename, resolve } from 'path';
import { Transactionable } from '@nocobase/database';
import { Model, Transactionable } from '@nocobase/database';
import fs from 'fs';
import { STORAGE_TYPE_ALI_OSS, STORAGE_TYPE_LOCAL, STORAGE_TYPE_S3, STORAGE_TYPE_TX_COS } from '../constants';
import { FileModel } from './FileModel';
import initActions from './actions';
import { getFileData } from './actions/attachments';
import { AttachmentInterface } from './interfaces/attachment-interface';
import { IStorage, StorageModel } from './storages';
import { AttachmentModel, IStorage, StorageModel } from './storages';
import StorageTypeAliOss from './storages/ali-oss';
import StorageTypeLocal from './storages/local';
import StorageTypeS3 from './storages/s3';
@ -29,6 +29,16 @@ export type * from './storages';
const DEFAULT_STORAGE_TYPE = STORAGE_TYPE_LOCAL;
class FileDeleteError extends Error {
data: Model;
constructor(message: string, data: Model) {
super(message);
this.name = 'FileDeleteError';
this.data = data;
}
}
export type FileRecordOptions = {
collectionName: string;
filePath: string;
@ -46,6 +56,23 @@ export class PluginFileManagerServer extends Plugin {
storageTypes = new Registry<IStorage>();
storagesCache = new Map<number, StorageModel>();
afterDestroy = async (record: Model, options) => {
const { collection } = record.constructor as typeof Model;
if (collection?.options?.template !== 'file' && collection.name !== 'attachments') {
return;
}
const storage = this.storagesCache.get(record.get('storageId'));
if (storage?.paranoid) {
return;
}
const storageConfig = this.storageTypes.get(storage.type);
const result = await storageConfig.delete(storage, [record as unknown as AttachmentModel]);
if (!result[0]) {
throw new FileDeleteError('Failed to delete file', record);
}
};
registerStorageType(type: string, options: IStorage) {
this.storageTypes.register(type, options);
}
@ -184,6 +211,8 @@ export class PluginFileManagerServer extends Plugin {
}
async load() {
this.db.on('afterDestroy', this.afterDestroy);
this.storageTypes.register(STORAGE_TYPE_LOCAL, new StorageTypeLocal());
this.storageTypes.register(STORAGE_TYPE_ALI_OSS, new StorageTypeAliOss());
this.storageTypes.register(STORAGE_TYPE_S3, new StorageTypeS3());
@ -224,8 +253,7 @@ export class PluginFileManagerServer extends Plugin {
initActions(this);
this.app.acl.allow('attachments', 'upload', 'loggedIn');
this.app.acl.allow('attachments', 'create', 'loggedIn');
this.app.acl.allow('attachments', ['upload', 'create', 'destroy'], 'loggedIn');
this.app.acl.allow('storages', 'getRules', 'loggedIn');
// this.app.resourcer.use(uploadMiddleware);