mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
tool oauth
This commit is contained in:
@ -14,6 +14,7 @@ import AuthForm from '@/app/components/base/form/form-scenarios/auth'
|
||||
import type { FromRefObject } from '@/app/components/base/form/types'
|
||||
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import type { PluginPayload } from '../types'
|
||||
import {
|
||||
useAddPluginCredentialHook,
|
||||
@ -21,6 +22,7 @@ import {
|
||||
useInvalidPluginCredentialInfoHook,
|
||||
useUpdatePluginCredentialHook,
|
||||
} from '../hooks/use-credential'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
|
||||
export type ApiKeyModalProps = {
|
||||
pluginPayload: PluginPayload
|
||||
@ -38,18 +40,25 @@ const ApiKeyModal = ({
|
||||
}: ApiKeyModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const { data = [] } = useGetPluginCredentialSchemaHook(pluginPayload, CredentialTypeEnum.API_KEY)
|
||||
const { data = [], isLoading } = useGetPluginCredentialSchemaHook(pluginPayload, CredentialTypeEnum.API_KEY)
|
||||
const formSchemas = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
type: FormTypeEnum.textInput,
|
||||
name: '__name__',
|
||||
label: 'Authorization name',
|
||||
label: t('plugin.auth.authorizationName'),
|
||||
required: false,
|
||||
},
|
||||
...data,
|
||||
]
|
||||
}, [data])
|
||||
}, [data, t])
|
||||
const defaultValues = formSchemas.reduce((acc, schema) => {
|
||||
if (schema.default)
|
||||
acc[schema.name] = schema.default
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
const secretInput = formSchemas.find(schema => schema.type === FormTypeEnum.secretInput)
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const { mutateAsync: addPluginCredential } = useAddPluginCredentialHook(pluginPayload)
|
||||
const { mutateAsync: updatePluginCredential } = useUpdatePluginCredentialHook(pluginPayload)
|
||||
const invalidatePluginCredentialInfo = useInvalidPluginCredentialInfoHook(pluginPayload)
|
||||
@ -77,7 +86,6 @@ const ApiKeyModal = ({
|
||||
await updatePluginCredential({
|
||||
credentials: transformedValues,
|
||||
credential_id: __credential_id__,
|
||||
type: CredentialTypeEnum.API_KEY,
|
||||
name: __name__ || '',
|
||||
})
|
||||
}
|
||||
@ -100,19 +108,21 @@ const ApiKeyModal = ({
|
||||
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.'
|
||||
title={t('plugin.auth.useApiAuth')}
|
||||
subTitle={t('plugin.auth.useApiAuthDesc')}
|
||||
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>
|
||||
secretInput && (
|
||||
<a
|
||||
className='system-xs-regular flex h-8 grow items-center text-text-accent'
|
||||
href={secretInput?.url}
|
||||
target='_blank'
|
||||
>
|
||||
{renderI18nObject(secretInput?.help as any)}
|
||||
<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'>
|
||||
@ -133,12 +143,23 @@ const ApiKeyModal = ({
|
||||
onExtraButtonClick={onRemove}
|
||||
disabled={disabled}
|
||||
>
|
||||
<AuthForm
|
||||
ref={formRef}
|
||||
formSchemas={formSchemas}
|
||||
defaultValues={editValues}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{
|
||||
isLoading && (
|
||||
<div className='flex h-40 items-center justify-center'>
|
||||
<Loading />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!isLoading && !!data.length && (
|
||||
<AuthForm
|
||||
ref={formRef}
|
||||
formSchemas={formSchemas}
|
||||
defaultValues={editValues || defaultValues}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import {
|
||||
memo,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AddOAuthButton from './add-oauth-button'
|
||||
import type { AddOAuthButtonProps } from './add-oauth-button'
|
||||
import AddApiKeyButton from './add-api-key-button'
|
||||
@ -24,10 +25,11 @@ const Authorize = ({
|
||||
canApiKey,
|
||||
disabled,
|
||||
}: AuthorizeProps) => {
|
||||
const { t } = useTranslation()
|
||||
const oAuthButtonProps: AddOAuthButtonProps = useMemo(() => {
|
||||
if (theme === 'secondary') {
|
||||
return {
|
||||
buttonText: !canApiKey ? 'Add OAuth Authorization' : 'Add OAuth',
|
||||
buttonText: !canApiKey ? t('plugin.auth.useOAuthAuth') : t('plugin.auth.addOAuth'),
|
||||
buttonVariant: 'secondary',
|
||||
className: 'hover:bg-components-button-secondary-bg',
|
||||
buttonLeftClassName: 'hover:bg-components-button-secondary-bg-hover',
|
||||
@ -38,25 +40,25 @@ const Authorize = ({
|
||||
}
|
||||
|
||||
return {
|
||||
buttonText: !canApiKey ? 'Use OAuth Authorization' : 'Use OAuth',
|
||||
buttonText: !canApiKey ? t('plugin.auth.useOAuthAuth') : t('plugin.auth.addOAuth'),
|
||||
pluginPayload,
|
||||
}
|
||||
}, [canApiKey, theme, pluginPayload])
|
||||
}, [canApiKey, theme, pluginPayload, t])
|
||||
|
||||
const apiKeyButtonProps: AddApiKeyButtonProps = useMemo(() => {
|
||||
if (theme === 'secondary') {
|
||||
return {
|
||||
pluginPayload,
|
||||
buttonVariant: 'secondary',
|
||||
buttonText: !canOAuth ? 'API Key Authorization Configuration' : 'Add API Key',
|
||||
buttonText: !canOAuth ? t('plugin.auth.useApiAuth') : t('plugin.auth.addApi'),
|
||||
}
|
||||
}
|
||||
return {
|
||||
pluginPayload,
|
||||
buttonText: !canOAuth ? 'API Key Authorization Configuration' : 'Use API Key',
|
||||
buttonText: !canOAuth ? t('plugin.auth.useApiAuth') : t('plugin.auth.addApi'),
|
||||
buttonVariant: !canOAuth ? 'primary' : 'secondary-accent',
|
||||
}
|
||||
}, [canOAuth, theme, pluginPayload])
|
||||
}, [canOAuth, theme, pluginPayload, t])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
@ -26,6 +27,7 @@ const AuthorizedInNode = ({
|
||||
onAuthorizationItemClick,
|
||||
credentialId,
|
||||
}: AuthorizedInNodeProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const {
|
||||
canApiKey,
|
||||
@ -37,11 +39,11 @@ const AuthorizedInNode = ({
|
||||
let label = ''
|
||||
let removed = false
|
||||
if (!credentialId) {
|
||||
label = 'Workspace default'
|
||||
label = t('plugin.auth.workspaceDefault')
|
||||
}
|
||||
else {
|
||||
const credential = credentials.find(c => c.id === credentialId)
|
||||
label = credential ? credential.name : 'Auth removed'
|
||||
label = credential ? credential.name : t('plugin.auth.authRemoved')
|
||||
removed = !credential
|
||||
}
|
||||
return (
|
||||
@ -65,13 +67,13 @@ const AuthorizedInNode = ({
|
||||
/>
|
||||
</Button>
|
||||
)
|
||||
}, [credentialId, credentials])
|
||||
}, [credentialId, credentials, t])
|
||||
const extraAuthorizationItems: Credential[] = [
|
||||
{
|
||||
id: '__workspace_default__',
|
||||
name: 'Workspace default',
|
||||
name: t('plugin.auth.workspaceDefault'),
|
||||
provider: '',
|
||||
is_default: false,
|
||||
is_default: !credentialId,
|
||||
isWorkspaceDefault: true,
|
||||
},
|
||||
]
|
||||
@ -100,6 +102,7 @@ const AuthorizedInNode = ({
|
||||
disableSetDefault
|
||||
onItemClick={handleAuthorizationItemClick}
|
||||
extraAuthorizationItems={extraAuthorizationItems}
|
||||
showItemSelectedIcon
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ import {
|
||||
useDeletePluginCredentialHook,
|
||||
useInvalidPluginCredentialInfoHook,
|
||||
useSetPluginDefaultCredentialHook,
|
||||
useUpdatePluginCredentialHook,
|
||||
} from '../hooks/use-credential'
|
||||
|
||||
type AuthorizedProps = {
|
||||
@ -49,6 +50,7 @@ type AuthorizedProps = {
|
||||
disableSetDefault?: boolean
|
||||
onItemClick?: (id: string) => void
|
||||
extraAuthorizationItems?: Credential[]
|
||||
showItemSelectedIcon?: boolean
|
||||
}
|
||||
const Authorized = ({
|
||||
pluginPayload,
|
||||
@ -66,6 +68,7 @@ const Authorized = ({
|
||||
disableSetDefault,
|
||||
onItemClick,
|
||||
extraAuthorizationItems,
|
||||
showItemSelectedIcon,
|
||||
}: AuthorizedProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
@ -125,6 +128,17 @@ const Authorized = ({
|
||||
})
|
||||
invalidatePluginCredentialInfo()
|
||||
}, [setPluginDefaultCredential, invalidatePluginCredentialInfo, notify, t])
|
||||
const { mutateAsync: updatePluginCredential } = useUpdatePluginCredentialHook(pluginPayload)
|
||||
const handleRename = useCallback(async (payload: {
|
||||
credential_id: string
|
||||
name: string
|
||||
}) => {
|
||||
await updatePluginCredential(payload)
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('common.api.actionSuccess'),
|
||||
})
|
||||
}, [updatePluginCredential, notify, t])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -149,7 +163,12 @@ const Authorized = ({
|
||||
isOpen && 'bg-components-button-secondary-bg-hover',
|
||||
)}>
|
||||
<Indicator className='mr-2' />
|
||||
{credentials.length} Authorizations
|
||||
{credentials.length}
|
||||
{
|
||||
credentials.length > 1
|
||||
? t('plugin.auth.authorizations')
|
||||
: t('plugin.auth.authorization')
|
||||
}
|
||||
<RiArrowDownSLine className='ml-0.5 h-4 w-4' />
|
||||
</Button>
|
||||
)
|
||||
@ -160,31 +179,35 @@ const Authorized = ({
|
||||
'max-h-[360px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg',
|
||||
popupClassName,
|
||||
)}>
|
||||
{
|
||||
!!extraAuthorizationItems?.length && (
|
||||
<div className='p-1'>
|
||||
{
|
||||
extraAuthorizationItems.map(credential => (
|
||||
<Item
|
||||
key={credential.id}
|
||||
credential={credential}
|
||||
disabled={disabled}
|
||||
onItemClick={onItemClick}
|
||||
disableRename
|
||||
disableEdit
|
||||
disableDelete
|
||||
disableSetDefault
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='py-1'>
|
||||
{
|
||||
!!extraAuthorizationItems?.length && (
|
||||
<div className='p-1'>
|
||||
{
|
||||
extraAuthorizationItems.map(credential => (
|
||||
<Item
|
||||
key={credential.id}
|
||||
credential={credential}
|
||||
disabled={disabled}
|
||||
onItemClick={onItemClick}
|
||||
disableRename
|
||||
disableEdit
|
||||
disableDelete
|
||||
disableSetDefault
|
||||
showSelectedIcon={showItemSelectedIcon}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!!oAuthCredentials.length && (
|
||||
<div className='p-1'>
|
||||
<div className='system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary'>
|
||||
<div className={cn(
|
||||
'system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary',
|
||||
showItemSelectedIcon && 'pl-7',
|
||||
)}>
|
||||
OAuth
|
||||
</div>
|
||||
{
|
||||
@ -192,6 +215,14 @@ const Authorized = ({
|
||||
<Item
|
||||
key={credential.id}
|
||||
credential={credential}
|
||||
disabled={disabled}
|
||||
disableEdit
|
||||
onDelete={openConfirm}
|
||||
onSetDefault={handleSetDefault}
|
||||
onRename={handleRename}
|
||||
disableSetDefault={disableSetDefault}
|
||||
onItemClick={onItemClick}
|
||||
showSelectedIcon={showItemSelectedIcon}
|
||||
/>
|
||||
))
|
||||
}
|
||||
@ -201,7 +232,10 @@ const Authorized = ({
|
||||
{
|
||||
!!apiKeyCredentials.length && (
|
||||
<div className='p-1'>
|
||||
<div className='system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary'>
|
||||
<div className={cn(
|
||||
'system-xs-medium px-3 pb-0.5 pt-1 text-text-tertiary',
|
||||
showItemSelectedIcon && 'pl-7',
|
||||
)}>
|
||||
API Keys
|
||||
</div>
|
||||
{
|
||||
@ -214,7 +248,10 @@ const Authorized = ({
|
||||
onEdit={handleEdit}
|
||||
onSetDefault={handleSetDefault}
|
||||
disableSetDefault={disableSetDefault}
|
||||
disableRename
|
||||
onItemClick={onItemClick}
|
||||
onRename={handleRename}
|
||||
showSelectedIcon={showItemSelectedIcon}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import {
|
||||
memo,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiCheckLine,
|
||||
RiDeleteBinLine,
|
||||
RiEditLine,
|
||||
RiEqualizer2Line,
|
||||
@ -12,6 +15,8 @@ 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 Input from '@/app/components/base/input'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { Credential } from '../types'
|
||||
import { CredentialTypeEnum } from '../types'
|
||||
|
||||
@ -21,11 +26,16 @@ type ItemProps = {
|
||||
onDelete?: (id: string) => void
|
||||
onEdit?: (id: string, values: Record<string, any>) => void
|
||||
onSetDefault?: (id: string) => void
|
||||
onRename?: (payload: {
|
||||
credential_id: string
|
||||
name: string
|
||||
}) => void
|
||||
disableRename?: boolean
|
||||
disableEdit?: boolean
|
||||
disableDelete?: boolean
|
||||
disableSetDefault?: boolean
|
||||
onItemClick?: (id: string) => void
|
||||
showSelectedIcon?: boolean
|
||||
}
|
||||
const Item = ({
|
||||
credential,
|
||||
@ -33,12 +43,17 @@ const Item = ({
|
||||
onDelete,
|
||||
onEdit,
|
||||
onSetDefault,
|
||||
onRename,
|
||||
disableRename,
|
||||
disableEdit,
|
||||
disableDelete,
|
||||
disableSetDefault,
|
||||
onItemClick,
|
||||
showSelectedIcon,
|
||||
}: ItemProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [renaming, setRenaming] = useState(false)
|
||||
const [renameValue, setRenameValue] = useState(credential.name)
|
||||
const isOAuth = credential.credential_type === CredentialTypeEnum.OAUTH2
|
||||
const showAction = useMemo(() => {
|
||||
return !(disableRename && disableEdit && disableDelete && disableSetDefault)
|
||||
@ -47,27 +62,77 @@ const Item = ({
|
||||
return (
|
||||
<div
|
||||
key={credential.id}
|
||||
className='group flex h-8 items-center rounded-lg p-1 hover:bg-state-base-hover'
|
||||
className={cn(
|
||||
'group flex h-8 items-center rounded-lg p-1 hover:bg-state-base-hover',
|
||||
renaming && 'bg-state-base-hover',
|
||||
)}
|
||||
onClick={() => onItemClick?.(credential.id)}
|
||||
>
|
||||
<div className='flex w-0 grow items-center space-x-1.5 pl-2'>
|
||||
<Indicator className='mr-1.5 shrink-0' />
|
||||
<div
|
||||
className='system-md-regular truncate text-text-secondary'
|
||||
title={credential.name}
|
||||
>
|
||||
{credential.name}
|
||||
</div>
|
||||
{
|
||||
credential.is_default && (
|
||||
<Badge>
|
||||
Default
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
showAction && (
|
||||
renaming && (
|
||||
<div className='flex w-full items-center space-x-1'>
|
||||
<Input
|
||||
wrapperClassName='grow rounded-[6px]'
|
||||
className='h-6'
|
||||
value={renameValue}
|
||||
onChange={e => setRenameValue(e.target.value)}
|
||||
placeholder={t('common.placeholder.input')}
|
||||
/>
|
||||
<Button
|
||||
size='small'
|
||||
variant='primary'
|
||||
onClick={() => {
|
||||
onRename?.({
|
||||
credential_id: credential.id,
|
||||
name: renameValue,
|
||||
})
|
||||
setRenaming(false)
|
||||
}}
|
||||
>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
<Button
|
||||
size='small'
|
||||
onClick={() => setRenaming(false)}
|
||||
>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!renaming && (
|
||||
<div className='flex w-0 grow items-center space-x-1.5'>
|
||||
{
|
||||
showSelectedIcon && (
|
||||
<div className='h-4 w-4'>
|
||||
{
|
||||
credential.is_default && (
|
||||
<RiCheckLine className='h-4 w-4 text-text-accent' />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<Indicator className='ml-2 mr-1.5 shrink-0' />
|
||||
<div
|
||||
className='system-md-regular truncate text-text-secondary'
|
||||
title={credential.name}
|
||||
>
|
||||
{credential.name}
|
||||
</div>
|
||||
{
|
||||
credential.is_default && (
|
||||
<Badge className='shrink-0'>
|
||||
{t('plugin.auth.default')}
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
showAction && !renaming && (
|
||||
<div className='ml-2 hidden shrink-0 items-center group-hover:flex'>
|
||||
{
|
||||
!credential.is_default && !disableSetDefault && (
|
||||
@ -79,14 +144,21 @@ const Item = ({
|
||||
onSetDefault?.(credential.id)
|
||||
}}
|
||||
>
|
||||
Set as default
|
||||
{t('plugin.auth.setDefault')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
{
|
||||
isOAuth && !disableRename && (
|
||||
<Tooltip popupContent='rename'>
|
||||
<ActionButton>
|
||||
!disableRename && (
|
||||
<Tooltip popupContent={t('common.operation.rename')}>
|
||||
<ActionButton
|
||||
disabled={disabled}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setRenaming(true)
|
||||
setRenameValue(credential.name)
|
||||
}}
|
||||
>
|
||||
<RiEditLine className='h-4 w-4 text-text-tertiary' />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
@ -94,7 +166,7 @@ const Item = ({
|
||||
}
|
||||
{
|
||||
!isOAuth && !disableEdit && (
|
||||
<Tooltip popupContent='edit'>
|
||||
<Tooltip popupContent={t('common.operation.edit')}>
|
||||
<ActionButton
|
||||
disabled={disabled}
|
||||
onClick={(e) => {
|
||||
@ -116,15 +188,16 @@ const Item = ({
|
||||
}
|
||||
{
|
||||
!disableDelete && (
|
||||
<Tooltip popupContent='delete'>
|
||||
<Tooltip popupContent={t('common.operation.delete')}>
|
||||
<ActionButton
|
||||
className='hover:bg-transparent'
|
||||
disabled={disabled}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onDelete?.(credential.id)
|
||||
}}
|
||||
>
|
||||
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
|
||||
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary hover:text-text-destructive' />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export { default as PluginAuth } from './plugin-auth'
|
||||
export { default as Authorized } from './authorized'
|
||||
export { default as AuthorizedInNode } from './authorized-in-node'
|
||||
export { default as PluginAuthInAgent } from './plugin-auth-in-agent'
|
||||
export { usePluginAuth } from './hooks/use-plugin-auth'
|
||||
export * from './types'
|
||||
|
||||
119
web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
Normal file
119
web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Authorize from './authorize'
|
||||
import Authorized from './authorized'
|
||||
import type {
|
||||
Credential,
|
||||
PluginPayload,
|
||||
} from './types'
|
||||
import { usePluginAuth } from './hooks/use-plugin-auth'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type PluginAuthInAgentProps = {
|
||||
pluginPayload: PluginPayload
|
||||
credentialId?: string
|
||||
onAuthorizationItemClick?: (id: string) => void
|
||||
}
|
||||
const PluginAuthInAgent = ({
|
||||
pluginPayload,
|
||||
credentialId,
|
||||
onAuthorizationItemClick,
|
||||
}: PluginAuthInAgentProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const {
|
||||
isAuthorized,
|
||||
canOAuth,
|
||||
canApiKey,
|
||||
credentials,
|
||||
disabled,
|
||||
} = usePluginAuth(pluginPayload, true)
|
||||
|
||||
const extraAuthorizationItems: Credential[] = [
|
||||
{
|
||||
id: '__workspace_default__',
|
||||
name: t('plugin.auth.workspaceDefault'),
|
||||
provider: '',
|
||||
is_default: !credentialId,
|
||||
isWorkspaceDefault: true,
|
||||
},
|
||||
]
|
||||
|
||||
const handleAuthorizationItemClick = useCallback((id: string) => {
|
||||
onAuthorizationItemClick?.(id)
|
||||
setIsOpen(false)
|
||||
}, [
|
||||
onAuthorizationItemClick,
|
||||
setIsOpen,
|
||||
])
|
||||
|
||||
const renderTrigger = useCallback((isOpen?: boolean) => {
|
||||
let label = ''
|
||||
let removed = false
|
||||
if (!credentialId) {
|
||||
label = t('plugin.auth.workspaceDefault')
|
||||
}
|
||||
else {
|
||||
const credential = credentials.find(c => c.id === credentialId)
|
||||
label = credential ? credential.name : t('plugin.auth.authRemoved')
|
||||
removed = !credential
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
className={cn(
|
||||
'w-full',
|
||||
isOpen && 'bg-components-button-secondary-bg-hover',
|
||||
removed && 'text-text-destructive',
|
||||
)}>
|
||||
<Indicator
|
||||
className='mr-2'
|
||||
color={removed ? 'red' : 'green'}
|
||||
/>
|
||||
{label}
|
||||
<RiArrowDownSLine className='ml-0.5 h-4 w-4' />
|
||||
</Button>
|
||||
)
|
||||
}, [credentialId, credentials, t])
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
!isAuthorized && (
|
||||
<Authorize
|
||||
pluginPayload={pluginPayload}
|
||||
canOAuth={canOAuth}
|
||||
canApiKey={canApiKey}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
isAuthorized && (
|
||||
<Authorized
|
||||
pluginPayload={pluginPayload}
|
||||
credentials={credentials}
|
||||
canOAuth={canOAuth}
|
||||
canApiKey={canApiKey}
|
||||
disabled={disabled}
|
||||
disableSetDefault
|
||||
onItemClick={handleAuthorizationItemClick}
|
||||
extraAuthorizationItems={extraAuthorizationItems}
|
||||
showItemSelectedIcon
|
||||
renderTrigger={renderTrigger}
|
||||
isOpen={isOpen}
|
||||
onOpenChange={setIsOpen}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(PluginAuthInAgent)
|
||||
Reference in New Issue
Block a user