import { useEffect, useMemo, useState } from 'react'; import { useFormContext } from 'react-hook-form'; import { SelectWithSearch } from '@/components/originui/select-with-search'; import { RAGFlowFormItem } from '@/components/ragflow-form'; import { Input } from '@/components/ui/input'; import { Segmented } from '@/components/ui/segmented'; import { t } from 'i18next'; // UI-only auth modes for S3 // access_key: Access Key ID + Secret // iam_role: only Role ARN // assume_role: no input fields (uses environment role) type AuthMode = 'access_key' | 'iam_role' | 'assume_role'; type BlobMode = 's3' | 's3_compatible'; const modeOptions = [ { label: 'S3', value: 's3' }, { label: 'S3 Compatible', value: 's3_compatible' }, ]; const authOptions = [ { label: 'Access Key', value: 'access_key' }, { label: 'IAM Role', value: 'iam_role' }, { label: 'Assume Role', value: 'assume_role' }, ]; const addressingOptions = [ { label: 'Virtual Hosted Style', value: 'virtual' }, { label: 'Path Style', value: 'path' }, ]; const deriveInitialAuthMode = (credentials: any): AuthMode => { const authMethod = credentials?.authentication_method; if (authMethod === 'iam_role') return 'iam_role'; if (authMethod === 'assume_role') return 'assume_role'; if (credentials?.aws_role_arn) return 'iam_role'; if (credentials?.aws_access_key_id || credentials?.aws_secret_access_key) return 'access_key'; return 'access_key'; }; const deriveInitialMode = (bucketType?: string): BlobMode => bucketType === 's3_compatible' ? 's3_compatible' : 's3'; const BlobTokenField = () => { const form = useFormContext(); const credentials = form.watch('config.credentials'); const watchedBucketType = form.watch('config.bucket_type'); const [mode, setMode] = useState( deriveInitialMode(watchedBucketType), ); const [authMode, setAuthMode] = useState(() => deriveInitialAuthMode(credentials), ); // Keep bucket_type in sync with UI mode useEffect(() => { const nextMode = deriveInitialMode(watchedBucketType); setMode((prev) => (prev === nextMode ? prev : nextMode)); }, [watchedBucketType]); useEffect(() => { form.setValue('config.bucket_type', mode, { shouldDirty: true }); // Default addressing style for compatible mode if ( mode === 's3_compatible' && !form.getValues('config.credentials.addressing_style') ) { form.setValue('config.credentials.addressing_style', 'virtual', { shouldDirty: false, }); } if (mode === 's3_compatible' && authMode !== 'access_key') { setAuthMode('access_key'); } // Persist authentication_method for backend const nextAuthMethod: AuthMode = mode === 's3_compatible' ? 'access_key' : authMode; form.setValue('config.credentials.authentication_method', nextAuthMethod, { shouldDirty: true, }); // Clear errors for fields that are not relevant in the current mode/auth selection const inactiveFields: string[] = []; if (mode === 's3_compatible') { inactiveFields.push('config.credentials.aws_role_arn'); } else { if (authMode === 'iam_role') { inactiveFields.push('config.credentials.aws_access_key_id'); inactiveFields.push('config.credentials.aws_secret_access_key'); } if (authMode === 'assume_role') { inactiveFields.push('config.credentials.aws_access_key_id'); inactiveFields.push('config.credentials.aws_secret_access_key'); inactiveFields.push('config.credentials.aws_role_arn'); } } if (inactiveFields.length) { form.clearErrors(inactiveFields as any); } }, [form, mode, authMode]); const isS3 = mode === 's3'; const requiresAccessKey = authMode === 'access_key' || mode === 's3_compatible'; const requiresRoleArn = isS3 && authMode === 'iam_role'; // Help text for assume role (no inputs) const assumeRoleNote = useMemo( () => t('No credentials required. Uses the default environment role.'), [t], ); return (
Mode
setMode(val as BlobMode)} className="w-full" itemClassName="flex-1 justify-center" />
{isS3 && (
Authentication
setAuthMode(val as AuthMode)} className="w-full" itemClassName="flex-1 justify-center" />
)} {requiresAccessKey && ( requiresAccessKey ? Boolean(val) || 'Access Key ID is required' : true, }} > {(field) => ( )} )} {requiresAccessKey && ( requiresAccessKey ? Boolean(val) || 'Secret Access Key is required' : true, }} > {(field) => ( )} )} {requiresRoleArn && ( requiresRoleArn ? Boolean(val) || 'Role ARN is required' : true, }} > {(field) => ( )} )} {isS3 && authMode === 'assume_role' && (
{assumeRoleNote}
)} {mode === 's3_compatible' && (
{(field) => ( field.onChange(val)} /> )} {(field) => ( )}
)}
); }; export default BlobTokenField;