From 80f167ca02a6a6b228f41e233c6741312653ce6f Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 18 Sep 2024 18:11:43 +0800 Subject: [PATCH] file upload limit --- .../base/chat/chat/chat-input-area/index.tsx | 2 +- .../chat/chat/chat-input-area/operation.tsx | 2 +- .../base/file-uploader/constants.ts | 2 +- .../file-from-link-or-local/index.tsx | 14 +++++++++--- .../base/file-uploader/file-input.tsx | 22 +++++++++++++++++-- .../file-uploader-in-attachment/index.tsx | 22 ++++++++++++++----- .../file-uploader-in-chat-input/file-list.tsx | 10 +++++++-- .../file-uploader-in-chat-input/index.tsx | 9 +++++++- .../components/base/file-uploader/hooks.ts | 19 ++++++++++------ .../base/prompt-editor/constants.tsx | 2 +- .../components/before-run-form/form-item.tsx | 20 ++++++++++++----- .../_base/components/file-upload-setting.tsx | 4 +++- web/i18n/en-US/app-debug.ts | 2 +- web/i18n/en-US/common.ts | 3 +++ web/i18n/zh-Hans/common.ts | 3 +++ 15 files changed, 103 insertions(+), 33 deletions(-) diff --git a/web/app/components/base/chat/chat/chat-input-area/index.tsx b/web/app/components/base/chat/chat/chat-input-area/index.tsx index 7f6510665b..f109e4d3b2 100644 --- a/web/app/components/base/chat/chat/chat-input-area/index.tsx +++ b/web/app/components/base/chat/chat/chat-input-area/index.tsx @@ -125,7 +125,7 @@ const ChatInputArea = ({ )} >
- +
(({ ref={ref} >
- {visionConfig?.enabled && } + {visionConfig?.enabled && } { speechToTextConfig?.enabled && ( React.ReactNode + fileConfig: FileUpload } const FileFromLinkOrLocal = ({ showFromLink = true, showFromLocal = true, trigger, + fileConfig, }: FileFromLinkOrLocalProps) => { const { t } = useTranslation() + const files = useStore(s => s.files) const [open, setOpen] = useState(false) const [url, setUrl] = useState('') - const { handleLoadFileFromLink } = useFile() + const { handleLoadFileFromLink } = useFile(fileConfig) + const disabled = !!fileConfig.number_limits && files.length >= fileConfig.number_limits return ( setUrl(e.target.value)} + disabled={disabled} /> ) } diff --git a/web/app/components/base/file-uploader/file-input.tsx b/web/app/components/base/file-uploader/file-input.tsx index 42398a6a33..e981278c14 100644 --- a/web/app/components/base/file-uploader/file-input.tsx +++ b/web/app/components/base/file-uploader/file-input.tsx @@ -1,19 +1,37 @@ import { useFile } from './hooks' +import { useStore } from './store' +import type { FileUpload } from '@/app/components/base/features/types' +import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' +import { SupportUploadFileTypes } from '@/app/components/workflow/types' -const FileInput = () => { - const { handleLocalFileUpload } = useFile() +type FileInputProps = { + fileConfig: FileUpload +} +const FileInput = ({ + fileConfig, +}: FileInputProps) => { + const files = useStore(s => s.files) + const { handleLocalFileUpload } = useFile(fileConfig) const handleChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) handleLocalFileUpload(file) } + + const allowedFileTypes = fileConfig.allowed_file_types + const isCustom = allowedFileTypes?.includes(SupportUploadFileTypes.custom) + const exts = isCustom ? (fileConfig.allowed_file_extensions || []) : (allowedFileTypes?.map(type => FILE_EXTS[type]) || []).flat().map(item => `.${item}`) + const accept = exts.join(',') + return ( ((e.target as HTMLInputElement).value = '')} type='file' onChange={handleChange} + accept={accept} + disabled={!!(fileConfig.number_limits && files.length >= fileConfig?.number_limits)} /> ) } diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx index c451746f06..54182413f1 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx @@ -17,19 +17,25 @@ import { useFile } from '../hooks' import FileItem from './file-item' import Button from '@/app/components/base/button' import cn from '@/utils/classnames' +import type { FileUpload } from '@/app/components/base/features/types' type Option = { value: string label: string icon: JSX.Element } -const FileUploaderInAttachment = () => { +type FileUploaderInAttachmentProps = { + fileConfig: FileUpload +} +const FileUploaderInAttachment = ({ + fileConfig, +}: FileUploaderInAttachmentProps) => { const { t } = useTranslation() const files = useStore(s => s.files) const { handleRemoveFile, handleReUploadFile, - } = useFile() + } = useFile(fileConfig) const options = [ { value: 'local', @@ -49,17 +55,18 @@ const FileUploaderInAttachment = () => { key={option.value} variant='tertiary' className={cn('basis-1/2 relative', open && 'bg-components-button-tertiary-bg-hover')} + disabled={!!(fileConfig.number_limits && files.length >= fileConfig.number_limits)} > {option.icon} {option.label} { option.value === 'local' && ( - + ) } ) - }, []) + }, [fileConfig, files.length]) const renderTrigger = useCallback((option: Option) => { return (open: boolean) => renderButton(option, open) }, [renderButton]) @@ -73,10 +80,11 @@ const FileUploaderInAttachment = () => { key={option.value} showFromLocal={false} trigger={renderTrigger(option)} + fileConfig={fileConfig} /> ) } - }, [renderButton, renderTrigger]) + }, [renderButton, renderTrigger, fileConfig]) return (
@@ -106,13 +114,15 @@ const FileUploaderInAttachment = () => { type FileUploaderInAttachmentWrapperProps = { onChange: (files: FileEntity[]) => void + fileConfig: FileUpload } const FileUploaderInAttachmentWrapper = ({ onChange, + fileConfig, }: FileUploaderInAttachmentWrapperProps) => { return ( - + ) } diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx index 870de729dc..45ec54b560 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx @@ -3,13 +3,19 @@ import { useFile } from '../hooks' import { useStore } from '../store' import FileImageItem from './file-image-item' import FileItem from './file-item' +import type { FileUpload } from '@/app/components/base/features/types' -const FileList = () => { +type FileListProps = { + fileConfig: FileUpload +} +const FileList = ({ + fileConfig, +}: FileListProps) => { const files = useStore(s => s.files) const { handleRemoveFile, handleReUploadFile, - } = useFile() + } = useFile(fileConfig) return (
diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx index 0380b60d87..6b8c4efce3 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx @@ -8,8 +8,14 @@ import { import FileFromLinkOrLocal from '../file-from-link-or-local' import ActionButton from '@/app/components/base/action-button' import cn from '@/utils/classnames' +import type { FileUpload } from '@/app/components/base/features/types' -const FileUploaderInChatInput = () => { +type FileUploaderInChatInputProps = { + fileConfig: FileUpload +} +const FileUploaderInChatInput = ({ + fileConfig, +}: FileUploaderInChatInputProps) => { const renderTrigger = useCallback((open: boolean) => { return ( { return ( ) } diff --git a/web/app/components/base/file-uploader/hooks.ts b/web/app/components/base/file-uploader/hooks.ts index 5558fcf8e5..3ef20f1668 100644 --- a/web/app/components/base/file-uploader/hooks.ts +++ b/web/app/components/base/file-uploader/hooks.ts @@ -13,17 +13,18 @@ import { fileUpload, getFileType, } from './utils' +import { FILE_SIZE_LIMIT } from './constants' import { useToastContext } from '@/app/components/base/toast' import { TransferMethod } from '@/types/app' -import { useFeaturesStore } from '@/app/components/base/features/hooks' import { SupportUploadFileTypes } from '@/app/components/workflow/types' +import type { FileUpload } from '@/app/components/base/features/types' +import { formatFileSize } from '@/utils/format' -export const useFile = () => { +export const useFile = (fileConfig: FileUpload) => { const { t } = useTranslation() const { notify } = useToastContext() const fileStore = useFileStore() const params = useParams() - const featuresStore = useFeaturesStore() const handleAddOrUpdateFiles = useCallback((newFile: FileEntity) => { const { @@ -95,9 +96,13 @@ export const useFile = () => { }, [fileStore]) const handleLocalFileUpload = useCallback((file: File) => { + if (file.size > FILE_SIZE_LIMIT) { + notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerLimit', { size: formatFileSize(FILE_SIZE_LIMIT) }) }) + return + } const reader = new FileReader() const isImage = file.type.startsWith('image') - const allowedFileTypes = featuresStore?.getState().features.file?.allowed_file_types + const allowedFileTypes = fileConfig.allowed_file_types const isCustomFileType = allowedFileTypes?.includes(SupportUploadFileTypes.custom) reader.addEventListener( @@ -122,7 +127,7 @@ export const useFile = () => { handleAddOrUpdateFiles({ ...uploadingFile, fileStorageId: res.id, progress: 100 }) }, onErrorCallback: () => { - notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') }) + notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerUploadError') }) handleAddOrUpdateFiles({ ...uploadingFile, progress: -1 }) }, }, !!params.token) @@ -132,12 +137,12 @@ export const useFile = () => { reader.addEventListener( 'error', () => { - notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerReadError') }) + notify({ type: 'error', message: t('common.fileUploader.uploadFromComputerReadError') }) }, false, ) reader.readAsDataURL(file) - }, [notify, t, handleAddOrUpdateFiles, params.token]) + }, [notify, t, handleAddOrUpdateFiles, params.token, fileConfig?.allowed_file_types]) const handleClipboardPasteFile = useCallback((e: ClipboardEvent) => { const file = e.clipboardData?.files[0] diff --git a/web/app/components/base/prompt-editor/constants.tsx b/web/app/components/base/prompt-editor/constants.tsx index 0caf45ee7d..4740042135 100644 --- a/web/app/components/base/prompt-editor/constants.tsx +++ b/web/app/components/base/prompt-editor/constants.tsx @@ -50,7 +50,7 @@ export const getInputVars = (text: string): ValueSelector[] => { return [] } -export const FILE_EXTS = { +export const FILE_EXTS: Record = { [SupportUploadFileTypes.image]: ['JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'], [SupportUploadFileTypes.document]: ['TXT', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'], [SupportUploadFileTypes.audio]: ['MP3', 'M4A', 'WAV', 'WEBM', 'AMR'], diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx index 8af5acc6d4..0985ad4a81 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx @@ -159,12 +159,20 @@ const FormItem: FC = ({ {/* #TODO# file upload */} {(type === InputVarType.singleFile || type === InputVarType.multiFiles) && ( - onChange(files.filter(file => file.progress !== -1).map(fileItem => ({ - type: fileItem.fileType, - transfer_method: fileItem.type, - url: fileItem.url, - upload_file_id: fileItem.fileId, - })))} /> + onChange(files.filter(file => file.progress !== -1).map(fileItem => ({ + type: fileItem.fileType, + transfer_method: fileItem.type, + url: fileItem.url, + upload_file_id: fileItem.fileId, + })))} + fileConfig={{ + allowed_file_types: payload.allowed_file_types, + allowed_file_extensions: payload.allowed_file_extensions, + allowed_file_upload_methods: payload.allowed_file_upload_methods, + number_limits: payload.max_length, + }} + /> )} { type === InputVarType.files && ( diff --git a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx index 21f33aad0a..df7a8f21d8 100644 --- a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx +++ b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx @@ -10,6 +10,8 @@ import FileTypeItem from './file-type-item' import InputNumberWithSlider from './input-number-with-slider' import Field from '@/app/components/app/configuration/config-var/config-modal/field' import { TransferMethod } from '@/types/app' +import { FILE_SIZE_LIMIT } from '@/app/components/base/file-uploader/constants' +import { formatFileSize } from '@/utils/format' type Props = { payload: UploadFileSetting @@ -138,7 +140,7 @@ const FileUploadSetting: FC = ({ title={t('appDebug.variableConfig.maxNumberOfUploads')!} >
-
{t('appDebug.variableConfig.maxNumberTip')}
+
{t('appDebug.variableConfig.maxNumberTip', { size: formatFileSize(FILE_SIZE_LIMIT) })}