void
+ onAddMenuOpenChange?: (open: boolean) => void
+}
+
+const CredentialConfigHeader = ({
+ pluginPayload,
+ canOAuth,
+ canApiKey,
+ hasOAuthClientConfigured,
+ disabled,
+ onCredentialAdded,
+ onAddMenuOpenChange,
+}: CredentialConfigHeaderProps) => {
+ const { t } = useTranslation()
+ const [showAddMenu, setShowAddMenu] = useState(false)
+
+ const handleAddMenuOpenChange = (open: boolean) => {
+ setShowAddMenu(open)
+ onAddMenuOpenChange?.(open)
+ }
+
+ const addButtonDisabled = disabled || (!canOAuth && !canApiKey && !hasOAuthClientConfigured)
+
+ return (
+
+
+
+
+
+ {t('plugin.auth.configuredCredentials.title')}
+
+
+ {t('plugin.auth.configuredCredentials.desc')}
+
+
+
+
+
+
+
+
+
+
+ {canOAuth && (
+
{
+ setShowAddMenu(false)
+ onCredentialAdded?.()
+ }}
+ />
+ )}
+ {canApiKey && (
+ {
+ setShowAddMenu(false)
+ onCredentialAdded?.()
+ }}
+ />
+ )}
+
+
+
+
+
+ )
+}
+
+export default memo(CredentialConfigHeader)
diff --git a/web/app/components/plugins/plugin-auth/end-user-credential-section.tsx b/web/app/components/plugins/plugin-auth/end-user-credential-section.tsx
new file mode 100644
index 0000000000..ccf6ae6d23
--- /dev/null
+++ b/web/app/components/plugins/plugin-auth/end-user-credential-section.tsx
@@ -0,0 +1,176 @@
+import {
+ memo,
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+} from 'react'
+import type { ReactNode } from 'react'
+import {
+ RiArrowDownSLine,
+ RiEqualizer2Line,
+ RiKey2Line,
+ RiUserStarLine,
+} from '@remixicon/react'
+import { useTranslation } from 'react-i18next'
+import AddOAuthButton from './authorize/add-oauth-button'
+import AddApiKeyButton from './authorize/add-api-key-button'
+import type { PluginPayload } from './types'
+import cn from '@/utils/classnames'
+import Switch from '@/app/components/base/switch'
+import {
+ PortalToFollowElem,
+ PortalToFollowElemContent,
+ PortalToFollowElemTrigger,
+} from '@/app/components/base/portal-to-follow-elem'
+
+export type EndUserCredentialSectionProps = {
+ pluginPayload: PluginPayload
+ canOAuth?: boolean
+ canApiKey?: boolean
+ disabled?: boolean
+ useEndUserCredentialEnabled?: boolean
+ endUserCredentialType?: string
+ onEndUserCredentialChange?: (enabled: boolean) => void
+ onEndUserCredentialTypeChange?: (type: string) => void
+ onCredentialAdded?: () => void
+ className?: string
+}
+
+const EndUserCredentialSection = ({
+ pluginPayload,
+ canOAuth,
+ canApiKey,
+ disabled,
+ useEndUserCredentialEnabled,
+ endUserCredentialType,
+ onEndUserCredentialChange,
+ onEndUserCredentialTypeChange,
+ onCredentialAdded,
+ className,
+}: EndUserCredentialSectionProps) => {
+ const { t } = useTranslation()
+ const [showEndUserTypeMenu, setShowEndUserTypeMenu] = useState(false)
+
+ const availableEndUserTypes = useMemo(() => {
+ const list: { value: string; label: string; icon: ReactNode }[] = []
+ if (canOAuth) {
+ list.push({
+ value: 'oauth2',
+ label: t('plugin.auth.endUserCredentials.optionOAuth'),
+ icon:
,
+ })
+ }
+ if (canApiKey) {
+ list.push({
+ value: 'api-key',
+ label: t('plugin.auth.endUserCredentials.optionApiKey'),
+ icon:
,
+ })
+ }
+ return list
+ }, [canOAuth, canApiKey, t])
+
+ const endUserCredentialLabel = useMemo(() => {
+ const found = availableEndUserTypes.find(item => item.value === endUserCredentialType)
+ return found?.label || availableEndUserTypes[0]?.label || '-'
+ }, [availableEndUserTypes, endUserCredentialType])
+
+ useEffect(() => {
+ if (!useEndUserCredentialEnabled)
+ return
+ if (!availableEndUserTypes.length)
+ return
+ const isValid = availableEndUserTypes.some(item => item.value === endUserCredentialType)
+ if (!isValid)
+ onEndUserCredentialTypeChange?.(availableEndUserTypes[0].value)
+ }, [useEndUserCredentialEnabled, endUserCredentialType, availableEndUserTypes, onEndUserCredentialTypeChange])
+
+ const handleSelectEndUserType = useCallback((value: string) => {
+ onEndUserCredentialTypeChange?.(value)
+ setShowEndUserTypeMenu(false)
+ }, [onEndUserCredentialTypeChange])
+
+ return (
+
+
+
+
+
+
+ {t('plugin.auth.endUserCredentials.title')}
+
+
+ {t('plugin.auth.endUserCredentials.desc')}
+
+
+
+
+ {
+ useEndUserCredentialEnabled && availableEndUserTypes.length > 0 && (
+
+
+ {t('plugin.auth.endUserCredentials.typeLabel')}
+
+
+
+
+
+
+
+
+ {canOAuth && (
+
{
+ handleSelectEndUserType('oauth2')
+ onCredentialAdded?.()
+ }}
+ />
+ )}
+ {canApiKey && (
+ {
+ handleSelectEndUserType('api-key')
+ onCredentialAdded?.()
+ }}
+ />
+ )}
+
+
+
+
+
+ )
+ }
+
+
+ )
+}
+
+export default memo(EndUserCredentialSection)
diff --git a/web/app/components/plugins/plugin-auth/index.tsx b/web/app/components/plugins/plugin-auth/index.tsx
index ee6f839590..c9b231907a 100644
--- a/web/app/components/plugins/plugin-auth/index.tsx
+++ b/web/app/components/plugins/plugin-auth/index.tsx
@@ -2,6 +2,10 @@ 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 { default as CredentialConfigHeader } from './credential-config-header'
+export type { CredentialConfigHeaderProps } from './credential-config-header'
+export { default as EndUserCredentialSection } from './end-user-credential-section'
+export type { EndUserCredentialSectionProps } from './end-user-credential-section'
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'
diff --git a/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx b/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
index d4b45edb26..4183cf6766 100644
--- a/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
+++ b/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
@@ -1,23 +1,16 @@
import {
memo,
useCallback,
- useEffect,
- useMemo,
useState,
} from 'react'
-import type { ReactNode } from 'react'
import {
- RiAddLine,
RiArrowDownSLine,
- RiEqualizer2Line,
- RiKey2Line,
- RiUserStarLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import Authorize from './authorize'
import Authorized from './authorized'
-import AddOAuthButton from './authorize/add-oauth-button'
-import AddApiKeyButton from './authorize/add-api-key-button'
+import CredentialConfigHeader from './credential-config-header'
+import EndUserCredentialSection from './end-user-credential-section'
import type {
Credential,
PluginPayload,
@@ -26,12 +19,6 @@ 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'
-import {
- PortalToFollowElem,
- PortalToFollowElemContent,
- PortalToFollowElemTrigger,
-} from '@/app/components/base/portal-to-follow-elem'
-import Switch from '@/app/components/base/switch'
type PluginAuthInAgentProps = {
pluginPayload: PluginPayload
@@ -53,8 +40,6 @@ const PluginAuthInAgent = ({
}: PluginAuthInAgentProps) => {
const { t } = useTranslation()
const [isOpen, setIsOpen] = useState(false)
- const [showAddMenu, setShowAddMenu] = useState(false)
- const [showEndUserTypeMenu, setShowEndUserTypeMenu] = useState(false)
const {
isAuthorized,
canOAuth,
@@ -66,6 +51,8 @@ const PluginAuthInAgent = ({
hasOAuthClientConfigured,
} = usePluginAuth(pluginPayload, true)
+ const configuredDisabled = !!useEndUserCredentialEnabled
+
const extraAuthorizationItems: Credential[] = [
{
id: '__workspace_default__',
@@ -122,251 +109,86 @@ const PluginAuthInAgent = ({
)
}, [credentialId, credentials, t])
- const availableEndUserTypes = useMemo(() => {
- const list: { value: string; label: string; icon: ReactNode }[] = []
- if (canOAuth) {
- list.push({
- value: 'oauth2',
- label: t('plugin.auth.endUserCredentials.optionOAuth'),
- icon:
,
- })
- }
- if (canApiKey) {
- list.push({
- value: 'api-key',
- label: t('plugin.auth.endUserCredentials.optionApiKey'),
- icon:
,
- })
- }
- return list
- }, [canOAuth, canApiKey, t])
-
- const endUserCredentialLabel = useMemo(() => {
- const found = availableEndUserTypes.find(item => item.value === endUserCredentialType)
- return found?.label || availableEndUserTypes[0]?.label || '-'
- }, [availableEndUserTypes, endUserCredentialType])
-
- useEffect(() => {
- if (!useEndUserCredentialEnabled)
- return
- if (!availableEndUserTypes.length)
- return
- const isValid = availableEndUserTypes.some(item => item.value === endUserCredentialType)
- if (!isValid)
- onEndUserCredentialTypeChange?.(availableEndUserTypes[0].value)
- }, [useEndUserCredentialEnabled, endUserCredentialType, availableEndUserTypes, onEndUserCredentialTypeChange])
-
- const handleSelectEndUserType = useCallback((value: string) => {
- onEndUserCredentialTypeChange?.(value)
- setShowEndUserTypeMenu(false)
- }, [onEndUserCredentialTypeChange])
-
const shouldShowAuthorizeCard = !credentials.length && (canOAuth || canApiKey || hasOAuthClientConfigured)
- const endUserSection = (
-
-
-
-
-
-
- {t('plugin.auth.endUserCredentials.title')}
-
-
- {t('plugin.auth.endUserCredentials.desc')}
-
-
-
-
- {
- useEndUserCredentialEnabled && availableEndUserTypes.length > 0 && (
-
-
- {t('plugin.auth.endUserCredentials.typeLabel')}
-
-
-
-
-
-
-
-
- {canOAuth && (
-
{
- handleSelectEndUserType('oauth2')
- invalidPluginCredentialInfo()
- }}
- />
- )}
- {canApiKey && (
- {
- handleSelectEndUserType('api-key')
- invalidPluginCredentialInfo()
- }}
- />
- )}
-
-
-
-
-
- )
- }
-
-
- )
-
return (
-
-
-
-
-
- {t('plugin.auth.configuredCredentials.title')}
-
-
- {t('plugin.auth.configuredCredentials.desc')}
-
-
-
-
-
-
-
-
-
-
- {
- canOAuth && (
-
{
- setShowAddMenu(false)
- invalidPluginCredentialInfo()
- }}
- />
- )
- }
- {
- canApiKey && (
- {
- setShowAddMenu(false)
- invalidPluginCredentialInfo()
- }}
- />
- )
- }
-
-
-
-
+
+
- {
- !isAuthorized && shouldShowAuthorizeCard && (
-
-
-
-
+
+ {
+ !isAuthorized && shouldShowAuthorizeCard && (
+
-
- )
- }
- {
- !isAuthorized && !shouldShowAuthorizeCard && (
-
- )
- }
- {
- isAuthorized && (
-
- )
- }
- {endUserSection}
+ )
+ }
+ {
+ !isAuthorized && !shouldShowAuthorizeCard && (
+
+ )
+ }
+ {
+ isAuthorized && (
+
+ )
+ }
+
+
)
}
diff --git a/web/app/components/plugins/plugin-auth/plugin-auth.tsx b/web/app/components/plugins/plugin-auth/plugin-auth.tsx
index 6ba0a6f0a2..ef27dda518 100644
--- a/web/app/components/plugins/plugin-auth/plugin-auth.tsx
+++ b/web/app/components/plugins/plugin-auth/plugin-auth.tsx
@@ -1,17 +1,13 @@
import {
memo,
- useCallback,
useEffect,
useMemo,
useState,
} from 'react'
-import type { ReactNode } from 'react'
import {
RiAddLine,
RiArrowDownSLine,
- RiEqualizer2Line,
RiKey2Line,
- RiUserStarLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import {
@@ -23,10 +19,10 @@ import Authorize from './authorize'
import Authorized from './authorized'
import AddApiKeyButton from './authorize/add-api-key-button'
import AddOAuthButton from './authorize/add-oauth-button'
+import EndUserCredentialSection from './end-user-credential-section'
import Item from './authorized/item'
import type { PluginPayload } from './types'
import { usePluginAuth } from './hooks/use-plugin-auth'
-import Switch from '@/app/components/base/switch'
import cn from '@/utils/classnames'
type PluginAuthProps = {
@@ -63,50 +59,12 @@ const PluginAuth = ({
const shouldShowGuide = !!showConnectGuide
const [showCredentialPanel, setShowCredentialPanel] = useState(false)
const [showAddMenu, setShowAddMenu] = useState(false)
- const [showEndUserTypeMenu, setShowEndUserTypeMenu] = useState(false)
const configuredDisabled = !!endUserCredentialEnabled
const shouldShowAuthorizeCard = useMemo(() => {
const hasCredential = credentials.length > 0
const canAdd = canOAuth || canApiKey || hasOAuthClientConfigured
return !hasCredential && canAdd
}, [credentials.length, canOAuth, canApiKey, hasOAuthClientConfigured])
- const availableEndUserTypes = useMemo(() => {
- const list: { value: string; label: string; icon: ReactNode }[] = []
- if (canOAuth) {
- list.push({
- value: 'oauth2',
- label: t('plugin.auth.endUserCredentials.optionOAuth'),
- icon:
,
- })
- }
- if (canApiKey) {
- list.push({
- value: 'api-key',
- label: t('plugin.auth.endUserCredentials.optionApiKey'),
- icon:
,
- })
- }
- return list
- }, [canOAuth, canApiKey, t])
- const endUserCredentialLabel = useMemo(() => {
- const found = availableEndUserTypes.find(item => item.value === endUserCredentialType)
- return found?.label || availableEndUserTypes[0]?.label || '-'
- }, [availableEndUserTypes, endUserCredentialType])
-
- useEffect(() => {
- if (!endUserCredentialEnabled)
- return
- if (!availableEndUserTypes.length)
- return
- const isValid = availableEndUserTypes.some(item => item.value === endUserCredentialType)
- if (!isValid)
- onEndUserCredentialTypeChange?.(availableEndUserTypes[0].value)
- }, [endUserCredentialEnabled, endUserCredentialType, availableEndUserTypes, onEndUserCredentialTypeChange])
-
- const handleSelectEndUserType = useCallback((value: string) => {
- onEndUserCredentialTypeChange?.(value)
- setShowEndUserTypeMenu(false)
- }, [onEndUserCredentialTypeChange])
const containerClassName = useMemo(() => {
if (showConnectGuide)
return className
@@ -145,86 +103,18 @@ const PluginAuth = ({
}, [credentials, t])
const endUserSwitch = (
-
-
-
-
-
-
-
- {t('plugin.auth.endUserCredentials.title')}
-
-
- {t('plugin.auth.endUserCredentials.desc')}
-
-
-
-
- {
- endUserCredentialEnabled && availableEndUserTypes.length > 0 && (
-
-
- {t('plugin.auth.endUserCredentials.typeLabel')}
-
-
-
-
-
-
-
-
- {canOAuth && (
-
{
- handleSelectEndUserType('oauth2')
- invalidPluginCredentialInfo()
- }}
- />
- )}
- {canApiKey && (
- {
- handleSelectEndUserType('api-key')
- invalidPluginCredentialInfo()
- }}
- />
- )}
-
-
-
-
-
- )
- }
-
-
-
+
)
return (