mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
fix(attachment-url): only allow file collections with public URL access (#6664)
* refactor: isPublicAccessStorage * fix: bug * fix: bug * fix: only allow file collections with public URL access * fix: build * fix: test
This commit is contained in:
parent
3e2abe3d3c
commit
f99cdb5f02
@ -54,7 +54,7 @@ describe('CollectionSelect', () => {
|
||||
role="button"
|
||||
>
|
||||
<div
|
||||
class="css-9mlexe ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
||||
class="css-a7w9kk ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
||||
>
|
||||
<div
|
||||
class="ant-formily-item-label"
|
||||
@ -195,7 +195,7 @@ describe('CollectionSelect', () => {
|
||||
role="button"
|
||||
>
|
||||
<div
|
||||
class="css-9mlexe ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
||||
class="css-a7w9kk ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
||||
>
|
||||
<div
|
||||
class="ant-formily-item-label"
|
||||
|
@ -48,6 +48,7 @@ export type RemoteSelectProps<P = any> = SelectProps<P, any> & {
|
||||
CustomDropdownRender?: (v: any) => any;
|
||||
optionFilter?: (option: any) => boolean;
|
||||
toOptionsItem?: (data) => any;
|
||||
onSuccess?: (data) => any;
|
||||
};
|
||||
|
||||
const InternalRemoteSelect = withDynamicSchemaProps(
|
||||
@ -68,6 +69,7 @@ const InternalRemoteSelect = withDynamicSchemaProps(
|
||||
dataSource: propsDataSource,
|
||||
toOptionsItem = (value) => value,
|
||||
popupMatchSelectWidth = false,
|
||||
onSuccess,
|
||||
...others
|
||||
} = props;
|
||||
const dataSource = useDataSourceKey();
|
||||
@ -178,6 +180,7 @@ const InternalRemoteSelect = withDynamicSchemaProps(
|
||||
{
|
||||
manual,
|
||||
debounceWait: wait,
|
||||
onSuccess,
|
||||
...(service.defaultParams ? { defaultParams: [service.defaultParams] } : {}),
|
||||
},
|
||||
);
|
||||
|
@ -113,7 +113,6 @@ const InnerAttachmentUrl = (props) => {
|
||||
if (underFilter) {
|
||||
return <Input {...props} />;
|
||||
}
|
||||
console.log(collectionField);
|
||||
return (
|
||||
<div style={{ width: '100%', overflow: 'auto' }}>
|
||||
<AssociationField.FileSelector
|
||||
|
@ -7,7 +7,7 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { useFieldSchema } from '@formily/react';
|
||||
import { useFieldSchema, useField } from '@formily/react';
|
||||
import { useCollectionField, useDesignable, useRequest } from '@nocobase/client';
|
||||
import { cloneDeep, uniqBy } from 'lodash';
|
||||
import { useCallback } from 'react';
|
||||
@ -63,15 +63,11 @@ export const useInsertSchema = (component) => {
|
||||
|
||||
export const useAttachmentTargetProps = () => {
|
||||
const { t } = useTranslation();
|
||||
// TODO(refactor): whitelist should be changed to storage property,url is signed by plugin-s3-pro, this enmus is from plugin-file-manager
|
||||
const buildInStorage = ['local', 'ali-oss', 's3', 'tx-cos'];
|
||||
const field = useField();
|
||||
return {
|
||||
service: {
|
||||
resource: 'collections',
|
||||
resource: 'collections:listFileCollectionsWithPublicStorage',
|
||||
params: {
|
||||
filter: {
|
||||
'options.template': 'file',
|
||||
},
|
||||
paginate: false,
|
||||
},
|
||||
},
|
||||
@ -80,27 +76,9 @@ export const useAttachmentTargetProps = () => {
|
||||
label: 'title',
|
||||
value: 'name',
|
||||
},
|
||||
mapOptions: (value) => {
|
||||
if (value.name === 'attachments') {
|
||||
return {
|
||||
...value,
|
||||
title: t('Attachments'),
|
||||
};
|
||||
}
|
||||
return value;
|
||||
},
|
||||
toOptionsItem: (data) => {
|
||||
data.unshift({
|
||||
name: 'attachments',
|
||||
title: t('Attachments'),
|
||||
});
|
||||
return uniqBy(
|
||||
data.filter((v) => v.name),
|
||||
'name',
|
||||
);
|
||||
},
|
||||
optionFilter: (option) => {
|
||||
return !option.storage || buildInStorage.includes(option.storage);
|
||||
onSuccess: (data) => {
|
||||
field.data = field.data || {};
|
||||
field.data.options = data?.data;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -51,12 +51,18 @@ export class AttachmentURLFieldInterface extends CollectionFieldInterface {
|
||||
...defaultProps,
|
||||
target: {
|
||||
required: true,
|
||||
default: 'attachments',
|
||||
type: 'string',
|
||||
title: tStr('Which file collection should it be uploaded to'),
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-use-component-props': useAttachmentTargetProps,
|
||||
'x-reactions': (field) => {
|
||||
const options = field.data?.options || [];
|
||||
const hasAttachments = options.some((opt) => opt?.name === 'attachments');
|
||||
if (hasAttachments) {
|
||||
!field.initialValue && field.setInitialValue('attachments');
|
||||
}
|
||||
},
|
||||
},
|
||||
targetKey: {
|
||||
'x-hidden': true,
|
||||
|
@ -8,13 +8,50 @@
|
||||
*/
|
||||
|
||||
import { Plugin } from '@nocobase/server';
|
||||
import PluginFileManagerServer from '@nocobase/plugin-file-manager';
|
||||
|
||||
export class PluginFieldAttachmentUrlServer extends Plugin {
|
||||
async afterAdd() {}
|
||||
|
||||
async beforeLoad() {}
|
||||
|
||||
async load() {}
|
||||
async load() {
|
||||
this.app.resourceManager.registerActionHandlers({
|
||||
'collections:listFileCollectionsWithPublicStorage': async (ctx, next) => {
|
||||
const fileCollections = await this.db.getRepository('collections').find({
|
||||
filter: {
|
||||
'options.template': 'file',
|
||||
},
|
||||
});
|
||||
|
||||
const filePlugin = this.pm.get('file-manager') as PluginFileManagerServer | any;
|
||||
|
||||
const options = [];
|
||||
|
||||
const fileCollection = this.db.getCollection('attachments');
|
||||
|
||||
if (await filePlugin.isPublicAccessStorage(fileCollection?.options?.storage)) {
|
||||
options.push({
|
||||
title: '{{t("Attachments")}}',
|
||||
name: 'attachments',
|
||||
});
|
||||
}
|
||||
|
||||
for (const fileCollection of fileCollections) {
|
||||
if (await filePlugin.isPublicAccessStorage(fileCollection?.options?.storage)) {
|
||||
options.push({
|
||||
name: fileCollection.name,
|
||||
title: fileCollection.title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ctx.body = options;
|
||||
|
||||
await next();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async install() {}
|
||||
|
||||
|
@ -313,6 +313,27 @@ export class PluginFileManagerServer extends Plugin {
|
||||
const storageType = this.storageTypes.get(storage.type);
|
||||
return new storageType(storage).getFileURL(file, preview ? storage.options.thumbnailRule : '');
|
||||
}
|
||||
async isPublicAccessStorage(storageName) {
|
||||
const storageRepository = this.db.getRepository('storages');
|
||||
const storages = await storageRepository.findOne({
|
||||
filter: { default: true },
|
||||
});
|
||||
let storage;
|
||||
if (!storageName) {
|
||||
storage = storages;
|
||||
} else {
|
||||
storage = await storageRepository.findOne({
|
||||
filter: {
|
||||
name: storageName,
|
||||
},
|
||||
});
|
||||
}
|
||||
storage = this.parseStorage(storage);
|
||||
if (['local', 'ali-oss', 's3', 'tx-cos'].includes(storage.type)) {
|
||||
return true;
|
||||
}
|
||||
return !!storage.options?.public;
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginFileManagerServer;
|
||||
|
Loading…
x
Reference in New Issue
Block a user