tool oauth
parent
c53d5c105b
commit
0f1be60daa
@ -0,0 +1,76 @@
|
|||||||
|
import {
|
||||||
|
isValidElement,
|
||||||
|
memo,
|
||||||
|
useMemo,
|
||||||
|
} from 'react'
|
||||||
|
import type { AnyFieldApi } from '@tanstack/react-form'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import Input from '@/app/components/base/input'
|
||||||
|
import type { FormSchema } from '@/app/components/base/form/types'
|
||||||
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||||
|
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||||
|
|
||||||
|
export type BaseFieldProps = {
|
||||||
|
fieldClassName?: string
|
||||||
|
labelClassName?: string
|
||||||
|
inputContainerClassName?: string
|
||||||
|
inputClassName?: string
|
||||||
|
formSchema: FormSchema
|
||||||
|
field: AnyFieldApi
|
||||||
|
}
|
||||||
|
const BaseField = ({
|
||||||
|
fieldClassName,
|
||||||
|
labelClassName,
|
||||||
|
inputContainerClassName,
|
||||||
|
inputClassName,
|
||||||
|
formSchema,
|
||||||
|
field,
|
||||||
|
}: BaseFieldProps) => {
|
||||||
|
const renderI18nObject = useRenderI18nObject()
|
||||||
|
const {
|
||||||
|
label,
|
||||||
|
} = formSchema
|
||||||
|
|
||||||
|
const memorizedLabel = useMemo(() => {
|
||||||
|
if (isValidElement(label))
|
||||||
|
return label
|
||||||
|
|
||||||
|
if (typeof label === 'object' && label !== null)
|
||||||
|
return renderI18nObject(label as Record<string, string>)
|
||||||
|
}, [label, renderI18nObject])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn(fieldClassName)}>
|
||||||
|
<div className={cn(labelClassName)}>
|
||||||
|
{memorizedLabel}
|
||||||
|
</div>
|
||||||
|
<div className={cn(inputContainerClassName)}>
|
||||||
|
{
|
||||||
|
formSchema.type === FormTypeEnum.textInput && (
|
||||||
|
<Input
|
||||||
|
className={cn(inputClassName)}
|
||||||
|
id={field.name}
|
||||||
|
value={field.state.value}
|
||||||
|
onChange={e => field.handleChange(e.target.value)}
|
||||||
|
onBlur={field.handleBlur}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
formSchema.type === FormTypeEnum.secretInput && (
|
||||||
|
<Input
|
||||||
|
type='password'
|
||||||
|
className={cn(inputClassName)}
|
||||||
|
id={field.name}
|
||||||
|
value={field.state.value}
|
||||||
|
onChange={e => field.handleChange(e.target.value)}
|
||||||
|
onBlur={field.handleBlur}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(BaseField)
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useImperativeHandle,
|
||||||
|
} from 'react'
|
||||||
|
import type {
|
||||||
|
AnyFieldApi,
|
||||||
|
} from '@tanstack/react-form'
|
||||||
|
import { useForm } from '@tanstack/react-form'
|
||||||
|
import type {
|
||||||
|
FormRef,
|
||||||
|
FormSchema,
|
||||||
|
} from '@/app/components/base/form/types'
|
||||||
|
import {
|
||||||
|
BaseField,
|
||||||
|
} from '.'
|
||||||
|
import type {
|
||||||
|
BaseFieldProps,
|
||||||
|
} from '.'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
export type BaseFormProps = {
|
||||||
|
formSchemas?: FormSchema[]
|
||||||
|
defaultValues?: Record<string, any>
|
||||||
|
formClassName?: string
|
||||||
|
ref?: FormRef
|
||||||
|
} & Pick<BaseFieldProps, 'fieldClassName' | 'labelClassName' | 'inputContainerClassName' | 'inputClassName'>
|
||||||
|
|
||||||
|
const BaseForm = ({
|
||||||
|
formSchemas,
|
||||||
|
defaultValues,
|
||||||
|
formClassName,
|
||||||
|
fieldClassName,
|
||||||
|
labelClassName,
|
||||||
|
inputContainerClassName,
|
||||||
|
inputClassName,
|
||||||
|
ref,
|
||||||
|
}: BaseFormProps) => {
|
||||||
|
const form = useForm({
|
||||||
|
defaultValues,
|
||||||
|
})
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => {
|
||||||
|
return {
|
||||||
|
getFormStore() {
|
||||||
|
return form.store
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, [form])
|
||||||
|
|
||||||
|
const renderField = useCallback((field: AnyFieldApi) => {
|
||||||
|
const formSchema = formSchemas?.find(schema => schema.name === field.name)
|
||||||
|
|
||||||
|
if (formSchema) {
|
||||||
|
return (
|
||||||
|
<BaseField
|
||||||
|
field={field}
|
||||||
|
formSchema={formSchema}
|
||||||
|
fieldClassName={fieldClassName}
|
||||||
|
labelClassName={labelClassName}
|
||||||
|
inputContainerClassName={inputContainerClassName}
|
||||||
|
inputClassName={inputClassName}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}, [formSchemas, fieldClassName, labelClassName, inputContainerClassName, inputClassName])
|
||||||
|
|
||||||
|
if (!formSchemas?.length)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className={cn(formClassName)}>
|
||||||
|
{
|
||||||
|
formSchemas.map((formSchema) => {
|
||||||
|
return (
|
||||||
|
<form.Field
|
||||||
|
key={formSchema.name}
|
||||||
|
name={formSchema.name}
|
||||||
|
>
|
||||||
|
{renderField}
|
||||||
|
</form.Field>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(BaseForm)
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export { default as BaseForm, type BaseFormProps } from './base-form'
|
||||||
|
export { default as BaseField, type BaseFieldProps } from './base-field'
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import { BaseForm } from '../../components/base'
|
||||||
|
import type { BaseFormProps } from '../../components/base'
|
||||||
|
|
||||||
|
const AuthForm = ({
|
||||||
|
formSchemas = [],
|
||||||
|
defaultValues,
|
||||||
|
ref,
|
||||||
|
}: BaseFormProps) => {
|
||||||
|
return (
|
||||||
|
<BaseForm
|
||||||
|
ref={ref}
|
||||||
|
formSchemas={formSchemas}
|
||||||
|
defaultValues={defaultValues}
|
||||||
|
formClassName='space-y-4'
|
||||||
|
labelClassName='h-6 flex items-center mb-1 system-sm-medium text-text-secondary'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(AuthForm)
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
import type {
|
||||||
|
ForwardedRef,
|
||||||
|
ReactNode,
|
||||||
|
} from 'react'
|
||||||
|
import type { AnyFormApi } from '@tanstack/react-form'
|
||||||
|
|
||||||
|
export type TypeWithI18N<T = string> = {
|
||||||
|
en_US: T
|
||||||
|
zh_Hans: T
|
||||||
|
[key: string]: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormShowOnObject = {
|
||||||
|
variable: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FormTypeEnum {
|
||||||
|
textInput = 'text-input',
|
||||||
|
textNumber = 'number-input',
|
||||||
|
secretInput = 'secret-input',
|
||||||
|
select = 'select',
|
||||||
|
radio = 'radio',
|
||||||
|
boolean = 'boolean',
|
||||||
|
files = 'files',
|
||||||
|
file = 'file',
|
||||||
|
modelSelector = 'model-selector',
|
||||||
|
toolSelector = 'tool-selector',
|
||||||
|
multiToolSelector = 'array[tools]',
|
||||||
|
appSelector = 'app-selector',
|
||||||
|
dynamicSelect = 'dynamic-select',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormSchema = {
|
||||||
|
type: FormTypeEnum
|
||||||
|
name: string
|
||||||
|
label: string | ReactNode | TypeWithI18N
|
||||||
|
required: boolean
|
||||||
|
default?: any
|
||||||
|
tooltip?: string | TypeWithI18N
|
||||||
|
show_on: FormShowOnObject[]
|
||||||
|
url?: string
|
||||||
|
scope?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormValues = Record<string, any>
|
||||||
|
|
||||||
|
export type FromRefObject = {
|
||||||
|
getFormStore: () => AnyFormApi['store']
|
||||||
|
}
|
||||||
|
export type FormRef = ForwardedRef<FromRefObject>
|
||||||
@ -0,0 +1,109 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
} from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiExternalLinkLine } from '@remixicon/react'
|
||||||
|
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
|
||||||
|
import Modal from '@/app/components/base/modal/modal'
|
||||||
|
import {
|
||||||
|
useAddPluginToolCredential,
|
||||||
|
useGetPluginToolCredentialSchema,
|
||||||
|
useInvalidPluginToolCredentialInfo,
|
||||||
|
useUpdatePluginToolCredential,
|
||||||
|
} from '@/service/use-plugins-auth'
|
||||||
|
import { CredentialTypeEnum } from '../types'
|
||||||
|
import AuthForm from '@/app/components/base/form/form-scenarios/auth'
|
||||||
|
import type { FromRefObject } from '@/app/components/base/form/types'
|
||||||
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
|
|
||||||
|
export type ApiKeyModalProps = {
|
||||||
|
provider: string
|
||||||
|
onClose?: () => void
|
||||||
|
editValues?: Record<string, any>
|
||||||
|
onRemove?: () => void
|
||||||
|
}
|
||||||
|
const ApiKeyModal = ({
|
||||||
|
provider,
|
||||||
|
onClose,
|
||||||
|
editValues,
|
||||||
|
onRemove,
|
||||||
|
}: ApiKeyModalProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { notify } = useToastContext()
|
||||||
|
const { data } = useGetPluginToolCredentialSchema(provider, CredentialTypeEnum.API_KEY)
|
||||||
|
const { mutateAsync: addPluginToolCredential } = useAddPluginToolCredential(provider)
|
||||||
|
const { mutateAsync: updatePluginToolCredential } = useUpdatePluginToolCredential(provider)
|
||||||
|
const invalidatePluginToolCredentialInfo = useInvalidPluginToolCredentialInfo(provider)
|
||||||
|
const formRef = useRef<FromRefObject>(null)
|
||||||
|
const handleConfirm = useCallback(async () => {
|
||||||
|
const store = formRef.current?.getFormStore()
|
||||||
|
const values = store?.state.values
|
||||||
|
|
||||||
|
if (editValues) {
|
||||||
|
await updatePluginToolCredential({
|
||||||
|
credentials: values,
|
||||||
|
type: CredentialTypeEnum.API_KEY,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await addPluginToolCredential({
|
||||||
|
credentials: values,
|
||||||
|
type: CredentialTypeEnum.API_KEY,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
|
||||||
|
onClose?.()
|
||||||
|
invalidatePluginToolCredentialInfo()
|
||||||
|
}, [addPluginToolCredential, onClose, invalidatePluginToolCredentialInfo, updatePluginToolCredential, notify, t, editValues])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
size='md'
|
||||||
|
title='API Key Authorization Configuration'
|
||||||
|
subTitle='After configuring credentials, all members within the workspace can use this tool when orchestrating applications.'
|
||||||
|
onClose={onClose}
|
||||||
|
onCancel={onClose}
|
||||||
|
footerSlot={
|
||||||
|
<a
|
||||||
|
className='system-xs-regular flex h-8 grow items-center text-text-accent'
|
||||||
|
href=''
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
|
Get your API Key from OpenAI
|
||||||
|
<RiExternalLinkLine className='ml-1 h-3 w-3' />
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
bottomSlot={
|
||||||
|
<div className='flex items-center justify-center bg-background-section-burn py-3 text-xs text-text-tertiary'>
|
||||||
|
<Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
|
||||||
|
{t('common.modelProvider.encrypted.front')}
|
||||||
|
<a
|
||||||
|
className='mx-1 text-text-accent'
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
showExtraButton={!!editValues}
|
||||||
|
onExtraButtonClick={onRemove}
|
||||||
|
>
|
||||||
|
<AuthForm
|
||||||
|
ref={formRef}
|
||||||
|
formSchemas={data}
|
||||||
|
defaultValues={editValues}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ApiKeyModal)
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useMemo,
|
||||||
|
} from 'react'
|
||||||
|
import AddOAuthButton from './add-oauth-button'
|
||||||
|
import type { AddOAuthButtonProps } from './add-oauth-button'
|
||||||
|
import AddApiKeyButton from './add-api-key-button'
|
||||||
|
import type { AddApiKeyButtonProps } from './add-api-key-button'
|
||||||
|
|
||||||
|
type AuthorizeProps = {
|
||||||
|
provider?: string
|
||||||
|
theme?: 'primary' | 'secondary'
|
||||||
|
showDivider?: boolean
|
||||||
|
canOAuth?: boolean
|
||||||
|
canApiKey?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
const Authorize = ({
|
||||||
|
provider = '',
|
||||||
|
theme = 'primary',
|
||||||
|
showDivider = true,
|
||||||
|
canOAuth,
|
||||||
|
canApiKey,
|
||||||
|
disabled,
|
||||||
|
}: AuthorizeProps) => {
|
||||||
|
const oAuthButtonProps: AddOAuthButtonProps = useMemo(() => {
|
||||||
|
if (theme === 'secondary') {
|
||||||
|
return {
|
||||||
|
buttonText: !canApiKey ? 'Add OAuth Authorization' : 'Add OAuth',
|
||||||
|
buttonVariant: 'secondary',
|
||||||
|
className: 'hover:bg-components-button-secondary-bg',
|
||||||
|
buttonLeftClassName: 'hover:bg-components-button-secondary-bg-hover',
|
||||||
|
buttonRightClassName: 'hover:bg-components-button-secondary-bg-hover',
|
||||||
|
dividerClassName: 'bg-divider-regular opacity-100',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
buttonText: !canApiKey ? 'Use OAuth Authorization' : '',
|
||||||
|
}
|
||||||
|
}, [canApiKey, theme])
|
||||||
|
|
||||||
|
const apiKeyButtonProps: AddApiKeyButtonProps = useMemo(() => {
|
||||||
|
if (theme === 'secondary') {
|
||||||
|
return {
|
||||||
|
provider,
|
||||||
|
buttonVariant: 'secondary',
|
||||||
|
buttonText: !canOAuth ? 'API Key Authorization Configuration' : 'Add API Key',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
provider,
|
||||||
|
buttonText: !canOAuth ? 'API Key Authorization Configuration' : '',
|
||||||
|
buttonVariant: !canOAuth ? 'primary' : 'secondary-accent',
|
||||||
|
}
|
||||||
|
}, [canOAuth, theme, provider])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='flex items-center space-x-1.5'>
|
||||||
|
{
|
||||||
|
canOAuth && (
|
||||||
|
<AddOAuthButton
|
||||||
|
{...oAuthButtonProps}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
showDivider && canOAuth && canApiKey && (
|
||||||
|
<div className='system-2xs-medium-uppercase flex shrink-0 flex-col items-center justify-between text-text-tertiary'>
|
||||||
|
<div className='h-2 w-[1px] bg-divider-subtle'></div>
|
||||||
|
or
|
||||||
|
<div className='h-2 w-[1px] bg-divider-subtle'></div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
canApiKey && (
|
||||||
|
<AddApiKeyButton
|
||||||
|
{...apiKeyButtonProps}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Authorize)
|
||||||
@ -0,0 +1,205 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react'
|
||||||
|
import {
|
||||||
|
RiArrowDownSLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
PortalToFollowElem,
|
||||||
|
PortalToFollowElemContent,
|
||||||
|
PortalToFollowElemTrigger,
|
||||||
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
import Confirm from '@/app/components/base/confirm'
|
||||||
|
import Authorize from '../authorize'
|
||||||
|
import type { Credential } from '../types'
|
||||||
|
import { CredentialTypeEnum } from '../types'
|
||||||
|
import ApiKeyModal from '../authorize/api-key-modal'
|
||||||
|
import Item from './item'
|
||||||
|
import {
|
||||||
|
useDeletePluginToolCredential,
|
||||||
|
useInvalidPluginToolCredentialInfo,
|
||||||
|
useSetPluginToolDefaultCredential,
|
||||||
|
} from '@/service/use-plugins-auth'
|
||||||
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
|
|
||||||
|
type AuthorizedProps = {
|
||||||
|
provider: string
|
||||||
|
credentials: Credential[]
|
||||||
|
canOAuth?: boolean
|
||||||
|
canApiKey?: boolean
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
const Authorized = ({
|
||||||
|
provider,
|
||||||
|
credentials,
|
||||||
|
canOAuth,
|
||||||
|
canApiKey,
|
||||||
|
disabled,
|
||||||
|
}: AuthorizedProps) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { notify } = useToastContext()
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const oAuthCredentials = credentials.filter(credential => credential.credential_type === CredentialTypeEnum.OAUTH2)
|
||||||
|
const apiKeyCredentials = credentials.filter(credential => credential.credential_type === CredentialTypeEnum.API_KEY)
|
||||||
|
const pendingOperationCredentialId = useRef<string | null>(null)
|
||||||
|
const [deleteCredentialId, setDeleteCredentialId] = useState<string | null>(null)
|
||||||
|
const { mutateAsync: deletePluginToolCredential } = useDeletePluginToolCredential(provider)
|
||||||
|
const invalidatePluginToolCredentialInfo = useInvalidPluginToolCredentialInfo(provider)
|
||||||
|
const openConfirm = useCallback((credentialId?: string) => {
|
||||||
|
if (credentialId)
|
||||||
|
pendingOperationCredentialId.current = credentialId
|
||||||
|
|
||||||
|
setDeleteCredentialId(pendingOperationCredentialId.current)
|
||||||
|
}, [])
|
||||||
|
const closeConfirm = useCallback(() => {
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
pendingOperationCredentialId.current = null
|
||||||
|
}, [])
|
||||||
|
const handleConfirm = useCallback(async () => {
|
||||||
|
if (!pendingOperationCredentialId.current) {
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await deletePluginToolCredential({ credential_id: pendingOperationCredentialId.current })
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
invalidatePluginToolCredentialInfo()
|
||||||
|
setDeleteCredentialId(null)
|
||||||
|
pendingOperationCredentialId.current = null
|
||||||
|
}, [deletePluginToolCredential, invalidatePluginToolCredentialInfo, notify, t])
|
||||||
|
const [editValues, setEditValues] = useState<Record<string, any> | null>(null)
|
||||||
|
const handleEdit = useCallback((id: string, values: Record<string, any>) => {
|
||||||
|
pendingOperationCredentialId.current = id
|
||||||
|
setEditValues(values)
|
||||||
|
}, [])
|
||||||
|
const handleRemove = useCallback(() => {
|
||||||
|
setDeleteCredentialId(pendingOperationCredentialId.current)
|
||||||
|
}, [])
|
||||||
|
const { mutateAsync: setPluginToolDefaultCredential } = useSetPluginToolDefaultCredential(provider)
|
||||||
|
const handleSetDefault = useCallback(async (id: string) => {
|
||||||
|
await setPluginToolDefaultCredential(id)
|
||||||
|
notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('common.api.actionSuccess'),
|
||||||
|
})
|
||||||
|
invalidatePluginToolCredentialInfo()
|
||||||
|
}, [setPluginToolDefaultCredential, invalidatePluginToolCredentialInfo, notify, t])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PortalToFollowElem
|
||||||
|
open={isOpen}
|
||||||
|
onOpenChange={setIsOpen}
|
||||||
|
placement='bottom-start'
|
||||||
|
offset={8}
|
||||||
|
triggerPopupSameWidth
|
||||||
|
>
|
||||||
|
<PortalToFollowElemTrigger
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className={cn(
|
||||||
|
'w-full',
|
||||||
|
isOpen && 'bg-components-button-secondary-bg-hover',
|
||||||
|
)}>
|
||||||
|
<Indicator className='mr-2' />
|
||||||
|
{credentials.length} Authorizations
|
||||||
|
<RiArrowDownSLine className='ml-0.5 h-4 w-4' />
|
||||||
|
</Button>
|
||||||
|
</PortalToFollowElemTrigger>
|
||||||
|
<PortalToFollowElemContent className='z-[100]'>
|
||||||
|
<div className='max-h-[360px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
|
||||||
|
<div className='py-1'>
|
||||||
|
{
|
||||||
|
!!oAuthCredentials.length && (
|
||||||
|
<div className='p-1'>
|
||||||
|
<div className='system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary'>
|
||||||
|
OAuth
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
oAuthCredentials.map(credential => (
|
||||||
|
<Item
|
||||||
|
key={credential.id}
|
||||||
|
credential={credential}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!!apiKeyCredentials.length && (
|
||||||
|
<div className='p-1'>
|
||||||
|
<div className='system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary'>
|
||||||
|
API Keys
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
apiKeyCredentials.map(credential => (
|
||||||
|
<Item
|
||||||
|
key={credential.id}
|
||||||
|
credential={credential}
|
||||||
|
disabled={disabled}
|
||||||
|
onDelete={openConfirm}
|
||||||
|
onEdit={handleEdit}
|
||||||
|
onSetDefault={handleSetDefault}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className='h-[1px] bg-divider-subtle'></div>
|
||||||
|
<div className='p-2'>
|
||||||
|
<Authorize
|
||||||
|
provider={provider}
|
||||||
|
theme='secondary'
|
||||||
|
showDivider={false}
|
||||||
|
canOAuth={canOAuth}
|
||||||
|
canApiKey={canApiKey}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PortalToFollowElemContent>
|
||||||
|
</PortalToFollowElem>
|
||||||
|
{
|
||||||
|
deleteCredentialId && (
|
||||||
|
<Confirm
|
||||||
|
isShow
|
||||||
|
title='Are you sure?'
|
||||||
|
content='content'
|
||||||
|
onCancel={closeConfirm}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!!editValues && (
|
||||||
|
<ApiKeyModal
|
||||||
|
provider={provider}
|
||||||
|
editValues={editValues}
|
||||||
|
onClose={() => {
|
||||||
|
setEditValues(null)
|
||||||
|
pendingOperationCredentialId.current = null
|
||||||
|
}}
|
||||||
|
onRemove={handleRemove}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Authorized)
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
} from 'react'
|
||||||
|
import {
|
||||||
|
RiDeleteBinLine,
|
||||||
|
RiEditLine,
|
||||||
|
RiEqualizer2Line,
|
||||||
|
} from '@remixicon/react'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
import Badge from '@/app/components/base/badge'
|
||||||
|
import ActionButton from '@/app/components/base/action-button'
|
||||||
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import type { Credential } from '../types'
|
||||||
|
import { CredentialTypeEnum } from '../types'
|
||||||
|
|
||||||
|
type ItemProps = {
|
||||||
|
credential: Credential
|
||||||
|
disabled?: boolean
|
||||||
|
onDelete?: (id: string) => void
|
||||||
|
onEdit?: (id: string, values: Record<string, any>) => void
|
||||||
|
onSetDefault?: (id: string) => void
|
||||||
|
}
|
||||||
|
const Item = ({
|
||||||
|
credential,
|
||||||
|
disabled,
|
||||||
|
onDelete,
|
||||||
|
onEdit,
|
||||||
|
onSetDefault,
|
||||||
|
}: ItemProps) => {
|
||||||
|
const isOAuth = credential.credential_type === CredentialTypeEnum.OAUTH2
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={credential.id}
|
||||||
|
className='group flex h-8 items-center rounded-lg p-1 hover:bg-state-base-hover'
|
||||||
|
>
|
||||||
|
<div className='flex grow items-center space-x-1.5 pl-2'>
|
||||||
|
<Indicator className='mr-1.5' />
|
||||||
|
<div
|
||||||
|
className='system-md-regular truncate text-text-secondary'
|
||||||
|
title={credential.name}
|
||||||
|
>
|
||||||
|
{credential.name}
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
credential.is_default && (
|
||||||
|
<Badge>
|
||||||
|
Default
|
||||||
|
</Badge>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className='ml-2 hidden shrink-0 items-center group-hover:flex'>
|
||||||
|
<Button
|
||||||
|
size='small'
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => onSetDefault?.(credential.id)}
|
||||||
|
>
|
||||||
|
Set as default
|
||||||
|
</Button>
|
||||||
|
{
|
||||||
|
isOAuth && (
|
||||||
|
<Tooltip popupContent='rename'>
|
||||||
|
<ActionButton>
|
||||||
|
<RiEditLine className='h-4 w-4 text-text-tertiary' />
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
!isOAuth && (
|
||||||
|
<Tooltip popupContent='edit'>
|
||||||
|
<ActionButton
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => onEdit?.(credential.id, credential.credentials)}
|
||||||
|
>
|
||||||
|
<RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Tooltip popupContent='delete'>
|
||||||
|
<ActionButton
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => onDelete?.(credential.id)}
|
||||||
|
>
|
||||||
|
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
|
||||||
|
</ActionButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Item)
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export { default as PluginAuth } from './plugin-auth'
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import Authorize from './authorize'
|
||||||
|
import Authorized from './authorized'
|
||||||
|
import { useAppContext } from '@/context/app-context'
|
||||||
|
import { useGetPluginToolCredentialInfo } from '@/service/use-plugins-auth'
|
||||||
|
import { CredentialTypeEnum } from './types'
|
||||||
|
|
||||||
|
type PluginAuthProps = {
|
||||||
|
provider?: string
|
||||||
|
}
|
||||||
|
const PluginAuth = ({
|
||||||
|
provider = '',
|
||||||
|
}: PluginAuthProps) => {
|
||||||
|
const { data } = useGetPluginToolCredentialInfo(provider)
|
||||||
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
|
const isAuthorized = !!data?.credentials.length
|
||||||
|
const canOAuth = data?.supported_credential_types.includes(CredentialTypeEnum.OAUTH2)
|
||||||
|
const canApiKey = data?.supported_credential_types.includes(CredentialTypeEnum.API_KEY)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
!isAuthorized && (
|
||||||
|
<Authorize
|
||||||
|
provider={provider}
|
||||||
|
canOAuth={canOAuth}
|
||||||
|
canApiKey={canApiKey}
|
||||||
|
disabled={!isCurrentWorkspaceManager}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isAuthorized && (
|
||||||
|
<Authorized
|
||||||
|
provider={provider}
|
||||||
|
credentials={data?.credentials}
|
||||||
|
canOAuth={canOAuth}
|
||||||
|
canApiKey={canApiKey}
|
||||||
|
disabled={!isCurrentWorkspaceManager}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(PluginAuth)
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
export enum CredentialTypeEnum {
|
||||||
|
OAUTH2 = 'oauth2',
|
||||||
|
API_KEY = 'api-key',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Credential = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
provider: string
|
||||||
|
credential_type: CredentialTypeEnum
|
||||||
|
is_default: boolean
|
||||||
|
credentials: Record<string, any>
|
||||||
|
}
|
||||||
@ -1,52 +0,0 @@
|
|||||||
import { memo } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import { RiExternalLinkLine } from '@remixicon/react'
|
|
||||||
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
|
|
||||||
import Modal from '@/app/components/base/modal/modal'
|
|
||||||
|
|
||||||
export type ApiKeyModalProps = {
|
|
||||||
onClose?: () => void
|
|
||||||
}
|
|
||||||
const ApiKeyModal = ({
|
|
||||||
onClose,
|
|
||||||
}: ApiKeyModalProps) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
size='md'
|
|
||||||
title='API Key Authorization Configuration'
|
|
||||||
subTitle='After configuring credentials, all members within the workspace can use this tool when orchestrating applications.'
|
|
||||||
onClose={onClose}
|
|
||||||
onCancel={onClose}
|
|
||||||
footerSlot={
|
|
||||||
<a
|
|
||||||
className='system-xs-regular flex h-8 grow items-center text-text-accent'
|
|
||||||
href=''
|
|
||||||
target='_blank'
|
|
||||||
>
|
|
||||||
Get your API Key from OpenAI
|
|
||||||
<RiExternalLinkLine className='ml-1 h-3 w-3' />
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
bottomSlot={
|
|
||||||
<div className='flex items-center justify-center bg-background-section-burn py-3 text-xs text-text-tertiary'>
|
|
||||||
<Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
|
|
||||||
{t('common.modelProvider.encrypted.front')}
|
|
||||||
<a
|
|
||||||
className='mx-1 text-text-accent'
|
|
||||||
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>oauth</div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(ApiKeyModal)
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
import {
|
|
||||||
memo,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import {
|
|
||||||
RiArrowDownSLine,
|
|
||||||
RiDeleteBinLine,
|
|
||||||
RiEditLine,
|
|
||||||
} from '@remixicon/react'
|
|
||||||
import {
|
|
||||||
PortalToFollowElem,
|
|
||||||
PortalToFollowElemContent,
|
|
||||||
PortalToFollowElemTrigger,
|
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
|
||||||
import Button from '@/app/components/base/button'
|
|
||||||
import Indicator from '@/app/components/header/indicator'
|
|
||||||
import Badge from '@/app/components/base/badge'
|
|
||||||
import ActionButton from '@/app/components/base/action-button'
|
|
||||||
import cn from '@/utils/classnames'
|
|
||||||
import AddOauthButton from './add-oauth-button'
|
|
||||||
import AddApiKeyButton from './add-api-key-button'
|
|
||||||
|
|
||||||
const Authorization = () => {
|
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PortalToFollowElem
|
|
||||||
open={isOpen}
|
|
||||||
onOpenChange={setIsOpen}
|
|
||||||
placement='bottom-start'
|
|
||||||
offset={8}
|
|
||||||
triggerPopupSameWidth
|
|
||||||
>
|
|
||||||
<PortalToFollowElemTrigger
|
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
|
||||||
asChild
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
className={cn(
|
|
||||||
'w-full',
|
|
||||||
isOpen && 'bg-components-button-secondary-bg-hover',
|
|
||||||
)}>
|
|
||||||
<Indicator className='mr-2' />
|
|
||||||
4 Authorizations
|
|
||||||
<RiArrowDownSLine className='ml-0.5 h-4 w-4' />
|
|
||||||
</Button>
|
|
||||||
</PortalToFollowElemTrigger>
|
|
||||||
<PortalToFollowElemContent className='z-[11]'>
|
|
||||||
<div className='max-h-[360px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
|
|
||||||
<div className='py-1'>
|
|
||||||
<div className='p-1'>
|
|
||||||
<div className='system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary'>
|
|
||||||
OAuth
|
|
||||||
</div>
|
|
||||||
<div className='flex items-center rounded-lg p-1 hover:bg-state-base-hover'>
|
|
||||||
<div className='flex grow items-center space-x-1.5 pl-2'>
|
|
||||||
<Indicator className='mr-1.5' />
|
|
||||||
<div
|
|
||||||
className='system-md-regular truncate text-text-secondary'
|
|
||||||
title='Auth 1'
|
|
||||||
>
|
|
||||||
Auth 1
|
|
||||||
</div>
|
|
||||||
<Badge>
|
|
||||||
Default
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
<div className='ml-2 flex shrink-0 items-center'>
|
|
||||||
<ActionButton>
|
|
||||||
<RiEditLine className='h-4 w-4 text-text-tertiary' />
|
|
||||||
</ActionButton>
|
|
||||||
<ActionButton>
|
|
||||||
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
|
|
||||||
</ActionButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='p-1'>
|
|
||||||
<div className='system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary'>
|
|
||||||
API Keys
|
|
||||||
</div>
|
|
||||||
<div className='flex items-center rounded-lg p-1 hover:bg-state-base-hover'>
|
|
||||||
<div className='flex grow items-center space-x-1.5 pl-2'>
|
|
||||||
<Indicator className='mr-1.5' />
|
|
||||||
<div
|
|
||||||
className='system-md-regular truncate text-text-secondary'
|
|
||||||
title='Production'
|
|
||||||
>
|
|
||||||
Production
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='ml-2 flex shrink-0 items-center space-x-1'>
|
|
||||||
<Badge>
|
|
||||||
0.2
|
|
||||||
</Badge>
|
|
||||||
<Badge>
|
|
||||||
ENTERPRISE
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='h-[1px] bg-divider-subtle'></div>
|
|
||||||
<div className='flex items-center space-x-1.5 p-2'>
|
|
||||||
<AddOauthButton
|
|
||||||
buttonVariant='secondary'
|
|
||||||
buttonText='Add OAuth'
|
|
||||||
className='hover:bg-components-button-secondary-bg'
|
|
||||||
buttonLeftClassName='hover:bg-components-button-secondary-bg-hover'
|
|
||||||
buttonRightClassName='hover:bg-components-button-secondary-bg-hover'
|
|
||||||
dividerClassName='bg-divider-regular opacity-100'
|
|
||||||
/>
|
|
||||||
<AddApiKeyButton
|
|
||||||
buttonVariant='secondary'
|
|
||||||
buttonText='Add API Key'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</PortalToFollowElemContent>
|
|
||||||
</PortalToFollowElem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(Authorization)
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
import {
|
|
||||||
memo,
|
|
||||||
} from 'react'
|
|
||||||
import AddOAuthButton from './add-oauth-button'
|
|
||||||
import AddApiKeyButton from './add-api-key-button'
|
|
||||||
|
|
||||||
const ToolAuth = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='flex items-center space-x-1.5'>
|
|
||||||
<AddOAuthButton />
|
|
||||||
<div className='system-2xs-medium-uppercase flex shrink-0 flex-col items-center justify-between text-text-tertiary'>
|
|
||||||
<div className='h-2 w-[1px] bg-divider-subtle'></div>
|
|
||||||
or
|
|
||||||
<div className='h-2 w-[1px] bg-divider-subtle'></div>
|
|
||||||
</div>
|
|
||||||
<AddApiKeyButton />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(ToolAuth)
|
|
||||||
@ -0,0 +1,138 @@
|
|||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
useQuery,
|
||||||
|
} from '@tanstack/react-query'
|
||||||
|
import { get, post } from './base'
|
||||||
|
import { useInvalid } from './use-base'
|
||||||
|
import type {
|
||||||
|
Credential,
|
||||||
|
CredentialTypeEnum,
|
||||||
|
} from '@/app/components/plugins/plugin-auth/types'
|
||||||
|
import type { FormSchema } from '@/app/components/base/form/types'
|
||||||
|
|
||||||
|
const NAME_SPACE = 'plugins-auth'
|
||||||
|
|
||||||
|
export const useGetPluginToolCredentialInfo = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
enabled: !!provider,
|
||||||
|
queryKey: [NAME_SPACE, 'credential-info', provider],
|
||||||
|
queryFn: () => get<{
|
||||||
|
supported_credential_types: string[]
|
||||||
|
credentials: Credential[]
|
||||||
|
is_oauth_custom_client_enabled: boolean
|
||||||
|
}>(`/workspaces/current/tool-provider/builtin/${provider}/credential/info`),
|
||||||
|
staleTime: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useInvalidPluginToolCredentialInfo = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useInvalid([NAME_SPACE, 'credential-info', provider])
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSetPluginToolDefaultCredential = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (id: string) => {
|
||||||
|
return post(`/workspaces/current/tool-provider/builtin/${provider}/default-credential`, { body: { id } })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetPluginToolCredentialList = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'credential-list', provider],
|
||||||
|
queryFn: () => get(`/workspaces/current/tool-provider/builtin/${provider}/credentials`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAddPluginToolCredential = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (params: {
|
||||||
|
credentials: Record<string, any>
|
||||||
|
type: CredentialTypeEnum
|
||||||
|
name?: string
|
||||||
|
}) => {
|
||||||
|
return post(`/workspaces/current/tool-provider/builtin/${provider}/add`, { body: params })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useUpdatePluginToolCredential = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (params: {
|
||||||
|
credentials: Record<string, any>
|
||||||
|
type: CredentialTypeEnum
|
||||||
|
name?: string
|
||||||
|
}) => {
|
||||||
|
return post(`/workspaces/current/tool-provider/builtin/${provider}/update`, { body: params })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useDeletePluginToolCredential = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (params: { credential_id: string }) => {
|
||||||
|
return post(`/workspaces/current/tool-provider/builtin/${provider}/delete`, { body: params })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetPluginToolCredentialSchema = (
|
||||||
|
provider: string,
|
||||||
|
credential_type: CredentialTypeEnum,
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'credential-schema', provider, credential_type],
|
||||||
|
queryFn: () => get<FormSchema[]>(`/workspaces/current/tool-provider/builtin/${provider}/credential/schema/${credential_type}`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetPluginToolOAuthUrl = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'oauth-url', provider],
|
||||||
|
queryFn: () => get(`oauth/plugin/${provider}/tool/authorization-url`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetPluginToolOAuthClientSchema = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'oauth-client-schema', provider],
|
||||||
|
queryFn: () => get(`/workspaces/current/tool-provider/builtin/${provider}/oauth/client-schema`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSetPluginToolOAuthCustomClient = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (params) => {
|
||||||
|
return post(`/workspaces/current/tool-provider/builtin/${provider}/oauth/custom-client`, { body: params })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGetPluginToolOAuthCustomClientSchema = (
|
||||||
|
provider: string,
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: [NAME_SPACE, 'oauth-custom-client-schema', provider],
|
||||||
|
queryFn: () => get(`/workspaces/current/tool-provider/builtin/${provider}/oauth/custom-client`),
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue