You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

api-key-modal.tsx 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import {
  2. memo,
  3. useCallback,
  4. useMemo,
  5. useRef,
  6. useState,
  7. } from 'react'
  8. import { useTranslation } from 'react-i18next'
  9. import { RiExternalLinkLine } from '@remixicon/react'
  10. import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
  11. import Modal from '@/app/components/base/modal/modal'
  12. import { CredentialTypeEnum } from '../types'
  13. import AuthForm from '@/app/components/base/form/form-scenarios/auth'
  14. import type { FormRefObject } from '@/app/components/base/form/types'
  15. import { FormTypeEnum } from '@/app/components/base/form/types'
  16. import { useToastContext } from '@/app/components/base/toast'
  17. import Loading from '@/app/components/base/loading'
  18. import type { PluginPayload } from '../types'
  19. import {
  20. useAddPluginCredentialHook,
  21. useGetPluginCredentialSchemaHook,
  22. useUpdatePluginCredentialHook,
  23. } from '../hooks/use-credential'
  24. import { useRenderI18nObject } from '@/hooks/use-i18n'
  25. export type ApiKeyModalProps = {
  26. pluginPayload: PluginPayload
  27. onClose?: () => void
  28. editValues?: Record<string, any>
  29. onRemove?: () => void
  30. disabled?: boolean
  31. onUpdate?: () => void
  32. }
  33. const ApiKeyModal = ({
  34. pluginPayload,
  35. onClose,
  36. editValues,
  37. onRemove,
  38. disabled,
  39. onUpdate,
  40. }: ApiKeyModalProps) => {
  41. const { t } = useTranslation()
  42. const { notify } = useToastContext()
  43. const [doingAction, setDoingAction] = useState(false)
  44. const doingActionRef = useRef(doingAction)
  45. const handleSetDoingAction = useCallback((value: boolean) => {
  46. doingActionRef.current = value
  47. setDoingAction(value)
  48. }, [])
  49. const { data = [], isLoading } = useGetPluginCredentialSchemaHook(pluginPayload, CredentialTypeEnum.API_KEY)
  50. const formSchemas = useMemo(() => {
  51. return [
  52. {
  53. type: FormTypeEnum.textInput,
  54. name: '__name__',
  55. label: t('plugin.auth.authorizationName'),
  56. required: false,
  57. },
  58. ...data,
  59. ]
  60. }, [data, t])
  61. const defaultValues = formSchemas.reduce((acc, schema) => {
  62. if (schema.default)
  63. acc[schema.name] = schema.default
  64. return acc
  65. }, {} as Record<string, any>)
  66. const helpField = formSchemas.find(schema => schema.url && schema.help)
  67. const renderI18nObject = useRenderI18nObject()
  68. const { mutateAsync: addPluginCredential } = useAddPluginCredentialHook(pluginPayload)
  69. const { mutateAsync: updatePluginCredential } = useUpdatePluginCredentialHook(pluginPayload)
  70. const formRef = useRef<FormRefObject>(null)
  71. const handleConfirm = useCallback(async () => {
  72. if (doingActionRef.current)
  73. return
  74. const {
  75. isCheckValidated,
  76. values,
  77. } = formRef.current?.getFormValues({
  78. needCheckValidatedValues: true,
  79. needTransformWhenSecretFieldIsPristine: true,
  80. }) || { isCheckValidated: false, values: {} }
  81. if (!isCheckValidated)
  82. return
  83. try {
  84. const {
  85. __name__,
  86. __credential_id__,
  87. ...restValues
  88. } = values
  89. handleSetDoingAction(true)
  90. if (editValues) {
  91. await updatePluginCredential({
  92. credentials: restValues,
  93. credential_id: __credential_id__,
  94. name: __name__ || '',
  95. })
  96. }
  97. else {
  98. await addPluginCredential({
  99. credentials: restValues,
  100. type: CredentialTypeEnum.API_KEY,
  101. name: __name__ || '',
  102. })
  103. }
  104. notify({
  105. type: 'success',
  106. message: t('common.api.actionSuccess'),
  107. })
  108. onClose?.()
  109. onUpdate?.()
  110. }
  111. finally {
  112. handleSetDoingAction(false)
  113. }
  114. }, [addPluginCredential, onClose, onUpdate, updatePluginCredential, notify, t, editValues, handleSetDoingAction])
  115. return (
  116. <Modal
  117. size='md'
  118. title={t('plugin.auth.useApiAuth')}
  119. subTitle={t('plugin.auth.useApiAuthDesc')}
  120. onClose={onClose}
  121. onCancel={onClose}
  122. footerSlot={
  123. helpField && (
  124. <a
  125. className='system-xs-regular mr-2 flex items-center py-2 text-text-accent'
  126. href={helpField?.url}
  127. target='_blank'
  128. >
  129. <span className='break-all'>
  130. {renderI18nObject(helpField?.help as any)}
  131. </span>
  132. <RiExternalLinkLine className='ml-1 h-3 w-3' />
  133. </a>
  134. )
  135. }
  136. bottomSlot={
  137. <div className='flex items-center justify-center bg-background-section-burn py-3 text-xs text-text-tertiary'>
  138. <Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
  139. {t('common.modelProvider.encrypted.front')}
  140. <a
  141. className='mx-1 text-text-accent'
  142. target='_blank' rel='noopener noreferrer'
  143. href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
  144. >
  145. PKCS1_OAEP
  146. </a>
  147. {t('common.modelProvider.encrypted.back')}
  148. </div>
  149. }
  150. onConfirm={handleConfirm}
  151. showExtraButton={!!editValues}
  152. onExtraButtonClick={onRemove}
  153. disabled={disabled || isLoading || doingAction}
  154. >
  155. {
  156. isLoading && (
  157. <div className='flex h-40 items-center justify-center'>
  158. <Loading />
  159. </div>
  160. )
  161. }
  162. {
  163. !isLoading && !!data.length && (
  164. <AuthForm
  165. ref={formRef}
  166. formSchemas={formSchemas}
  167. defaultValues={editValues || defaultValues}
  168. disabled={disabled}
  169. />
  170. )
  171. }
  172. </Modal>
  173. )
  174. }
  175. export default memo(ApiKeyModal)