From af600e3e4fa62cd0c03ab488c5cb3bc178f19d36 Mon Sep 17 00:00:00 2001 From: Junyi Date: Wed, 23 Oct 2024 00:03:45 +0800 Subject: [PATCH] fix(plugin-file-manager): fix upload and destroy file record within an association (#5493) --- .../core/client/src/modules/blocks/index.ts | 1 + .../src/client/hooks/useUploadFiles.ts | 15 +- .../src/server/__tests__/action.test.ts | 20 +++ .../src/server/__tests__/collections/files.ts | 148 ++++++++++++++++++ .../{tables => collections}/users.ts | 4 + .../src/server/__tests__/index.ts | 2 +- .../src/server/actions/attachments.ts | 4 +- 7 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/collections/files.ts rename packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/{tables => collections}/users.ts (92%) diff --git a/packages/core/client/src/modules/blocks/index.ts b/packages/core/client/src/modules/blocks/index.ts index 6266fcd2ee..5869ff15c3 100644 --- a/packages/core/client/src/modules/blocks/index.ts +++ b/packages/core/client/src/modules/blocks/index.ts @@ -10,3 +10,4 @@ export * from './data-blocks/details-multi'; export * from './data-blocks/details-single'; export * from './useActionAvailable'; +export * from './useSourceId'; diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useUploadFiles.ts b/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useUploadFiles.ts index 689f352d9a..76467da549 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useUploadFiles.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/client/hooks/useUploadFiles.ts @@ -12,26 +12,29 @@ import { useActionContext, useBlockRequestContext, useCollection, + useDataBlockProps, + useDataBlockRequest, + useSourceId, useSourceIdFromParentRecord, } from '@nocobase/client'; import { useContext, useMemo } from 'react'; import { useStorageRules } from './useStorageRules'; export const useUploadFiles = () => { - const { service } = useBlockRequestContext(); + const service = useDataBlockRequest(); + const { association } = useDataBlockProps(); const { setVisible } = useActionContext(); - const { props: blockProps } = useBlockRequestContext(); const collection = useCollection(); - const sourceId = useSourceIdFromParentRecord(); + const sourceId = useSourceId(); const rules = useStorageRules(collection?.getOption('storage')); const action = useMemo(() => { let action = `${collection.name}:create`; - if (blockProps?.association) { - const [s, t] = blockProps.association.split('.'); + if (association) { + const [s, t] = association.split('.'); action = `${s}/${sourceId}/${t}:create`; } return action; - }, [collection.name, blockProps?.association, sourceId]); + }, [collection.name, association, sourceId]); const { setSelectedRows } = useContext(RecordPickerContext) || {}; const uploadingFiles = {}; diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/action.test.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/action.test.ts index b458fa1619..21aed67841 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/action.test.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/action.test.ts @@ -457,6 +457,26 @@ describe('action', () => { }); }); + describe('association', () => { + it('has-many', async () => { + const UserRepo = db.getRepository('users'); + const user = await UserRepo.findOne(); + const FileRepo = db.getRepository('users.files', user.id); + const f1s = await FileRepo.count(); + expect(f1s).toBe(0); + const { body } = await agent.resource('users.files', 1).create({ + [FILE_FIELD_NAME]: path.resolve(__dirname, './files/text.txt'), + }); + const f2s = await FileRepo.find({}); + expect(f2s.length).toBe(1); + expect(f2s[0].userId).toBe(user.id); + + await agent.resource('users.files', 1).destroy({ filterByTk: body.data.id }); + const f3s = await FileRepo.count(); + expect(f3s).toBe(0); + }); + }); + describe('storage actions', () => { describe('getRules', () => { it('get rules without key as default storage', async () => { diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/collections/files.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/collections/files.ts new file mode 100644 index 0000000000..d240920e17 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/collections/files.ts @@ -0,0 +1,148 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +export default { + name: 'files', + createdBy: true, + updatedBy: true, + template: 'file', + fields: [ + { + interface: 'input', + type: 'string', + name: 'title', + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("Title")}}`, + 'x-component': 'Input', + }, + }, + // '系统文件名(含扩展名)', + { + interface: 'input', + type: 'string', + name: 'filename', + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("File name")}}`, + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + // '扩展名(含“.”)', + { + interface: 'input', + type: 'string', + name: 'extname', + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("Extension name")}}`, + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + // '文件体积(字节)', + { + interface: 'integer', + type: 'integer', + name: 'size', + deletable: false, + uiSchema: { + type: 'number', + title: `{{t("Size")}}`, + 'x-component': 'InputNumber', + 'x-read-pretty': true, + 'x-component-props': { + stringMode: true, + step: '0', + }, + }, + }, + { + interface: 'input', + type: 'string', + name: 'mimetype', + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("MIME type")}}`, + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + // '相对路径(含“/”前缀)', + { + interface: 'input', + type: 'string', + name: 'path', + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("Path")}}`, + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + // 文件的可访问地址 + { + interface: 'url', + type: 'string', + name: 'url', + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("URL")}}`, + 'x-component': 'Input.URL', + 'x-read-pretty': true, + }, + }, + // 用于预览 + { + interface: 'url', + type: 'string', + name: 'preview', + field: 'url', // 直接引用 url 字段 + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("Preview")}}`, + 'x-component': 'Preview', + 'x-read-pretty': true, + }, + }, + { + comment: '存储引擎', + type: 'belongsTo', + name: 'storage', + target: 'storages', + foreignKey: 'storageId', + deletable: false, + uiSchema: { + type: 'string', + title: `{{t("Storage")}}`, + 'x-component': 'Input', + 'x-read-pretty': true, + }, + }, + // '其他文件信息(如图片的宽高)', + { + type: 'jsonb', + name: 'meta', + deletable: false, + defaultValue: {}, + }, + { + type: 'belongsTo', + name: 'user', + }, + ], +}; diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/tables/users.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/collections/users.ts similarity index 92% rename from packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/tables/users.ts rename to packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/collections/users.ts index afdb4cfe71..197fdd068f 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/tables/users.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/collections/users.ts @@ -21,6 +21,10 @@ export default { name: 'avatar', target: 'attachments', }, + { + type: 'hasMany', + name: 'files', + }, { type: 'belongsToMany', name: 'pubkeys', diff --git a/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/index.ts b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/index.ts index 4f5c3b3bb9..67288ea0a7 100644 --- a/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/index.ts +++ b/packages/plugins/@nocobase/plugin-file-manager/src/server/__tests__/index.ts @@ -30,7 +30,7 @@ export async function getApp(options = {}): Promise { }); await app.db.import({ - directory: path.resolve(__dirname, './tables'), + directory: path.resolve(__dirname, './collections'), }); await app.db.sync(); 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 b757236179..a3925eb9cc 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 @@ -131,14 +131,14 @@ export async function createMiddleware(ctx: Context, next: Next) { } export async function destroyMiddleware(ctx: Context, next: Next) { - const { resourceName, actionName } = ctx.action; + 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); + const repository = ctx.db.getRepository(resourceName, sourceId); const { filterByTk, filter } = ctx.action.params;