| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 | 'use client'import type { FC } from 'react'import React, { useCallback, useState } from 'react'import { useTranslation } from 'react-i18next'import {  PortalToFollowElem,  PortalToFollowElemContent,} from '@/app/components/base/portal-to-follow-elem'import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'import Button from '@/app/components/base/button'import type { FirecrawlConfig } from '@/models/common'import Field from '@/app/components/datasets/create/website/base/field'import Toast from '@/app/components/base/toast'import { createDataSourceApiKeyBinding } from '@/service/datasets'import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'type Props = {  onCancel: () => void  onSaved: () => void}const I18N_PREFIX = 'datasetCreation.firecrawl'const DEFAULT_BASE_URL = 'https://api.firecrawl.dev'const ConfigFirecrawlModal: FC<Props> = ({  onCancel,  onSaved,}) => {  const { t } = useTranslation()  const [isSaving, setIsSaving] = useState(false)  const [config, setConfig] = useState<FirecrawlConfig>({    api_key: '',    base_url: '',  })  const handleConfigChange = useCallback((key: string) => {    return (value: string | number) => {      setConfig(prev => ({ ...prev, [key]: value as string }))    }  }, [])  const handleSave = useCallback(async () => {    if (isSaving)      return    let errorMsg = ''    if (config.base_url && !((config.base_url.startsWith('http://') || config.base_url.startsWith('https://'))))      errorMsg = t('common.errorMsg.urlError')    if (!errorMsg) {      if (!config.api_key) {        errorMsg = t('common.errorMsg.fieldRequired', {          field: 'API Key',        })      }    }    if (errorMsg) {      Toast.notify({        type: 'error',        message: errorMsg,      })      return    }    const postData = {      category: 'website',      provider: 'firecrawl',      credentials: {        auth_type: 'bearer',        config: {          api_key: config.api_key,          base_url: config.base_url || DEFAULT_BASE_URL,        },      },    }    try {      setIsSaving(true)      await createDataSourceApiKeyBinding(postData)      Toast.notify({        type: 'success',        message: t('common.api.success'),      })    }    finally {      setIsSaving(false)    }    onSaved()  }, [config.api_key, config.base_url, onSaved, t, isSaving])  return (    <PortalToFollowElem open>      <PortalToFollowElemContent className='w-full h-full z-[60]'>        <div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>          <div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>            <div className='px-8 pt-8'>              <div className='flex justify-between items-center mb-4'>                <div className='text-xl font-semibold text-gray-900'>{t(`${I18N_PREFIX}.configFirecrawl`)}</div>              </div>              <div className='space-y-4'>                <Field                  label='API Key'                  labelClassName='!text-sm'                  isRequired                  value={config.api_key}                  onChange={handleConfigChange('api_key')}                  placeholder={t(`${I18N_PREFIX}.apiKeyPlaceholder`)!}                />                <Field                  label='Base URL'                  labelClassName='!text-sm'                  value={config.base_url}                  onChange={handleConfigChange('base_url')}                  placeholder={DEFAULT_BASE_URL}                />              </div>              <div className='my-8 flex justify-between items-center h-8'>                <a className='flex items-center space-x-1 leading-[18px] text-xs font-normal text-[#155EEF]' target='_blank' href='https://www.firecrawl.dev/account'>                  <span>{t(`${I18N_PREFIX}.getApiKeyLinkText`)}</span>                  <LinkExternal02 className='w-3 h-3' />                </a>                <div className='flex'>                  <Button                    size='large'                    className='mr-2'                    onClick={onCancel}                  >                    {t('common.operation.cancel')}                  </Button>                  <Button                    variant='primary'                    size='large'                    onClick={handleSave}                    loading={isSaving}                  >                    {t('common.operation.save')}                  </Button>                </div>              </div>            </div>            <div className='border-t-[0.5px] border-t-black/5'>              <div className='flex justify-center items-center py-3 bg-gray-50 text-xs text-gray-500'>                <Lock01 className='mr-1 w-3 h-3 text-gray-500' />                {t('common.modelProvider.encrypted.front')}                <a                  className='text-primary-600 mx-1'                  target='_blank' rel='noopener noreferrer'                  href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'                >                  PKCS1_OAEP                </a>                {t('common.modelProvider.encrypted.back')}              </div>            </div>          </div>        </div>      </PortalToFollowElemContent>    </PortalToFollowElem>  )}export default React.memo(ConfigFirecrawlModal)
 |