From 55a7d9a828b3921bada214c9c95b113b04af4dae Mon Sep 17 00:00:00 2001 From: Junyi Date: Thu, 23 Jan 2025 22:27:11 +0800 Subject: [PATCH] refactor(plugin-file-manager): move destroy to collection event (#6127) --- .../src/server/actions/attachments.ts | 66 ------------------- .../src/server/actions/index.ts | 4 +- .../plugin-file-manager/src/server/server.ts | 36 ++++++++-- 3 files changed, 33 insertions(+), 73 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/attachments.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/attachments.ts index 1a97f9c961..c279cab5ea 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/attachments.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/attachments.ts @@ -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(); -} diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/index.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/index.ts index 487dc429b1..5d078ad08f 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/index.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/actions/index.ts @@ -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); } diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/server.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/server.ts index 32f9eda076..5ff1fe4ca0 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/server/server.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/server.ts @@ -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(); storagesCache = new Map(); + 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);