mirror of https://github.com/langgenius/dify.git
datasource oauth
This commit is contained in:
parent
039a053027
commit
caa2de3344
|
|
@ -1,15 +1,32 @@
|
|||
import { memo } from 'react'
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Item from './item'
|
||||
import Configure from './configure'
|
||||
import type { DataSourceAuth } from './types'
|
||||
import type {
|
||||
DataSourceAuth,
|
||||
DataSourceCredential,
|
||||
} from './types'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import { AuthCategory } from '@/app/components/plugins/plugin-auth/types'
|
||||
import {
|
||||
ApiKeyModal,
|
||||
usePluginAuthAction,
|
||||
} from '@/app/components/plugins/plugin-auth'
|
||||
import { useDataSourceAuthUpdate } from './hooks'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
|
||||
type CardProps = {
|
||||
item: DataSourceAuth
|
||||
disabled?: boolean
|
||||
}
|
||||
const Card = ({
|
||||
item,
|
||||
disabled,
|
||||
}: CardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const {
|
||||
icon,
|
||||
|
|
@ -17,7 +34,56 @@ const Card = ({
|
|||
author,
|
||||
provider,
|
||||
credentials_list,
|
||||
credential_schema,
|
||||
} = item
|
||||
const pluginPayload = {
|
||||
category: AuthCategory.datasource,
|
||||
provider: item.name,
|
||||
}
|
||||
const { handleAuthUpdate } = useDataSourceAuthUpdate()
|
||||
const {
|
||||
deleteCredentialId,
|
||||
doingAction,
|
||||
handleConfirm,
|
||||
handleEdit,
|
||||
handleRemove,
|
||||
handleRename,
|
||||
handleSetDefault,
|
||||
editValues,
|
||||
setEditValues,
|
||||
openConfirm,
|
||||
closeConfirm,
|
||||
pendingOperationCredentialId,
|
||||
} = usePluginAuthAction(pluginPayload, handleAuthUpdate)
|
||||
const handleAction = useCallback((
|
||||
action: string,
|
||||
credentialItem: DataSourceCredential,
|
||||
renamePayload?: Record<string, any>,
|
||||
) => {
|
||||
if (action === 'edit') {
|
||||
handleEdit(
|
||||
credentialItem.id,
|
||||
{
|
||||
...credentialItem.credential,
|
||||
__name__: credentialItem.name,
|
||||
__credential_id__: credentialItem.id,
|
||||
},
|
||||
)
|
||||
}
|
||||
if (action === 'delete')
|
||||
openConfirm(credentialItem.id)
|
||||
|
||||
if (action === 'setDefault')
|
||||
handleSetDefault(credentialItem.id)
|
||||
|
||||
if (action === 'rename')
|
||||
handleRename(renamePayload as any)
|
||||
}, [
|
||||
openConfirm,
|
||||
handleEdit,
|
||||
handleSetDefault,
|
||||
handleRename,
|
||||
])
|
||||
|
||||
return (
|
||||
<div className='rounded-xl bg-background-section-burn'>
|
||||
|
|
@ -36,7 +102,11 @@ const Card = ({
|
|||
{provider}
|
||||
</div>
|
||||
</div>
|
||||
<Configure />
|
||||
<Configure
|
||||
pluginPayload={pluginPayload}
|
||||
item={item}
|
||||
onUpdate={handleAuthUpdate}
|
||||
/>
|
||||
</div>
|
||||
<div className='system-xs-medium flex h-4 items-center pl-3 text-text-tertiary'>
|
||||
Connected workspace
|
||||
|
|
@ -45,9 +115,15 @@ const Card = ({
|
|||
{
|
||||
!!credentials_list.length && (
|
||||
<div className='space-y-1 p-3 pt-2'>
|
||||
<Item />
|
||||
<Item />
|
||||
<Item />
|
||||
{
|
||||
credentials_list.map(credentialItem => (
|
||||
<Item
|
||||
key={credentialItem.id}
|
||||
credentialItem={credentialItem}
|
||||
onAction={handleAction}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -60,6 +136,33 @@ const Card = ({
|
|||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
deleteCredentialId && (
|
||||
<Confirm
|
||||
isShow
|
||||
title={t('datasetDocuments.list.delete.title')}
|
||||
isDisabled={doingAction}
|
||||
onCancel={closeConfirm}
|
||||
onConfirm={handleConfirm}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
!!editValues && (
|
||||
<ApiKeyModal
|
||||
pluginPayload={pluginPayload}
|
||||
onClose={() => {
|
||||
setEditValues(null)
|
||||
pendingOperationCredentialId.current = null
|
||||
}}
|
||||
onUpdate={handleAuthUpdate}
|
||||
formSchemas={credential_schema}
|
||||
editValues={editValues}
|
||||
onRemove={handleRemove}
|
||||
disabled={disabled || doingAction}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,58 @@
|
|||
import { memo } from 'react'
|
||||
import {
|
||||
memo,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import {
|
||||
RiAddLine,
|
||||
RiEqualizer2Line,
|
||||
} 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 {
|
||||
AddApiKeyButton,
|
||||
AddOAuthButton,
|
||||
} from '@/app/components/plugins/plugin-auth'
|
||||
import type { DataSourceAuth } from './types'
|
||||
import type {
|
||||
AddApiKeyButtonProps,
|
||||
AddOAuthButtonProps,
|
||||
PluginPayload,
|
||||
} from '@/app/components/plugins/plugin-auth/types'
|
||||
|
||||
type ConfigureProps = {
|
||||
item: DataSourceAuth
|
||||
pluginPayload: PluginPayload
|
||||
onUpdate?: () => void
|
||||
disabled?: boolean
|
||||
}
|
||||
const Configure = ({
|
||||
item,
|
||||
pluginPayload,
|
||||
onUpdate,
|
||||
disabled,
|
||||
}: ConfigureProps) => {
|
||||
const { t } = useTranslation()
|
||||
const canApiKey = item.credential_schema?.length
|
||||
const oAuthData = item.oauth_schema || {}
|
||||
const canOAuth = oAuthData.client_schema?.length
|
||||
const oAuthButtonProps: AddOAuthButtonProps = useMemo(() => {
|
||||
return {
|
||||
buttonText: t('plugin.auth.addOAuth'),
|
||||
pluginPayload,
|
||||
}
|
||||
}, [pluginPayload, t])
|
||||
|
||||
const apiKeyButtonProps: AddApiKeyButtonProps = useMemo(() => {
|
||||
return {
|
||||
pluginPayload,
|
||||
buttonText: t('plugin.auth.addApi'),
|
||||
}
|
||||
}, [pluginPayload, t])
|
||||
|
||||
const Configure = () => {
|
||||
return (
|
||||
<>
|
||||
<PortalToFollowElem
|
||||
|
|
@ -25,34 +67,46 @@ const Configure = () => {
|
|||
variant='secondary-accent'
|
||||
>
|
||||
<RiAddLine className='h-4 w-4' />
|
||||
Configure
|
||||
{t('common.dataSource.configure')}
|
||||
</Button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[61]'>
|
||||
<div className='w-[240px] space-y-1.5 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-lg'>
|
||||
<Button
|
||||
variant='primary'
|
||||
className='w-full px-0'
|
||||
>
|
||||
<div className='grow'>
|
||||
use oauth
|
||||
</div>
|
||||
<div className='h-4 w-[1px] bg-text-primary-on-surface opacity-[0.15]'></div>
|
||||
<div className='flex h-8 w-8 shrink-0 items-center justify-center'>
|
||||
<RiEqualizer2Line className='h-4 w-4' />
|
||||
</div>
|
||||
</Button>
|
||||
<div className='system-2xs-medium-uppercase flex h-4 items-center p-2 text-text-quaternary'>
|
||||
<div className='mr-2 h-[1px] grow bg-gradient-to-l from-[rgba(16,24,40,0.08)]' />
|
||||
OR
|
||||
<div className='ml-2 h-[1px] grow bg-gradient-to-r from-[rgba(16,24,40,0.08)]' />
|
||||
</div>
|
||||
<Button
|
||||
className='w-full'
|
||||
variant='secondary-accent'
|
||||
>
|
||||
Use API Key
|
||||
</Button>
|
||||
{
|
||||
canOAuth && (
|
||||
<AddOAuthButton
|
||||
{...oAuthButtonProps}
|
||||
onUpdate={onUpdate}
|
||||
oAuthData={{
|
||||
schema: oAuthData.client_schema || [],
|
||||
is_oauth_custom_client_enabled: oAuthData.is_oauth_custom_client_enabled,
|
||||
is_system_oauth_params_exists: oAuthData.is_system_oauth_params_exists,
|
||||
client_params: oAuthData.oauth_custom_client_params,
|
||||
redirect_uri: oAuthData.redirect_uri,
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
canApiKey && canOAuth && (
|
||||
<div className='system-2xs-medium-uppercase flex h-4 items-center p-2 text-text-quaternary'>
|
||||
<div className='mr-2 h-[1px] grow bg-gradient-to-l from-[rgba(16,24,40,0.08)]' />
|
||||
OR
|
||||
<div className='ml-2 h-[1px] grow bg-gradient-to-r from-[rgba(16,24,40,0.08)]' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
canApiKey && (
|
||||
<AddApiKeyButton
|
||||
{...apiKeyButtonProps}
|
||||
formSchemas={item.credential_schema}
|
||||
onUpdate={onUpdate}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
export * from './use-marketplace-all-plugins'
|
||||
export * from './use-data-source-auth-update'
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { useCallback } from 'react'
|
||||
import { useInvalidDataSourceListAuth } from '@/service/use-datasource'
|
||||
import { useInvalidDataSourceList } from '@/service/use-pipeline'
|
||||
|
||||
export const useDataSourceAuthUpdate = () => {
|
||||
const invalidateDataSourceListAuth = useInvalidDataSourceListAuth()
|
||||
const invalidateDataSourceList = useInvalidDataSourceList()
|
||||
const handleAuthUpdate = useCallback(() => {
|
||||
invalidateDataSourceListAuth()
|
||||
invalidateDataSourceList()
|
||||
}, [invalidateDataSourceListAuth, invalidateDataSourceList])
|
||||
|
||||
return {
|
||||
handleAuthUpdate,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,79 @@
|
|||
import { memo } from 'react'
|
||||
import {
|
||||
memo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import Operator from './operator'
|
||||
import type {
|
||||
DataSourceCredential,
|
||||
} from './types'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
type ItemProps = {
|
||||
credentialItem: DataSourceCredential
|
||||
onAction: (action: string, credentialItem: DataSourceCredential, renamePayload?: Record<string, any>) => void
|
||||
}
|
||||
const Item = ({
|
||||
credentialItem,
|
||||
onAction,
|
||||
}: ItemProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [renaming, setRenaming] = useState(false)
|
||||
const [renameValue, setRenameValue] = useState(credentialItem.name)
|
||||
|
||||
const Item = () => {
|
||||
return (
|
||||
<div className='flex h-10 items-center rounded-lg bg-components-panel-on-panel-item-bg pl-3 pr-1'>
|
||||
<div className='mr-2 h-5 w-5 shrink-0'></div>
|
||||
<div className='system-sm-medium grow text-text-secondary'>
|
||||
Evan’s Notion
|
||||
</div>
|
||||
{/* <div className='mr-2 h-5 w-5 shrink-0'></div> */}
|
||||
{
|
||||
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')}
|
||||
onClick={e => e.stopPropagation()}
|
||||
/>
|
||||
<Button
|
||||
size='small'
|
||||
variant='primary'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onAction?.(
|
||||
'rename',
|
||||
credentialItem,
|
||||
{
|
||||
credential_id: credentialItem.id,
|
||||
name: renameValue,
|
||||
},
|
||||
)
|
||||
setRenaming(false)
|
||||
}}
|
||||
>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
<Button
|
||||
size='small'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setRenaming(false)
|
||||
}}
|
||||
>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!renaming && (
|
||||
<div className='system-sm-medium grow text-text-secondary'>
|
||||
{credentialItem.name}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='flex shrink-0 items-center'>
|
||||
<div className='mr-1 flex h-3 w-3 items-center justify-center'>
|
||||
<Indicator color='green' />
|
||||
|
|
@ -18,7 +83,10 @@ const Item = () => {
|
|||
</div>
|
||||
</div>
|
||||
<div className='ml-3 mr-2 h-3 w-[1px] bg-divider-regular'></div>
|
||||
<Operator />
|
||||
<Operator
|
||||
credentialItem={credentialItem}
|
||||
onAction={onAction}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,40 +3,94 @@ import {
|
|||
useCallback,
|
||||
useMemo,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiEditLine,
|
||||
RiEqualizer2Line,
|
||||
RiHome9Line,
|
||||
RiLoopLeftLine,
|
||||
RiStickyNoteAddLine,
|
||||
} from '@remixicon/react'
|
||||
import Dropdown from '@/app/components/base/dropdown'
|
||||
import type { Item } from '@/app/components/base/dropdown'
|
||||
import type {
|
||||
DataSourceCredential,
|
||||
} from './types'
|
||||
import { CredentialTypeEnum } from '@/app/components/plugins/plugin-auth/types'
|
||||
|
||||
const Operator = () => {
|
||||
type OperatorProps = {
|
||||
credentialItem: DataSourceCredential
|
||||
onAction: (action: string, credentialItem: DataSourceCredential) => void
|
||||
onRename?: () => void
|
||||
}
|
||||
const Operator = ({
|
||||
credentialItem,
|
||||
onAction,
|
||||
onRename,
|
||||
}: OperatorProps) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
type,
|
||||
} = credentialItem
|
||||
const items = useMemo(() => {
|
||||
return [
|
||||
const commonItems = [
|
||||
{
|
||||
value: 'change',
|
||||
value: 'setDefault',
|
||||
text: (
|
||||
<div className='flex'>
|
||||
<RiStickyNoteAddLine className='mr-2 h-4 w-4 text-text-tertiary' />
|
||||
<div>
|
||||
<div className='system-sm-semibold mb-1 text-text-secondary'>Change authorized pages</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>18 Pages authorized</div>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<RiHome9Line className='mr-2 h-4 w-4 text-text-tertiary' />
|
||||
<div className='system-sm-semibold text-text-secondary'>{t('plugin.auth.setDefault')}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'sync',
|
||||
value: 'rename',
|
||||
text: (
|
||||
<div className='flex items-center'>
|
||||
<RiLoopLeftLine className='mr-2 h-4 w-4 text-text-tertiary' />
|
||||
<div className='system-sm-semibold text-text-secondary'>Sync</div>
|
||||
<RiEditLine className='mr-2 h-4 w-4 text-text-tertiary' />
|
||||
<div className='system-sm-semibold text-text-secondary'>{t('common.operation.rename')}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'edit',
|
||||
text: (
|
||||
<div className='flex items-center'>
|
||||
<RiEqualizer2Line className='mr-2 h-4 w-4 text-text-tertiary' />
|
||||
<div className='system-sm-semibold text-text-secondary'>{t('common.operation.edit')}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
if (type === CredentialTypeEnum.OAUTH2) {
|
||||
const oAuthItems = [
|
||||
{
|
||||
value: 'change',
|
||||
text: (
|
||||
<div className='flex'>
|
||||
<RiStickyNoteAddLine className='mr-2 h-4 w-4 text-text-tertiary' />
|
||||
<div>
|
||||
<div className='system-sm-semibold mb-1 text-text-secondary'>{t('common.dataSource.notion.changeAuthorizedPages')}</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>18 {t('common.dataSource.notion.pagesAuthorized')}</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'sync',
|
||||
text: (
|
||||
<div className='flex items-center'>
|
||||
<RiLoopLeftLine className='mr-2 h-4 w-4 text-text-tertiary' />
|
||||
<div className='system-sm-semibold text-text-secondary'>{t('common.dataSource.notion.sync')}</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]
|
||||
commonItems.push(...oAuthItems)
|
||||
}
|
||||
return commonItems
|
||||
}, [t, type])
|
||||
|
||||
const secondItems = useMemo(() => {
|
||||
return [
|
||||
|
|
@ -51,23 +105,29 @@ const Operator = () => {
|
|||
},
|
||||
]
|
||||
}, [])
|
||||
|
||||
const handleSelect = useCallback((item: Item) => {
|
||||
console.log('Selected item:', item)
|
||||
}, [])
|
||||
if (item.value === 'rename') {
|
||||
onRename?.()
|
||||
return
|
||||
}
|
||||
onAction(
|
||||
item.value as string,
|
||||
credentialItem,
|
||||
)
|
||||
}, [onAction, credentialItem, onRename])
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
items={items}
|
||||
secondItems={secondItems}
|
||||
onSelect={handleSelect}
|
||||
popupClassName='z-[61]'
|
||||
triggerProps={{
|
||||
size: 'l',
|
||||
}}
|
||||
itemClassName='py-2 h-auto hover:bg-state-base-hover'
|
||||
secondItemClassName='py-2 h-auto hover:bg-state-base-hover'
|
||||
/>
|
||||
<Dropdown
|
||||
items={items}
|
||||
secondItems={secondItems}
|
||||
onSelect={handleSelect}
|
||||
popupClassName='z-[61]'
|
||||
triggerProps={{
|
||||
size: 'l',
|
||||
}}
|
||||
itemClassName='py-2 h-auto hover:bg-state-base-hover'
|
||||
secondItemClassName='py-2 h-auto hover:bg-state-base-hover'
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
import type {
|
||||
FormSchema,
|
||||
TypeWithI18N,
|
||||
} from '@/app/components/base/form/types'
|
||||
import type { CredentialTypeEnum } from '@/app/components/plugins/plugin-auth/types'
|
||||
|
||||
export type DataSourceCredential = {
|
||||
credential: Record<string, any>
|
||||
type: string
|
||||
type: CredentialTypeEnum
|
||||
name: string
|
||||
id: string
|
||||
}
|
||||
|
|
@ -9,14 +15,18 @@ export type DataSourceAuth = {
|
|||
provider: string
|
||||
plugin_id: string
|
||||
plugin_unique_identifier: string
|
||||
icon: any
|
||||
icon: string
|
||||
name: string
|
||||
label: any
|
||||
description: any
|
||||
credential_schema?: any[]
|
||||
label: TypeWithI18N
|
||||
description: TypeWithI18N
|
||||
credential_schema?: FormSchema[]
|
||||
oauth_schema?: {
|
||||
client_schema?: any[]
|
||||
credentials_schema?: any[]
|
||||
client_schema?: FormSchema[]
|
||||
credentials_schema?: FormSchema[]
|
||||
is_oauth_custom_client_enabled?: boolean
|
||||
is_system_oauth_params_exists?: boolean
|
||||
oauth_custom_client_params?: Record<string, any>
|
||||
redirect_uri?: string
|
||||
}
|
||||
credentials_list: DataSourceCredential[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import Button from '@/app/components/base/button'
|
|||
import type { ButtonProps } from '@/app/components/base/button'
|
||||
import ApiKeyModal from './api-key-modal'
|
||||
import type { PluginPayload } from '../types'
|
||||
import type { FormSchema } from '@/app/components/base/form/types'
|
||||
|
||||
export type AddApiKeyButtonProps = {
|
||||
pluginPayload: PluginPayload
|
||||
|
|
@ -13,13 +14,15 @@ export type AddApiKeyButtonProps = {
|
|||
buttonText?: string
|
||||
disabled?: boolean
|
||||
onUpdate?: () => void
|
||||
formSchemas?: FormSchema[]
|
||||
}
|
||||
const AddApiKeyButton = ({
|
||||
pluginPayload,
|
||||
buttonVariant = 'secondary-accent',
|
||||
buttonText = 'use api key',
|
||||
buttonText = 'Use Api Key',
|
||||
disabled,
|
||||
onUpdate,
|
||||
formSchemas = [],
|
||||
}: AddApiKeyButtonProps) => {
|
||||
const [isApiKeyModalOpen, setIsApiKeyModalOpen] = useState(false)
|
||||
|
||||
|
|
@ -39,6 +42,7 @@ const AddApiKeyButton = ({
|
|||
pluginPayload={pluginPayload}
|
||||
onClose={() => setIsApiKeyModalOpen(false)}
|
||||
onUpdate={onUpdate}
|
||||
formSchemas={formSchemas}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,13 @@ export type AddOAuthButtonProps = {
|
|||
dividerClassName?: string
|
||||
disabled?: boolean
|
||||
onUpdate?: () => void
|
||||
oAuthData?: {
|
||||
schema?: FormSchema[]
|
||||
is_oauth_custom_client_enabled?: boolean
|
||||
is_system_oauth_params_exists?: boolean
|
||||
client_params?: Record<string, any>
|
||||
redirect_uri?: string
|
||||
}
|
||||
}
|
||||
const AddOAuthButton = ({
|
||||
pluginPayload,
|
||||
|
|
@ -47,19 +54,26 @@ const AddOAuthButton = ({
|
|||
dividerClassName,
|
||||
disabled,
|
||||
onUpdate,
|
||||
oAuthData,
|
||||
}: AddOAuthButtonProps) => {
|
||||
const { t } = useTranslation()
|
||||
const renderI18nObject = useRenderI18nObject()
|
||||
const [isOAuthSettingsOpen, setIsOAuthSettingsOpen] = useState(false)
|
||||
const { mutateAsync: getPluginOAuthUrl } = useGetPluginOAuthUrlHook(pluginPayload)
|
||||
const { data, isLoading } = useGetPluginOAuthClientSchemaHook(pluginPayload)
|
||||
const mergedOAuthData = useMemo(() => {
|
||||
if (oAuthData)
|
||||
return oAuthData
|
||||
|
||||
return data
|
||||
}, [oAuthData, data])
|
||||
const {
|
||||
schema = [],
|
||||
is_oauth_custom_client_enabled,
|
||||
is_system_oauth_params_exists,
|
||||
client_params,
|
||||
redirect_uri,
|
||||
} = data || {}
|
||||
} = mergedOAuthData as any
|
||||
const isConfigured = is_system_oauth_params_exists || is_oauth_custom_client_enabled
|
||||
const handleOAuth = useCallback(async () => {
|
||||
const { authorization_url } = await getPluginOAuthUrl()
|
||||
|
|
@ -112,7 +126,7 @@ const AddOAuthButton = ({
|
|||
)
|
||||
}, [t, redirect_uri, renderI18nObject])
|
||||
const memorizedSchemas = useMemo(() => {
|
||||
const result: FormSchema[] = schema.map((item, index) => {
|
||||
const result: FormSchema[] = (schema as FormSchema[]).map((item, index) => {
|
||||
return {
|
||||
...item,
|
||||
label: index === 0 ? renderCustomLabel(item) : item.label,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
|
|||
import Modal from '@/app/components/base/modal/modal'
|
||||
import { CredentialTypeEnum } from '../types'
|
||||
import AuthForm from '@/app/components/base/form/form-scenarios/auth'
|
||||
import type { FormRefObject } from '@/app/components/base/form/types'
|
||||
import type {
|
||||
FormRefObject,
|
||||
FormSchema,
|
||||
} 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'
|
||||
|
|
@ -30,6 +33,7 @@ export type ApiKeyModalProps = {
|
|||
onRemove?: () => void
|
||||
disabled?: boolean
|
||||
onUpdate?: () => void
|
||||
formSchemas?: FormSchema[]
|
||||
}
|
||||
const ApiKeyModal = ({
|
||||
pluginPayload,
|
||||
|
|
@ -38,6 +42,7 @@ const ApiKeyModal = ({
|
|||
onRemove,
|
||||
disabled,
|
||||
onUpdate,
|
||||
formSchemas: formSchemasFromProps = [],
|
||||
}: ApiKeyModalProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
|
|
@ -48,6 +53,12 @@ const ApiKeyModal = ({
|
|||
setDoingAction(value)
|
||||
}, [])
|
||||
const { data = [], isLoading } = useGetPluginCredentialSchemaHook(pluginPayload, CredentialTypeEnum.API_KEY)
|
||||
const mergedData = useMemo(() => {
|
||||
if (formSchemasFromProps?.length)
|
||||
return formSchemasFromProps
|
||||
|
||||
return data
|
||||
}, [formSchemasFromProps, data])
|
||||
const formSchemas = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
|
|
@ -56,9 +67,9 @@ const ApiKeyModal = ({
|
|||
label: t('plugin.auth.authorizationName'),
|
||||
required: false,
|
||||
},
|
||||
...data,
|
||||
...mergedData,
|
||||
]
|
||||
}, [data, t])
|
||||
}, [mergedData, t])
|
||||
const defaultValues = formSchemas.reduce((acc, schema) => {
|
||||
if (schema.default)
|
||||
acc[schema.name] = schema.default
|
||||
|
|
@ -165,7 +176,7 @@ const ApiKeyModal = ({
|
|||
)
|
||||
}
|
||||
{
|
||||
!isLoading && !!data.length && (
|
||||
!isLoading && !!mergedData.length && (
|
||||
<AuthForm
|
||||
ref={formRef}
|
||||
formSchemas={formSchemas}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import {
|
||||
memo,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type AuthorizedInDataSourceNodeProps = {
|
||||
authorizationsNum: number
|
||||
onJumpToDataSourcePage: () => void
|
||||
}
|
||||
const AuthorizedInDataSourceNode = ({
|
||||
authorizationsNum,
|
||||
onJumpToDataSourcePage,
|
||||
}: AuthorizedInDataSourceNodeProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Button
|
||||
size='small'
|
||||
onClick={onJumpToDataSourcePage}
|
||||
>
|
||||
<Indicator
|
||||
className='mr-1.5'
|
||||
color='green'
|
||||
/>
|
||||
{
|
||||
authorizationsNum > 1
|
||||
? t('plugin.auth.authorizations')
|
||||
: t('plugin.auth.authorization')
|
||||
}
|
||||
<RiEqualizer2Line
|
||||
className={cn(
|
||||
'h-3.5 w-3.5 text-components-button-ghost-text',
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(AuthorizedInDataSourceNode)
|
||||
|
|
@ -24,6 +24,22 @@ export const useGetApi = ({ category = AuthCategory.tool, provider }: PluginPayl
|
|||
}
|
||||
}
|
||||
|
||||
if (category === AuthCategory.datasource) {
|
||||
return {
|
||||
getCredentialInfo: '',
|
||||
setDefaultCredential: `/auth/plugin/datasource/${provider}/default`,
|
||||
getCredentials: `/auth/plugin/datasource/${provider}`,
|
||||
addCredential: `/auth/plugin/datasource/${provider}`,
|
||||
updateCredential: `/auth/plugin/datasource/${provider}/update`,
|
||||
deleteCredential: `/auth/plugin/datasource/${provider}/delete`,
|
||||
getCredentialSchema: () => '',
|
||||
getOauthUrl: `/oauth/plugin/${provider}/datasource/get-authorization-url`,
|
||||
getOauthClientSchema: '',
|
||||
setCustomOauthClient: `/auth/plugin/datasource/${provider}/custom-client`,
|
||||
deleteCustomOAuthClient: `/auth/plugin/datasource/${provider}/custom-client`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getCredentialInfo: '',
|
||||
setDefaultCredential: '',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
import {
|
||||
useCallback,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import type { PluginPayload } from '../types'
|
||||
import {
|
||||
useDeletePluginCredentialHook,
|
||||
useSetPluginDefaultCredentialHook,
|
||||
useUpdatePluginCredentialHook,
|
||||
} from '../hooks/use-credential'
|
||||
|
||||
export const usePluginAuthAction = (
|
||||
pluginPayload: PluginPayload,
|
||||
onUpdate?: () => void,
|
||||
) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const pendingOperationCredentialId = useRef<string | null>(null)
|
||||
const [deleteCredentialId, setDeleteCredentialId] = useState<string | null>(null)
|
||||
const { mutateAsync: deletePluginCredential } = useDeletePluginCredentialHook(pluginPayload)
|
||||
const openConfirm = useCallback((credentialId?: string) => {
|
||||
if (credentialId)
|
||||
pendingOperationCredentialId.current = credentialId
|
||||
|
||||
setDeleteCredentialId(pendingOperationCredentialId.current)
|
||||
}, [])
|
||||
const closeConfirm = useCallback(() => {
|
||||
setDeleteCredentialId(null)
|
||||
pendingOperationCredentialId.current = null
|
||||
}, [])
|
||||
const [doingAction, setDoingAction] = useState(false)
|
||||
const doingActionRef = useRef(doingAction)
|
||||
const handleSetDoingAction = useCallback((doing: boolean) => {
|
||||
doingActionRef.current = doing
|
||||
setDoingAction(doing)
|
||||
}, [])
|
||||
const handleConfirm = useCallback(async () => {
|
||||
if (doingActionRef.current)
|
||||
return
|
||||
if (!pendingOperationCredentialId.current) {
|
||||
setDeleteCredentialId(null)
|
||||
return
|
||||
}
|
||||
try {
|
||||
handleSetDoingAction(true)
|
||||
await deletePluginCredential({ credential_id: pendingOperationCredentialId.current })
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('common.api.actionSuccess'),
|
||||
})
|
||||
onUpdate?.()
|
||||
setDeleteCredentialId(null)
|
||||
pendingOperationCredentialId.current = null
|
||||
}
|
||||
finally {
|
||||
handleSetDoingAction(false)
|
||||
}
|
||||
}, [deletePluginCredential, onUpdate, notify, t, handleSetDoingAction])
|
||||
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: setPluginDefaultCredential } = useSetPluginDefaultCredentialHook(pluginPayload)
|
||||
const handleSetDefault = useCallback(async (id: string) => {
|
||||
if (doingActionRef.current)
|
||||
return
|
||||
try {
|
||||
handleSetDoingAction(true)
|
||||
await setPluginDefaultCredential(id)
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('common.api.actionSuccess'),
|
||||
})
|
||||
onUpdate?.()
|
||||
}
|
||||
finally {
|
||||
handleSetDoingAction(false)
|
||||
}
|
||||
}, [setPluginDefaultCredential, onUpdate, notify, t, handleSetDoingAction])
|
||||
const { mutateAsync: updatePluginCredential } = useUpdatePluginCredentialHook(pluginPayload)
|
||||
const handleRename = useCallback(async (payload: {
|
||||
credential_id: string
|
||||
name: string
|
||||
}) => {
|
||||
if (doingActionRef.current)
|
||||
return
|
||||
try {
|
||||
handleSetDoingAction(true)
|
||||
await updatePluginCredential(payload)
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('common.api.actionSuccess'),
|
||||
})
|
||||
onUpdate?.()
|
||||
}
|
||||
finally {
|
||||
handleSetDoingAction(false)
|
||||
}
|
||||
}, [updatePluginCredential, notify, t, handleSetDoingAction, onUpdate])
|
||||
|
||||
return {
|
||||
doingAction,
|
||||
handleSetDoingAction,
|
||||
openConfirm,
|
||||
closeConfirm,
|
||||
deleteCredentialId,
|
||||
setDeleteCredentialId,
|
||||
handleConfirm,
|
||||
editValues,
|
||||
setEditValues,
|
||||
handleEdit,
|
||||
handleRemove,
|
||||
handleSetDefault,
|
||||
handleRename,
|
||||
pendingOperationCredentialId,
|
||||
}
|
||||
}
|
||||
|
|
@ -3,4 +3,10 @@ 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 { default as PluginAuthInDataSourceNode } from './plugin-auth-in-datasource-node'
|
||||
export { default as AuthorizedInDataSourceNode } from './authorized-in-data-source-node'
|
||||
export { default as AddOAuthButton } from './authorize/add-oauth-button'
|
||||
export { default as AddApiKeyButton } from './authorize/add-api-key-button'
|
||||
export { default as ApiKeyModal } from './authorize/api-key-modal'
|
||||
export * from './hooks/use-plugin-auth-action'
|
||||
export * from './types'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
import { memo } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiAddLine } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
type PluginAuthInDataSourceNodeProps = {
|
||||
children?: ReactNode
|
||||
isAuthorized?: boolean
|
||||
onJumpToDataSourcePage: () => void
|
||||
}
|
||||
const PluginAuthInDataSourceNode = ({
|
||||
children,
|
||||
isAuthorized,
|
||||
onJumpToDataSourcePage,
|
||||
}: PluginAuthInDataSourceNodeProps) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
{
|
||||
!isAuthorized && (
|
||||
<div className='px-4 pb-2'>
|
||||
<Button
|
||||
className='w-full'
|
||||
variant='primary'
|
||||
onClick={onJumpToDataSourcePage}
|
||||
>
|
||||
<RiAddLine className='mr-1 h-4 w-4' />
|
||||
{t('common.integrations.connect')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{isAuthorized && children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(PluginAuthInDataSourceNode)
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
export type { AddApiKeyButtonProps } from './authorize/add-api-key-button'
|
||||
export type { AddOAuthButtonProps } from './authorize/add-oauth-button'
|
||||
|
||||
export enum AuthCategory {
|
||||
tool = 'tool',
|
||||
datasource = 'datasource',
|
||||
|
|
|
|||
|
|
@ -62,11 +62,15 @@ import { Stop } from '@/app/components/base/icons/src/vender/line/mediaAndDevice
|
|||
import { useHooksStore } from '@/app/components/workflow/hooks-store'
|
||||
import { FlowType } from '@/types/common'
|
||||
import {
|
||||
AuthorizedInDataSourceNode,
|
||||
AuthorizedInNode,
|
||||
PluginAuth,
|
||||
PluginAuthInDataSourceNode,
|
||||
} from '@/app/components/plugins/plugin-auth'
|
||||
import { AuthCategory } from '@/app/components/plugins/plugin-auth'
|
||||
import { canFindTool } from '@/utils'
|
||||
import { DataSourceClassification } from '@/app/components/workflow/nodes/data-source/types'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
|
||||
type BasePanelProps = {
|
||||
children: ReactNode
|
||||
|
|
@ -240,6 +244,11 @@ const BasePanel: FC<BasePanelProps> = ({
|
|||
const showPluginAuth = useMemo(() => {
|
||||
return data.type === BlockEnum.Tool && currCollection?.allow_delete
|
||||
}, [currCollection, data.type])
|
||||
const dataSourceList = useStore(s => s.dataSourceList)
|
||||
const currentDataSource = useMemo(() => {
|
||||
if (data.type === BlockEnum.DataSource && data.provider_type !== DataSourceClassification.localFile)
|
||||
return dataSourceList?.find(item => item.plugin_id === data.plugin_id)
|
||||
}, [dataSourceList, data.plugin_id, data.type, data.provider_type])
|
||||
const handleAuthorizationItemClick = useCallback((credential_id: string) => {
|
||||
handleNodeDataUpdateWithSyncDraft({
|
||||
id,
|
||||
|
|
@ -248,6 +257,10 @@ const BasePanel: FC<BasePanelProps> = ({
|
|||
},
|
||||
})
|
||||
}, [handleNodeDataUpdateWithSyncDraft, id])
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const handleJumpToDataSourcePage = useCallback(() => {
|
||||
setShowAccountSettingModal({ payload: 'data-source' })
|
||||
}, [setShowAccountSettingModal])
|
||||
|
||||
if (logParams.showSpecialResultPanel) {
|
||||
return (
|
||||
|
|
@ -413,7 +426,26 @@ const BasePanel: FC<BasePanelProps> = ({
|
|||
)
|
||||
}
|
||||
{
|
||||
!showPluginAuth && (
|
||||
!!currentDataSource && (
|
||||
<PluginAuthInDataSourceNode
|
||||
onJumpToDataSourcePage={handleJumpToDataSourcePage}
|
||||
isAuthorized={currentDataSource.is_authorized}
|
||||
>
|
||||
<div className='flex items-center justify-between pl-4 pr-3'>
|
||||
<Tab
|
||||
value={tabType}
|
||||
onChange={setTabType}
|
||||
/>
|
||||
<AuthorizedInDataSourceNode
|
||||
onJumpToDataSourcePage={handleJumpToDataSourcePage}
|
||||
authorizationsNum={3}
|
||||
/>
|
||||
</div>
|
||||
</PluginAuthInDataSourceNode>
|
||||
)
|
||||
}
|
||||
{
|
||||
!showPluginAuth && !currentDataSource && (
|
||||
<div className='flex items-center justify-between pl-4 pr-3'>
|
||||
<Tab
|
||||
value={tabType}
|
||||
|
|
|
|||
|
|
@ -1,137 +0,0 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||
import cn from '@/utils/classnames'
|
||||
import Drawer from '@/app/components/base/drawer-plus'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
|
||||
import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useDataSourceCredentials } from '@/service/use-pipeline'
|
||||
import type { ToolCredential } from '@/app/components/tools/types'
|
||||
|
||||
type Props = {
|
||||
dataSourceItem: any
|
||||
onCancel: () => void
|
||||
onSaved: (value: Record<string, any>) => void
|
||||
isHideRemoveBtn?: boolean
|
||||
onRemove?: () => void
|
||||
isSaving?: boolean
|
||||
}
|
||||
|
||||
const ConfigCredential: FC<Props> = ({
|
||||
dataSourceItem,
|
||||
onCancel,
|
||||
onSaved,
|
||||
isHideRemoveBtn,
|
||||
onRemove = noop,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const language = useLanguage()
|
||||
const {
|
||||
provider,
|
||||
plugin_id,
|
||||
credentialsSchema = [],
|
||||
is_authorized,
|
||||
} = dataSourceItem
|
||||
const transformedCredentialsSchema = useMemo(() => {
|
||||
return toolCredentialToFormSchemas(credentialsSchema)
|
||||
}, [credentialsSchema])
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [tempCredential, setTempCredential] = useState<any>({})
|
||||
const handleUpdateCredentials = useCallback((credentialValue: ToolCredential[]) => {
|
||||
const defaultCredentials = addDefaultValue(credentialValue, transformedCredentialsSchema)
|
||||
setTempCredential(defaultCredentials)
|
||||
}, [transformedCredentialsSchema])
|
||||
useDataSourceCredentials(provider, plugin_id, handleUpdateCredentials)
|
||||
|
||||
const handleSave = async () => {
|
||||
for (const field of transformedCredentialsSchema) {
|
||||
if (field.required && !tempCredential[field.name]) {
|
||||
Toast.notify({ type: 'error', message: t('common.errorMsg.fieldRequired', { field: field.label[language] || field.label.en_US }) })
|
||||
return
|
||||
}
|
||||
}
|
||||
setIsLoading(true)
|
||||
try {
|
||||
await onSaved(tempCredential)
|
||||
setIsLoading(false)
|
||||
}
|
||||
finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
isShow
|
||||
onHide={onCancel}
|
||||
title={t('tools.auth.setupModalTitle') as string}
|
||||
titleDescription={t('tools.auth.setupModalTitleDescription') as string}
|
||||
panelClassName='mt-[64px] mb-2 !w-[420px] border-components-panel-border'
|
||||
maxWidthClassName='!max-w-[420px]'
|
||||
height='calc(100vh - 64px)'
|
||||
contentClassName='!bg-components-panel-bg'
|
||||
headerClassName='!border-b-divider-subtle'
|
||||
body={
|
||||
|
||||
<div className='h-full px-6 py-3'>
|
||||
{!transformedCredentialsSchema.length
|
||||
? <Loading type='app' />
|
||||
: (
|
||||
<>
|
||||
<Form
|
||||
value={tempCredential}
|
||||
onChange={(v) => {
|
||||
setTempCredential(v)
|
||||
}}
|
||||
formSchemas={transformedCredentialsSchema as any}
|
||||
isEditMode={true}
|
||||
showOnVariableMap={{}}
|
||||
validating={false}
|
||||
inputClassName='!bg-components-input-bg-normal'
|
||||
fieldMoreInfo={item => item.url
|
||||
? (<a
|
||||
href={item.url}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
className='inline-flex items-center text-xs text-text-accent'
|
||||
>
|
||||
{t('tools.howToGet')}
|
||||
<LinkExternal02 className='ml-1 h-3 w-3' />
|
||||
</a>)
|
||||
: null}
|
||||
/>
|
||||
<div className={cn((is_authorized && !isHideRemoveBtn) ? 'justify-between' : 'justify-end', 'mt-2 flex ')} >
|
||||
{
|
||||
(is_authorized && !isHideRemoveBtn) && (
|
||||
<Button onClick={onRemove}>{t('common.operation.remove')}</Button>
|
||||
)
|
||||
}
|
||||
< div className='flex space-x-2'>
|
||||
<Button onClick={onCancel}>{t('common.operation.cancel')}</Button>
|
||||
<Button loading={isLoading || isSaving} disabled={isLoading || isSaving} variant='primary' onClick={handleSave}>{t('common.operation.save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
</div >
|
||||
}
|
||||
isShowMask={true}
|
||||
clickOutsideNotOpen={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
export default memo(ConfigCredential)
|
||||
|
|
@ -6,13 +6,11 @@ import {
|
|||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { memo } from 'react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import type { DataSourceNodeType } from './types'
|
||||
import { DataSourceClassification } from './types'
|
||||
import type { NodePanelProps } from '@/app/components/workflow/types'
|
||||
import {
|
||||
BoxGroupField,
|
||||
Group,
|
||||
} from '@/app/components/workflow/nodes/_base/components/layout'
|
||||
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
|
||||
import TagInput from '@/app/components/base/tag-input'
|
||||
|
|
@ -26,18 +24,13 @@ import {
|
|||
WEBSITE_CRAWL_OUTPUT,
|
||||
} from './constants'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import Button from '@/app/components/base/button'
|
||||
import ConfigCredential from './components/config-credential'
|
||||
import InputVarList from '@/app/components/workflow/nodes/tool/components/input-var-list'
|
||||
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||
import type { Var } from '@/app/components/workflow/types'
|
||||
import { VarType } from '@/app/components/workflow/types'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { useUpdateDataSourceCredentials } from '@/service/use-pipeline'
|
||||
|
||||
const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
const dataSourceList = useStore(s => s.dataSourceList)
|
||||
const {
|
||||
|
|
@ -55,11 +48,6 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => {
|
|||
const isOnlineDocument = provider_type === DataSourceClassification.onlineDocument
|
||||
const isOnlineDrive = provider_type === DataSourceClassification.onlineDrive
|
||||
const currentDataSource = dataSourceList?.find(ds => ds.plugin_id === plugin_id)
|
||||
const isAuthorized = !!currentDataSource?.is_authorized
|
||||
const [showAuthModal, {
|
||||
setTrue: openAuthModal,
|
||||
setFalse: hideAuthModal,
|
||||
}] = useBoolean(false)
|
||||
const currentDataSourceItem: any = currentDataSource?.tools.find(tool => tool.name === data.datasource_name)
|
||||
const formSchemas = useMemo(() => {
|
||||
return currentDataSourceItem ? toolParametersToFormSchemas(currentDataSourceItem.parameters) : []
|
||||
|
|
@ -77,40 +65,10 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => {
|
|||
return varPayload.type !== VarType.arrayFile
|
||||
}, [currVarType])
|
||||
|
||||
const { mutateAsync } = useUpdateDataSourceCredentials()
|
||||
const handleAuth = useCallback(async (value: any) => {
|
||||
await mutateAsync({
|
||||
provider: currentDataSource?.provider || '',
|
||||
pluginId: currentDataSource?.plugin_id || '',
|
||||
credentials: value,
|
||||
name: 'd14249c6-abe3-47ad-b0f1-1e65a591e790', // todo: fake name field, need to be removed later
|
||||
})
|
||||
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('common.api.actionSuccess'),
|
||||
})
|
||||
hideAuthModal()
|
||||
}, [currentDataSource, mutateAsync, notify, t, hideAuthModal])
|
||||
|
||||
return (
|
||||
<div >
|
||||
{
|
||||
!isAuthorized && !showAuthModal && !isLocalFile && currentDataSource && (
|
||||
<Group>
|
||||
<Button
|
||||
variant='primary'
|
||||
className='w-full'
|
||||
onClick={openAuthModal}
|
||||
disabled={nodesReadOnly}
|
||||
>
|
||||
{t('workflow.nodes.tool.authorize')}
|
||||
</Button>
|
||||
</Group>
|
||||
)
|
||||
}
|
||||
{
|
||||
isAuthorized && !isLocalFile && !!formSchemas?.length && (
|
||||
currentDataSource?.is_authorized && !isLocalFile && !!formSchemas?.length && (
|
||||
<BoxGroupField
|
||||
boxGroupProps={{
|
||||
boxProps: { withBorderBottom: true },
|
||||
|
|
@ -222,16 +180,6 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => {
|
|||
))
|
||||
}
|
||||
</OutputVars>
|
||||
{
|
||||
showAuthModal && !isLocalFile && (
|
||||
<ConfigCredential
|
||||
dataSourceItem={currentDataSource!}
|
||||
onCancel={hideAuthModal}
|
||||
onSaved={handleAuth}
|
||||
isHideRemoveBtn
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useQuery } from '@tanstack/react-query'
|
||||
import { get } from './base'
|
||||
import { useInvalid } from './use-base'
|
||||
import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types'
|
||||
|
||||
const NAME_SPACE = 'data-source-auth'
|
||||
|
|
@ -11,3 +12,8 @@ export const useGetDataSourceListAuth = () => {
|
|||
retry: 0,
|
||||
})
|
||||
}
|
||||
|
||||
export const useInvalidDataSourceListAuth = (
|
||||
) => {
|
||||
return useInvalid([NAME_SPACE, 'list'])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import type {
|
|||
import type { DataSourceItem } from '@/app/components/workflow/block-selector/types'
|
||||
import type { ToolCredential } from '@/app/components/tools/types'
|
||||
import type { IconInfo } from '@/models/datasets'
|
||||
import { useInvalid } from './use-base'
|
||||
|
||||
const NAME_SPACE = 'pipeline'
|
||||
|
||||
|
|
@ -180,6 +181,10 @@ export const useDataSourceList = (enabled: boolean, onSuccess?: (v: DataSourceIt
|
|||
})
|
||||
}
|
||||
|
||||
export const useInvalidDataSourceList = () => {
|
||||
return useInvalid([NAME_SPACE, 'datasource'])
|
||||
}
|
||||
|
||||
export const publishedPipelineInfoQueryKeyPrefix = [NAME_SPACE, 'published-pipeline']
|
||||
|
||||
export const usePublishedPipelineInfo = (pipelineId: string) => {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ export const useGetPluginCredentialSchema = (
|
|||
url: string,
|
||||
) => {
|
||||
return useQuery({
|
||||
enabled: !!url,
|
||||
queryKey: [NAME_SPACE, 'credential-schema', url],
|
||||
queryFn: () => get<FormSchema[]>(url),
|
||||
})
|
||||
|
|
@ -119,6 +120,7 @@ export const useGetPluginOAuthClientSchema = (
|
|||
url: string,
|
||||
) => {
|
||||
return useQuery({
|
||||
enabled: !!url,
|
||||
queryKey: [NAME_SPACE, 'oauth-client-schema', url],
|
||||
queryFn: () => get<{
|
||||
schema: FormSchema[]
|
||||
|
|
|
|||
Loading…
Reference in New Issue