config-jina-reader-modal.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import {
  6. PortalToFollowElem,
  7. PortalToFollowElemContent,
  8. } from '@/app/components/base/portal-to-follow-elem'
  9. import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
  10. import Button from '@/app/components/base/button'
  11. import { DataSourceProvider } from '@/models/common'
  12. import Field from '@/app/components/datasets/create/website/base/field'
  13. import Toast from '@/app/components/base/toast'
  14. import { createDataSourceApiKeyBinding } from '@/service/datasets'
  15. import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
  16. type Props = {
  17. onCancel: () => void
  18. onSaved: () => void
  19. }
  20. const I18N_PREFIX = 'datasetCreation.jinaReader'
  21. const ConfigJinaReaderModal: FC<Props> = ({
  22. onCancel,
  23. onSaved,
  24. }) => {
  25. const { t } = useTranslation()
  26. const [isSaving, setIsSaving] = useState(false)
  27. const [apiKey, setApiKey] = useState('')
  28. const handleSave = useCallback(async () => {
  29. if (isSaving)
  30. return
  31. let errorMsg = ''
  32. if (!errorMsg) {
  33. if (!apiKey) {
  34. errorMsg = t('common.errorMsg.fieldRequired', {
  35. field: 'API Key',
  36. })
  37. }
  38. }
  39. if (errorMsg) {
  40. Toast.notify({
  41. type: 'error',
  42. message: errorMsg,
  43. })
  44. return
  45. }
  46. const postData = {
  47. category: 'website',
  48. provider: DataSourceProvider.jinaReader,
  49. credentials: {
  50. auth_type: 'bearer',
  51. config: {
  52. api_key: apiKey,
  53. },
  54. },
  55. }
  56. try {
  57. setIsSaving(true)
  58. await createDataSourceApiKeyBinding(postData)
  59. Toast.notify({
  60. type: 'success',
  61. message: t('common.api.success'),
  62. })
  63. }
  64. finally {
  65. setIsSaving(false)
  66. }
  67. onSaved()
  68. }, [apiKey, onSaved, t, isSaving])
  69. return (
  70. <PortalToFollowElem open>
  71. <PortalToFollowElemContent className='w-full h-full z-[60]'>
  72. <div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
  73. <div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
  74. <div className='px-8 pt-8'>
  75. <div className='flex justify-between items-center mb-4'>
  76. <div className='text-xl font-semibold text-gray-900'>{t(`${I18N_PREFIX}.configJinaReader`)}</div>
  77. </div>
  78. <div className='space-y-4'>
  79. <Field
  80. label='API Key'
  81. labelClassName='!text-sm'
  82. isRequired
  83. value={apiKey}
  84. onChange={(value: string | number) => setApiKey(value as string)}
  85. placeholder={t(`${I18N_PREFIX}.apiKeyPlaceholder`)!}
  86. />
  87. </div>
  88. <div className='my-8 flex justify-between items-center h-8'>
  89. <a className='flex items-center space-x-1 leading-[18px] text-xs font-normal text-[#155EEF]' target='_blank' href='https://jina.ai/reader/'>
  90. <span>{t(`${I18N_PREFIX}.getApiKeyLinkText`)}</span>
  91. <LinkExternal02 className='w-3 h-3' />
  92. </a>
  93. <div className='flex'>
  94. <Button
  95. size='large'
  96. className='mr-2'
  97. onClick={onCancel}
  98. >
  99. {t('common.operation.cancel')}
  100. </Button>
  101. <Button
  102. variant='primary'
  103. size='large'
  104. onClick={handleSave}
  105. loading={isSaving}
  106. >
  107. {t('common.operation.save')}
  108. </Button>
  109. </div>
  110. </div>
  111. </div>
  112. <div className='border-t-[0.5px] border-t-black/5'>
  113. <div className='flex justify-center items-center py-3 bg-gray-50 text-xs text-gray-500'>
  114. <Lock01 className='mr-1 w-3 h-3 text-gray-500' />
  115. {t('common.modelProvider.encrypted.front')}
  116. <a
  117. className='text-primary-600 mx-1'
  118. target='_blank' rel='noopener noreferrer'
  119. href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
  120. >
  121. PKCS1_OAEP
  122. </a>
  123. {t('common.modelProvider.encrypted.back')}
  124. </div>
  125. </div>
  126. </div>
  127. </div>
  128. </PortalToFollowElemContent>
  129. </PortalToFollowElem>
  130. )
  131. }
  132. export default React.memo(ConfigJinaReaderModal)