Merge remote-tracking branch 'origin/main' into feat/trigger

This commit is contained in:
yessenia
2025-09-25 17:14:24 +08:00
3013 changed files with 148826 additions and 44294 deletions

View File

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

View File

@ -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()
@ -86,7 +100,7 @@ const AddOAuthButton = ({
{
redirect_uri && (
<div className='system-sm-medium flex w-full py-0.5'>
<div className='w-0 grow break-words'>{redirect_uri}</div>
<div className='w-0 grow break-words break-all'>{redirect_uri}</div>
<ActionButton
className='shrink-0'
onClick={() => {
@ -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,

View File

@ -10,7 +10,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'
@ -28,6 +31,7 @@ export type ApiKeyModalProps = {
onRemove?: () => void
disabled?: boolean
onUpdate?: () => void
formSchemas?: FormSchema[]
}
const ApiKeyModal = ({
pluginPayload,
@ -36,6 +40,7 @@ const ApiKeyModal = ({
onRemove,
disabled,
onUpdate,
formSchemas: formSchemasFromProps = [],
}: ApiKeyModalProps) => {
const { t } = useTranslation()
const { notify } = useToastContext()
@ -46,6 +51,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 [
{
@ -54,9 +65,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
@ -150,7 +161,7 @@ const ApiKeyModal = ({
)
}
{
!isLoading && !!data.length && (
!isLoading && !!mergedData.length && (
<AuthForm
ref={formRef}
formSchemas={formSchemas}

View File

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

View File

@ -36,14 +36,22 @@ const AuthorizedInNode = ({
disabled,
invalidPluginCredentialInfo,
notAllowCustomCredential,
} = usePluginAuth(pluginPayload, isOpen || !!credentialId)
} = usePluginAuth(pluginPayload, true)
const renderTrigger = useCallback((open?: boolean) => {
let label = ''
let removed = false
let unavailable = false
let color = 'green'
let defaultUnavailable = false
if (!credentialId) {
label = t('plugin.auth.workspaceDefault')
const defaultCredential = credentials.find(c => c.is_default)
if (defaultCredential?.not_allowed_to_use) {
color = 'gray'
defaultUnavailable = true
}
}
else {
const credential = credentials.find(c => c.id === credentialId)
@ -63,6 +71,7 @@ const AuthorizedInNode = ({
open && !removed && 'bg-components-button-ghost-bg-hover',
removed && 'bg-transparent text-text-destructive',
)}
variant={(defaultUnavailable || unavailable) ? 'ghost' : 'secondary'}
>
<Indicator
className='mr-1.5'
@ -70,7 +79,12 @@ const AuthorizedInNode = ({
/>
{label}
{
unavailable && t('plugin.auth.unavailable')
(unavailable || defaultUnavailable) && (
<>
&nbsp;
{t('plugin.auth.unavailable')}
</>
)
}
<RiArrowDownSLine
className={cn(
@ -81,6 +95,7 @@ const AuthorizedInNode = ({
</Button>
)
}, [credentialId, credentials, t])
const defaultUnavailable = credentials.find(c => c.is_default)?.not_allowed_to_use
const extraAuthorizationItems: Credential[] = [
{
id: '__workspace_default__',
@ -88,6 +103,7 @@ const AuthorizedInNode = ({
provider: '',
is_default: !credentialId,
isWorkspaceDefault: true,
not_allowed_to_use: defaultUnavailable,
},
]
const handleAuthorizationItemClick = useCallback((id: string) => {

View File

@ -174,6 +174,7 @@ const Authorized = ({
}
}, [updatePluginCredential, notify, t, handleSetDoingAction, onUpdate])
const unavailableCredentials = credentials.filter(credential => credential.not_allowed_to_use)
const unavailableCredential = credentials.find(credential => credential.not_allowed_to_use && credential.is_default)
return (
<>
@ -197,7 +198,7 @@ const Authorized = ({
'w-full',
isOpen && 'bg-components-button-secondary-bg-hover',
)}>
<Indicator className='mr-2' />
<Indicator className='mr-2' color={unavailableCredential ? 'gray' : 'green'} />
{credentials.length}&nbsp;
{
credentials.length > 1

View File

@ -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: '',

View File

@ -5,7 +5,7 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useToastContext } from '@/app/components/base/toast'
import type { PluginPayload } from '@/app/components/plugins/plugin-auth/types'
import type { PluginPayload } from '../types'
import {
useDeletePluginCredentialHook,
useSetPluginDefaultCredentialHook,

View File

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

View File

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

View File

@ -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',