fix: Add dataset file upload restrictions (#29397)

Co-authored-by: kurokobo <kuro664@gmail.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
This commit is contained in:
Wu Tianwei
2025-12-10 16:41:05 +08:00
committed by GitHub
parent 88b20bc6d0
commit bafd093fa9
35 changed files with 206 additions and 151 deletions

View File

@@ -10,14 +10,12 @@ import Trigger from './trigger'
import List from './list'
export type CredentialSelectorProps = {
pluginName: string
currentCredentialId: string
onCredentialChange: (credentialId: string) => void
credentials: Array<DataSourceCredential>
}
const CredentialSelector = ({
pluginName,
currentCredentialId,
onCredentialChange,
credentials,
@@ -50,7 +48,6 @@ const CredentialSelector = ({
<PortalToFollowElemTrigger onClick={toggle} className='grow overflow-hidden'>
<Trigger
currentCredential={currentCredential}
pluginName={pluginName}
isOpen={open}
/>
</PortalToFollowElemTrigger>
@@ -58,7 +55,6 @@ const CredentialSelector = ({
<List
currentCredentialId={currentCredentialId}
credentials={credentials}
pluginName={pluginName}
onCredentialChange={handleCredentialChange}
/>
</PortalToFollowElemContent>

View File

@@ -2,22 +2,18 @@ import { CredentialIcon } from '@/app/components/datasets/common/credential-icon
import type { DataSourceCredential } from '@/types/pipeline'
import { RiCheckLine } from '@remixicon/react'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
type ItemProps = {
credential: DataSourceCredential
pluginName: string
isSelected: boolean
onCredentialChange: (credentialId: string) => void
}
const Item = ({
credential,
pluginName,
isSelected,
onCredentialChange,
}: ItemProps) => {
const { t } = useTranslation()
const { avatar_url, name } = credential
const handleCredentialChange = useCallback(() => {
@@ -30,15 +26,12 @@ const Item = ({
onClick={handleCredentialChange}
>
<CredentialIcon
avatar_url={avatar_url}
avatarUrl={avatar_url}
name={name}
size={20}
/>
<span className='system-sm-medium grow truncate text-text-secondary'>
{t('datasetPipeline.credentialSelector.name', {
credentialName: name,
pluginName,
})}
{name}
</span>
{
isSelected && (

View File

@@ -5,14 +5,12 @@ import Item from './item'
type ListProps = {
currentCredentialId: string
credentials: Array<DataSourceCredential>
pluginName: string
onCredentialChange: (credentialId: string) => void
}
const List = ({
currentCredentialId,
credentials,
pluginName,
onCredentialChange,
}: ListProps) => {
return (
@@ -24,7 +22,6 @@ const List = ({
<Item
key={credential.id}
credential={credential}
pluginName={pluginName}
isSelected={isSelected}
onCredentialChange={onCredentialChange}
/>

View File

@@ -1,23 +1,18 @@
import React from 'react'
import type { DataSourceCredential } from '@/types/pipeline'
import { useTranslation } from 'react-i18next'
import { RiArrowDownSLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import { CredentialIcon } from '@/app/components/datasets/common/credential-icon'
type TriggerProps = {
currentCredential: DataSourceCredential | undefined
pluginName: string
isOpen: boolean
}
const Trigger = ({
currentCredential,
pluginName,
isOpen,
}: TriggerProps) => {
const { t } = useTranslation()
const {
avatar_url,
name = '',
@@ -31,16 +26,13 @@ const Trigger = ({
)}
>
<CredentialIcon
avatar_url={avatar_url}
avatarUrl={avatar_url}
name={name}
size={20}
/>
<div className='flex grow items-center gap-x-1 overflow-hidden'>
<span className='system-md-semibold grow truncate text-text-secondary'>
{t('datasetPipeline.credentialSelector.name', {
credentialName: name,
pluginName,
})}
{name}
</span>
<RiArrowDownSLine className='size-4 shrink-0 text-text-secondary' />
</div>

View File

@@ -11,12 +11,14 @@ type HeaderProps = {
docTitle: string
docLink: string
onClickConfiguration?: () => void
pluginName: string
} & CredentialSelectorProps
const Header = ({
docTitle,
docLink,
onClickConfiguration,
pluginName,
...rest
}: HeaderProps) => {
const { t } = useTranslation()
@@ -29,7 +31,7 @@ const Header = ({
/>
<Divider type='vertical' className='mx-1 h-3.5 shrink-0' />
<Tooltip
popupContent={t('datasetPipeline.configurationTip', { pluginName: rest.pluginName })}
popupContent={t('datasetPipeline.configurationTip', { pluginName })}
position='top'
>
<Button

View File

@@ -23,12 +23,12 @@ const SimplePieChart = dynamic(() => import('@/app/components/base/simple-pie-ch
export type LocalFileProps = {
allowedExtensions: string[]
notSupportBatchUpload?: boolean
supportBatchUpload?: boolean
}
const LocalFile = ({
allowedExtensions,
notSupportBatchUpload,
supportBatchUpload = false,
}: LocalFileProps) => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
@@ -42,7 +42,7 @@ const LocalFile = ({
const fileUploader = useRef<HTMLInputElement>(null)
const fileListRef = useRef<FileItem[]>([])
const hideUpload = notSupportBatchUpload && localFileList.length > 0
const hideUpload = !supportBatchUpload && localFileList.length > 0
const { data: fileUploadConfigResponse } = useFileUploadConfig()
const supportTypesShowNames = useMemo(() => {
@@ -64,9 +64,9 @@ const LocalFile = ({
const ACCEPTS = allowedExtensions.map((ext: string) => `.${ext}`)
const fileUploadConfig = useMemo(() => ({
file_size_limit: fileUploadConfigResponse?.file_size_limit ?? 15,
batch_count_limit: fileUploadConfigResponse?.batch_count_limit ?? 5,
file_upload_limit: fileUploadConfigResponse?.file_upload_limit ?? 5,
}), [fileUploadConfigResponse])
batch_count_limit: supportBatchUpload ? (fileUploadConfigResponse?.batch_count_limit ?? 5) : 1,
file_upload_limit: supportBatchUpload ? (fileUploadConfigResponse?.file_upload_limit ?? 5) : 1,
}), [fileUploadConfigResponse, supportBatchUpload])
const updateFile = useCallback((fileItem: FileItem, progress: number, list: FileItem[]) => {
const { setLocalFileList } = dataSourceStore.getState()
@@ -119,7 +119,7 @@ const LocalFile = ({
notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.size', { size: fileUploadConfig.file_size_limit }) })
return isValidType && isValidSize
}, [fileUploadConfig, notify, t, ACCEPTS])
}, [notify, t, ACCEPTS, fileUploadConfig.file_size_limit])
type UploadResult = Awaited<ReturnType<typeof upload>>
@@ -230,12 +230,12 @@ const LocalFile = ({
return
let files = [...e.dataTransfer.files] as File[]
if (notSupportBatchUpload)
if (!supportBatchUpload)
files = files.slice(0, 1)
const validFiles = files.filter(isValid)
initialUpload(validFiles)
}, [initialUpload, isValid, notSupportBatchUpload])
}, [initialUpload, isValid, supportBatchUpload])
const selectHandle = useCallback(() => {
if (fileUploader.current)
@@ -280,7 +280,7 @@ const LocalFile = ({
id='fileUploader'
className='hidden'
type='file'
multiple={!notSupportBatchUpload}
multiple={supportBatchUpload}
accept={ACCEPTS.join(',')}
onChange={fileChangeHandle}
/>
@@ -296,7 +296,7 @@ const LocalFile = ({
<RiUploadCloud2Line className='mr-2 size-5' />
<span>
{notSupportBatchUpload ? t('datasetCreation.stepOne.uploader.buttonSingleFile') : t('datasetCreation.stepOne.uploader.button')}
{supportBatchUpload ? t('datasetCreation.stepOne.uploader.button') : t('datasetCreation.stepOne.uploader.buttonSingleFile')}
{allowedExtensions.length > 0 && (
<label className='ml-1 cursor-pointer text-text-accent' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
)}
@@ -305,7 +305,7 @@ const LocalFile = ({
<div>{t('datasetCreation.stepOne.uploader.tip', {
size: fileUploadConfig.file_size_limit,
supportTypes: supportTypesShowNames,
batchCount: notSupportBatchUpload ? 1 : fileUploadConfig.batch_count_limit,
batchCount: fileUploadConfig.batch_count_limit,
totalCount: fileUploadConfig.file_upload_limit,
})}</div>
{dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}

View File

@@ -19,16 +19,18 @@ import { useDocLink } from '@/context/i18n'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
type OnlineDocumentsProps = {
isInPipeline?: boolean
nodeId: string
nodeData: DataSourceNodeType
onCredentialChange: (credentialId: string) => void
isInPipeline?: boolean
supportBatchUpload?: boolean
}
const OnlineDocuments = ({
nodeId,
nodeData,
isInPipeline = false,
supportBatchUpload = false,
onCredentialChange,
}: OnlineDocumentsProps) => {
const docLink = useDocLink()
@@ -157,7 +159,7 @@ const OnlineDocuments = ({
onSelect={handleSelectPages}
canPreview={!isInPipeline}
onPreview={handlePreviewPage}
isMultipleChoice={!isInPipeline}
isMultipleChoice={supportBatchUpload}
currentCredentialId={currentCredentialId}
/>
) : (

View File

@@ -17,6 +17,7 @@ type FileListProps = {
handleSelectFile: (file: OnlineDriveFile) => void
handleOpenFolder: (file: OnlineDriveFile) => void
isLoading: boolean
supportBatchUpload: boolean
}
const FileList = ({
@@ -32,6 +33,7 @@ const FileList = ({
handleOpenFolder,
isInPipeline,
isLoading,
supportBatchUpload,
}: FileListProps) => {
const [inputValue, setInputValue] = useState(keywords)
@@ -72,8 +74,8 @@ const FileList = ({
handleResetKeywords={handleResetKeywords}
handleOpenFolder={handleOpenFolder}
handleSelectFile={handleSelectFile}
isInPipeline={isInPipeline}
isLoading={isLoading}
supportBatchUpload={supportBatchUpload}
/>
</div>
)

View File

@@ -11,8 +11,8 @@ type FileListProps = {
fileList: OnlineDriveFile[]
selectedFileIds: string[]
keywords: string
isInPipeline: boolean
isLoading: boolean
supportBatchUpload: boolean
handleResetKeywords: () => void
handleSelectFile: (file: OnlineDriveFile) => void
handleOpenFolder: (file: OnlineDriveFile) => void
@@ -25,8 +25,8 @@ const List = ({
handleResetKeywords,
handleSelectFile,
handleOpenFolder,
isInPipeline,
isLoading,
supportBatchUpload,
}: FileListProps) => {
const anchorRef = useRef<HTMLDivElement>(null)
const observerRef = useRef<IntersectionObserver>(null)
@@ -80,7 +80,7 @@ const List = ({
isSelected={isSelected}
onSelect={handleSelectFile}
onOpen={handleOpenFolder}
isMultipleChoice={!isInPipeline}
isMultipleChoice={supportBatchUpload}
/>
)
})

View File

@@ -20,14 +20,16 @@ import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/con
type OnlineDriveProps = {
nodeId: string
nodeData: DataSourceNodeType
isInPipeline?: boolean
onCredentialChange: (credentialId: string) => void
isInPipeline?: boolean
supportBatchUpload?: boolean
}
const OnlineDrive = ({
nodeId,
nodeData,
isInPipeline = false,
supportBatchUpload = false,
onCredentialChange,
}: OnlineDriveProps) => {
const docLink = useDocLink()
@@ -111,7 +113,7 @@ const OnlineDrive = ({
},
},
)
}, [datasourceNodeRunURL, dataSourceStore])
}, [dataSourceStore, datasourceNodeRunURL, breadcrumbs])
useEffect(() => {
if (!currentCredentialId) return
@@ -152,12 +154,12 @@ const OnlineDrive = ({
draft.splice(index, 1)
}
else {
if (isInPipeline && draft.length >= 1) return
if (!supportBatchUpload && draft.length >= 1) return
draft.push(file.id)
}
})
setSelectedFileIds(newSelectedFileList)
}, [dataSourceStore, isInPipeline])
}, [dataSourceStore, supportBatchUpload])
const handleOpenFolder = useCallback((file: OnlineDriveFile) => {
const { breadcrumbs, prefix, setBreadcrumbs, setPrefix, setBucket, setOnlineDriveFileList, setSelectedFileIds } = dataSourceStore.getState()
@@ -177,7 +179,7 @@ const OnlineDrive = ({
setBreadcrumbs(newBreadcrumbs)
setPrefix(newPrefix)
}
}, [dataSourceStore, getOnlineDriveFiles])
}, [dataSourceStore])
const handleSetting = useCallback(() => {
setShowAccountSettingModal({
@@ -209,6 +211,7 @@ const OnlineDrive = ({
handleOpenFolder={handleOpenFolder}
isInPipeline={isInPipeline}
isLoading={isLoading}
supportBatchUpload={supportBatchUpload}
/>
</div>
)

View File

@@ -46,6 +46,7 @@ const CrawledResultItem = ({
/>
) : (
<Radio
className='shrink-0'
isChecked={isChecked}
onCheck={handleCheckChange}
/>

View File

@@ -33,14 +33,16 @@ const I18N_PREFIX = 'datasetCreation.stepOne.website'
export type WebsiteCrawlProps = {
nodeId: string
nodeData: DataSourceNodeType
isInPipeline?: boolean
onCredentialChange: (credentialId: string) => void
isInPipeline?: boolean
supportBatchUpload?: boolean
}
const WebsiteCrawl = ({
nodeId,
nodeData,
isInPipeline = false,
supportBatchUpload = false,
onCredentialChange,
}: WebsiteCrawlProps) => {
const { t } = useTranslation()
@@ -122,7 +124,7 @@ const WebsiteCrawl = ({
time_consuming: time_consuming ?? 0,
}
setCrawlResult(crawlResultData)
handleCheckedCrawlResultChange(isInPipeline ? [crawlData[0]] : crawlData) // default select the crawl result
handleCheckedCrawlResultChange(supportBatchUpload ? crawlData : crawlData.slice(0, 1)) // default select the crawl result
setCrawlErrorMessage('')
setStep(CrawlStep.finished)
},
@@ -132,7 +134,7 @@ const WebsiteCrawl = ({
},
},
)
}, [dataSourceStore, datasourceNodeRunURL, handleCheckedCrawlResultChange, isInPipeline, t])
}, [dataSourceStore, datasourceNodeRunURL, handleCheckedCrawlResultChange, supportBatchUpload, t])
const handleSubmit = useCallback((value: Record<string, any>) => {
handleRun(value)
@@ -149,7 +151,7 @@ const WebsiteCrawl = ({
setTotalNum(0)
setCrawlErrorMessage('')
onCredentialChange(credentialId)
}, [dataSourceStore, onCredentialChange])
}, [onCredentialChange])
return (
<div className='flex flex-col'>
@@ -195,7 +197,7 @@ const WebsiteCrawl = ({
previewIndex={previewIndex}
onPreview={handlePreview}
showPreview={!isInPipeline}
isMultipleChoice={!isInPipeline} // only support single choice in test run
isMultipleChoice={supportBatchUpload} // only support single choice in test run
/>
)}
</div>