tool oauth

This commit is contained in:
zxhlyh
2025-07-08 18:20:30 +08:00
parent c53d5c105b
commit 0f1be60daa
23 changed files with 978 additions and 268 deletions

View File

@ -0,0 +1,46 @@
import {
memo,
useState,
} from 'react'
import Button from '@/app/components/base/button'
import type { ButtonProps } from '@/app/components/base/button'
import ApiKeyModal from './api-key-modal'
export type AddApiKeyButtonProps = {
provider?: string
buttonVariant?: ButtonProps['variant']
buttonText?: string
disabled?: boolean
}
const AddApiKeyButton = ({
provider = '',
buttonVariant = 'secondary-accent',
buttonText = 'use api key',
disabled,
}: AddApiKeyButtonProps) => {
const [isApiKeyModalOpen, setIsApiKeyModalOpen] = useState(false)
return (
<>
<Button
className='grow'
variant={buttonVariant}
onClick={() => setIsApiKeyModalOpen(true)}
disabled={disabled}
>
{buttonText}
</Button>
{
isApiKeyModalOpen && (
<ApiKeyModal
provider={provider}
onClose={() => setIsApiKeyModalOpen(false)}
/>
)
}
</>
)
}
export default memo(AddApiKeyButton)

View File

@ -0,0 +1,72 @@
import {
memo,
useState,
} from 'react'
import { RiEqualizer2Line } from '@remixicon/react'
import Button from '@/app/components/base/button'
import type { ButtonProps } from '@/app/components/base/button'
import OAuthClientSettings from './oauth-client-settings'
import cn from '@/utils/classnames'
export type AddOAuthButtonProps = {
buttonVariant?: ButtonProps['variant']
buttonText?: string
className?: string
buttonLeftClassName?: string
buttonRightClassName?: string
dividerClassName?: string
disabled?: boolean
}
const AddOAuthButton = ({
buttonVariant = 'primary',
buttonText = 'use oauth',
className,
buttonLeftClassName,
buttonRightClassName,
dividerClassName,
disabled,
}: AddOAuthButtonProps) => {
const [isOAuthSettingsOpen, setIsOAuthSettingsOpen] = useState(false)
return (
<>
<Button
variant={buttonVariant}
className={cn(
'grow px-0 py-0 hover:bg-components-button-primary-bg',
className,
)}
disabled={disabled}
>
<div className={cn(
'flex h-full grow items-center justify-center rounded-l-lg hover:bg-components-button-primary-bg-hover',
buttonLeftClassName,
)}>
{buttonText}
</div>
<div className={cn(
'h-4 w-[1px] bg-text-primary-on-surface opacity-[0.15]',
dividerClassName,
)}></div>
<div
className={cn(
'flex h-full w-8 shrink-0 items-center justify-center rounded-r-lg hover:bg-components-button-primary-bg-hover',
buttonRightClassName,
)}
onClick={() => setIsOAuthSettingsOpen(true)}
>
<RiEqualizer2Line className='h-4 w-4' />
</div>
</Button>
{
isOAuthSettingsOpen && (
<OAuthClientSettings
onClose={() => setIsOAuthSettingsOpen(false)}
/>
)
}
</>
)
}
export default memo(AddOAuthButton)

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,26 @@
import { memo } from 'react'
import Modal from '@/app/components/base/modal/modal'
type OAuthClientSettingsProps = {
onClose?: () => void
}
const OAuthClientSettings = ({
onClose,
}: OAuthClientSettingsProps) => {
return (
<Modal
title='Oauth client settings'
confirmButtonText='Save & Authorize'
cancelButtonText='Save only'
extraButtonText='Cancel'
showExtraButton
extraButtonVariant='secondary'
onExtraButtonClick={onClose}
onClose={onClose}
>
<div>oauth</div>
</Modal>
)
}
export default memo(OAuthClientSettings)