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.

oauth-client-settings.tsx 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import {
  2. memo,
  3. useCallback,
  4. useRef,
  5. useState,
  6. } from 'react'
  7. import { RiExternalLinkLine } from '@remixicon/react'
  8. import {
  9. useForm,
  10. useStore,
  11. } from '@tanstack/react-form'
  12. import { useTranslation } from 'react-i18next'
  13. import Modal from '@/app/components/base/modal/modal'
  14. import {
  15. useDeletePluginOAuthCustomClientHook,
  16. useInvalidPluginOAuthClientSchemaHook,
  17. useSetPluginOAuthCustomClientHook,
  18. } from '../hooks/use-credential'
  19. import type { PluginPayload } from '../types'
  20. import AuthForm from '@/app/components/base/form/form-scenarios/auth'
  21. import type {
  22. FormRefObject,
  23. FormSchema,
  24. } from '@/app/components/base/form/types'
  25. import { useToastContext } from '@/app/components/base/toast'
  26. import Button from '@/app/components/base/button'
  27. import { useRenderI18nObject } from '@/hooks/use-i18n'
  28. type OAuthClientSettingsProps = {
  29. pluginPayload: PluginPayload
  30. onClose?: () => void
  31. editValues?: Record<string, any>
  32. disabled?: boolean
  33. schemas: FormSchema[]
  34. onAuth?: () => Promise<void>
  35. hasOriginalClientParams?: boolean
  36. onUpdate?: () => void
  37. }
  38. const OAuthClientSettings = ({
  39. pluginPayload,
  40. onClose,
  41. editValues,
  42. disabled,
  43. schemas,
  44. onAuth,
  45. hasOriginalClientParams,
  46. onUpdate,
  47. }: OAuthClientSettingsProps) => {
  48. const { t } = useTranslation()
  49. const { notify } = useToastContext()
  50. const [doingAction, setDoingAction] = useState(false)
  51. const doingActionRef = useRef(doingAction)
  52. const handleSetDoingAction = useCallback((value: boolean) => {
  53. doingActionRef.current = value
  54. setDoingAction(value)
  55. }, [])
  56. const defaultValues = schemas.reduce((acc, schema) => {
  57. if (schema.default)
  58. acc[schema.name] = schema.default
  59. return acc
  60. }, {} as Record<string, any>)
  61. const { mutateAsync: setPluginOAuthCustomClient } = useSetPluginOAuthCustomClientHook(pluginPayload)
  62. const invalidPluginOAuthClientSchema = useInvalidPluginOAuthClientSchemaHook(pluginPayload)
  63. const formRef = useRef<FormRefObject>(null)
  64. const handleConfirm = useCallback(async () => {
  65. if (doingActionRef.current)
  66. return
  67. try {
  68. const {
  69. isCheckValidated,
  70. values,
  71. } = formRef.current?.getFormValues({
  72. needCheckValidatedValues: true,
  73. needTransformWhenSecretFieldIsPristine: true,
  74. }) || { isCheckValidated: false, values: {} }
  75. if (!isCheckValidated)
  76. throw new Error('error')
  77. const {
  78. __oauth_client__,
  79. ...restValues
  80. } = values
  81. handleSetDoingAction(true)
  82. await setPluginOAuthCustomClient({
  83. client_params: restValues,
  84. enable_oauth_custom_client: __oauth_client__ === 'custom',
  85. })
  86. notify({
  87. type: 'success',
  88. message: t('common.api.actionSuccess'),
  89. })
  90. onClose?.()
  91. onUpdate?.()
  92. invalidPluginOAuthClientSchema()
  93. }
  94. finally {
  95. handleSetDoingAction(false)
  96. }
  97. }, [onClose, onUpdate, invalidPluginOAuthClientSchema, setPluginOAuthCustomClient, notify, t, handleSetDoingAction])
  98. const handleConfirmAndAuthorize = useCallback(async () => {
  99. await handleConfirm()
  100. if (onAuth)
  101. await onAuth()
  102. }, [handleConfirm, onAuth])
  103. const { mutateAsync: deletePluginOAuthCustomClient } = useDeletePluginOAuthCustomClientHook(pluginPayload)
  104. const handleRemove = useCallback(async () => {
  105. if (doingActionRef.current)
  106. return
  107. try {
  108. handleSetDoingAction(true)
  109. await deletePluginOAuthCustomClient()
  110. notify({
  111. type: 'success',
  112. message: t('common.api.actionSuccess'),
  113. })
  114. onClose?.()
  115. onUpdate?.()
  116. invalidPluginOAuthClientSchema()
  117. }
  118. finally {
  119. handleSetDoingAction(false)
  120. }
  121. }, [onUpdate, invalidPluginOAuthClientSchema, deletePluginOAuthCustomClient, notify, t, handleSetDoingAction, onClose])
  122. const form = useForm({
  123. defaultValues: editValues || defaultValues,
  124. })
  125. const __oauth_client__ = useStore(form.store, s => s.values.__oauth_client__)
  126. const helpField = schemas.find(schema => schema.url && schema.help)
  127. const renderI18nObject = useRenderI18nObject()
  128. return (
  129. <Modal
  130. title={t('plugin.auth.oauthClientSettings')}
  131. confirmButtonText={t('plugin.auth.saveAndAuth')}
  132. cancelButtonText={t('plugin.auth.saveOnly')}
  133. extraButtonText={t('common.operation.cancel')}
  134. showExtraButton
  135. extraButtonVariant='secondary'
  136. onExtraButtonClick={onClose}
  137. onClose={onClose}
  138. onCancel={handleConfirm}
  139. onConfirm={handleConfirmAndAuthorize}
  140. disabled={disabled || doingAction}
  141. footerSlot={
  142. __oauth_client__ === 'custom' && hasOriginalClientParams && (
  143. <div className='grow'>
  144. <Button
  145. variant='secondary'
  146. className='text-components-button-destructive-secondary-text'
  147. disabled={disabled || doingAction || !editValues}
  148. onClick={handleRemove}
  149. >
  150. {t('common.operation.remove')}
  151. </Button>
  152. </div>
  153. )
  154. }
  155. >
  156. <>
  157. <AuthForm
  158. formFromProps={form}
  159. ref={formRef}
  160. formSchemas={schemas}
  161. defaultValues={editValues || defaultValues}
  162. disabled={disabled}
  163. />
  164. {
  165. helpField && __oauth_client__ === 'custom' && (
  166. <a
  167. className='system-xs-regular mt-4 flex items-center text-text-accent'
  168. href={helpField?.url}
  169. target='_blank'
  170. >
  171. <span className='break-all'>
  172. {renderI18nObject(helpField?.help as any)}
  173. </span>
  174. <RiExternalLinkLine className='ml-1 h-3 w-3' />
  175. </a>
  176. )}
  177. </>
  178. </Modal>
  179. )
  180. }
  181. export default memo(OAuthClientSettings)