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 4.7KB

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