From 6b94d30a5f003d503601c447dd9cd5e5248bfb46 Mon Sep 17 00:00:00 2001 From: yessenia Date: Fri, 26 Sep 2025 16:27:39 +0800 Subject: [PATCH] fix: oauth subscription --- web/app/components/base/select/custom.tsx | 2 + .../subscription-list/create/api-key.tsx | 311 ------------------ .../subscription-list/create/common-modal.tsx | 10 +- .../subscription-list/create/index.tsx | 143 ++++---- .../subscription-list/create/manual.tsx | 219 ------------ .../subscription-list/create/oauth-client.tsx | 6 +- .../subscription-list/create/oauth.tsx | 289 ---------------- .../subscription-list/index.tsx | 2 +- 8 files changed, 89 insertions(+), 893 deletions(-) delete mode 100644 web/app/components/plugins/plugin-detail-panel/subscription-list/create/api-key.tsx delete mode 100644 web/app/components/plugins/plugin-detail-panel/subscription-list/create/manual.tsx delete mode 100644 web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth.tsx diff --git a/web/app/components/base/select/custom.tsx b/web/app/components/base/select/custom.tsx index 444c975f7e..f9032658c3 100644 --- a/web/app/components/base/select/custom.tsx +++ b/web/app/components/base/select/custom.tsx @@ -58,6 +58,7 @@ const CustomSelect = ({ onOpenChange, placement, offset, + triggerPopupSameWidth = true, } = containerProps || {} const { className: triggerClassName, @@ -85,6 +86,7 @@ const CustomSelect = ({ offset={offset || 4} open={mergedOpen} onOpenChange={handleOpenChange} + triggerPopupSameWidth={triggerPopupSameWidth} > handleOpenChange(!mergedOpen)} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/api-key.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/api-key.tsx deleted file mode 100644 index 65be93a8fa..0000000000 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/api-key.tsx +++ /dev/null @@ -1,311 +0,0 @@ -'use client' -import React, { useState } from 'react' -import { useTranslation } from 'react-i18next' -import { - RiArrowLeftLine, - RiArrowRightLine, - RiCloseLine, -} from '@remixicon/react' -import Modal from '@/app/components/base/modal' -import Button from '@/app/components/base/button' -import Input from '@/app/components/base/input' -import Toast from '@/app/components/base/toast' -import Form from '@/app/components/base/form/form-scenarios/auth' -import type { FormRefObject } from '@/app/components/base/form/types' -import { - useBuildTriggerSubscription, - useCreateTriggerSubscriptionBuilder, - useVerifyTriggerSubscriptionBuilder, -} from '@/service/use-triggers' -import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types' -import { usePluginStore } from '../../store' - -type Props = { - onClose: () => void - onSuccess: () => void -} - -enum ApiKeyStep { - Verify = 'verify', - Configuration = 'configuration', -} - -export const ApiKeyCreateModal = ({ onClose, onSuccess }: Props) => { - const { t } = useTranslation() - const detail = usePluginStore(state => state.detail) - // State - const [currentStep, setCurrentStep] = useState(ApiKeyStep.Verify) - const [subscriptionName, setSubscriptionName] = useState('') - const [subscriptionBuilder, setSubscriptionBuilder] = useState(null) - const [verificationError, setVerificationError] = useState('') - - // Form refs - const credentialsFormRef = React.useRef(null) - const parametersFormRef = React.useRef(null) - - // API mutations - const { mutate: createBuilder, isPending: isCreatingBuilder } = useCreateTriggerSubscriptionBuilder() - const { mutate: verifyBuilder, isPending: isVerifying } = useVerifyTriggerSubscriptionBuilder() - const { mutate: buildSubscription, isPending: isBuilding } = useBuildTriggerSubscription() - - // Get provider name and schemas - const providerName = `${detail?.plugin_id}/${detail?.declaration.name}` - const credentialsSchema = detail?.declaration.trigger?.credentials_schema || [] - const parametersSchema = detail?.declaration.trigger?.subscription_schema?.parameters_schema || [] - - const handleVerify = () => { - const credentialsFormValues = credentialsFormRef.current?.getFormValues({}) || { values: {}, isCheckValidated: false } - const credentials = credentialsFormValues.values - - if (!Object.keys(credentials).length) { - Toast.notify({ - type: 'error', - message: 'Please fill in all required credentials', - }) - return - } - - setVerificationError('') - - // First create builder - createBuilder( - { - provider: providerName, - credential_type: TriggerCredentialTypeEnum.ApiKey, - }, - { - onSuccess: (response) => { - const builder = response.subscription_builder - setSubscriptionBuilder(builder) - - // setCurrentStep('configuration') - - verifyBuilder( - { - provider: providerName, - subscriptionBuilderId: builder.id, - credentials, - }, - { - onSuccess: () => { - Toast.notify({ - type: 'success', - message: t('pluginTrigger.modal.apiKey.verify.success'), - }) - setCurrentStep(ApiKeyStep.Configuration) - }, - onError: (error: any) => { - setVerificationError(error?.message || t('pluginTrigger.modal.apiKey.verify.error')) - }, - }, - ) - }, - onError: (error: any) => { - Toast.notify({ - type: 'error', - message: error?.message || t('pluginTrigger.modal.errors.verifyFailed'), - }) - }, - }, - ) - } - - const handleCreate = () => { - if (!subscriptionName.trim()) { - Toast.notify({ - type: 'error', - message: t('pluginTrigger.modal.form.subscriptionName.required'), - }) - return - } - - if (!subscriptionBuilder) - return - - buildSubscription( - { - provider: providerName, - subscriptionBuilderId: subscriptionBuilder.id, - }, - { - onSuccess: () => { - Toast.notify({ - type: 'success', - message: 'Subscription created successfully', - }) - onSuccess() - onClose() - }, - onError: (error: any) => { - Toast.notify({ - type: 'error', - message: error?.message || t('modal.errors.createFailed'), - }) - }, - }, - ) - } - - const handleBack = () => { - setCurrentStep(ApiKeyStep.Verify) - } - - return ( - -
-
- {currentStep === ApiKeyStep.Configuration && ( - - )} -

- {t('pluginTrigger.modal.apiKey.title')} -

-
- -
- - {/* Step indicator */} -
-
-
-
- 1 -
- {t('pluginTrigger.modal.steps.verify')} -
- -
- -
-
- 2 -
- {t('pluginTrigger.modal.steps.configuration')} -
-
-
- -
- {currentStep === ApiKeyStep.Verify ? ( - // Step 1: Verify Credentials -
- - {credentialsSchema.length > 0 && ( -
-
-
- )} - - {verificationError && ( -
-
- {verificationError} -
-
- )} -
- ) : ( - // Step 2: Configuration -
- {/*
-

- {t('pluginTrigger.modal.apiKey.configuration.title')} -

-

- {t('pluginTrigger.modal.apiKey.configuration.description')} -

-
*/} - - {/* Subscription Name */} -
- - setSubscriptionName(e.target.value)} - placeholder={t('pluginTrigger.modal.form.subscriptionName.placeholder')} - /> -
- - {/* Callback URL (read-only) */} - {subscriptionBuilder?.endpoint && ( -
- - -
- {t('pluginTrigger.modal.form.callbackUrl.description')} -
-
- )} - - {/* Dynamic Parameters Form */} - {parametersSchema.length > 0 && ( -
-
- Subscription Parameters -
- -
- )} -
- )} -
- - {/* Footer */} -
- - - {currentStep === ApiKeyStep.Verify ? ( - - ) : ( - - )} -
-
- ) -} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx index 31c0876651..52bd686a0e 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx @@ -18,12 +18,13 @@ import { import { RiLoader2Line } from '@remixicon/react' import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { usePluginStore } from '../../store' +import { usePluginStore, usePluginSubscriptionStore } from '../../store' import LogViewer from '../log-viewer' type Props = { onClose: () => void createType: SupportedCreationMethods + builder?: TriggerSubscriptionBuilder } const CREDENTIAL_TYPE_MAP: Record = { @@ -58,14 +59,15 @@ const MultiSteps = ({ currentStep }: { currentStep: ApiKeyStep }) => { } -export const CommonCreateModal = ({ onClose, createType }: Props) => { +export const CommonCreateModal = ({ onClose, createType, builder }: Props) => { const { t } = useTranslation() const detail = usePluginStore(state => state.detail) + const { refresh } = usePluginSubscriptionStore() const [currentStep, setCurrentStep] = useState(createType === SupportedCreationMethods.APIKEY ? ApiKeyStep.Verify : ApiKeyStep.Configuration) const [subscriptionName, setSubscriptionName] = useState('') - const [subscriptionBuilder, setSubscriptionBuilder] = useState() + const [subscriptionBuilder, setSubscriptionBuilder] = useState(builder) const [verificationError, setVerificationError] = useState('') const { mutate: verifyCredentials, isPending: isVerifyingCredentials } = useVerifyTriggerSubscriptionBuilder() @@ -184,8 +186,8 @@ export const CommonCreateModal = ({ onClose, createType }: Props) => { type: 'success', message: 'Subscription created successfully', }) - // onSuccess() onClose() + refresh?.() }, onError: (error: any) => { Toast.notify({ diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx index f8fe63fb75..c9137662b3 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx @@ -1,13 +1,15 @@ import { ActionButton } from '@/app/components/base/action-button' +import Badge from '@/app/components/base/badge' import { Button } from '@/app/components/base/button' -import Modal from '@/app/components/base/modal' -import { PortalSelect } from '@/app/components/base/select' +import type { Option } from '@/app/components/base/select/custom' +import CustomSelect from '@/app/components/base/select/custom' import Toast from '@/app/components/base/toast' import Tooltip from '@/app/components/base/tooltip' +import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' import { openOAuthPopup } from '@/hooks/use-oauth' import { useInitiateTriggerOAuth, useTriggerOAuthConfig, useTriggerProviderInfo } from '@/service/use-triggers' import cn from '@/utils/classnames' -import { RiAddLine, RiCloseLine, RiEqualizer2Line } from '@remixicon/react' +import { RiAddLine, RiEqualizer2Line } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -16,30 +18,6 @@ import { usePluginStore } from '../../store' import { CommonCreateModal } from './common-modal' import { OAuthClientSettingsModal } from './oauth-client' -export const CreateModal = () => { - const { t } = useTranslation() - - return ( - -
-

- {t('pluginTrigger.modal.oauth.title')} -

- - - -
-
- ) -} - export enum CreateButtonType { FULL_BUTTON = 'full-button', ICON_BUTTON = 'icon-button', @@ -67,7 +45,7 @@ export const DEFAULT_METHOD = 'default' */ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BUTTON }: Props) => { const { t } = useTranslation() - const [selectedCreateType, setSelectedCreateType] = useState(null) + const [selectedCreateInfo, setSelectedCreateInfo] = useState<{ type: SupportedCreationMethods, builder?: TriggerSubscriptionBuilder } | null>(null) const detail = usePluginStore(state => state.detail) const provider = `${detail?.plugin_id}/${detail?.declaration.name}` @@ -99,25 +77,32 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU showClientSettingsModal() } - const allOptions = [ - { - value: SupportedCreationMethods.OAUTH, - name: t('pluginTrigger.subscription.addType.options.oauth.title'), - extra: , - show: supportedMethods.includes(SupportedCreationMethods.OAUTH), - }, - { - value: SupportedCreationMethods.APIKEY, - name: t('pluginTrigger.subscription.addType.options.apiKey.title'), - show: supportedMethods.includes(SupportedCreationMethods.APIKEY), - }, - { - value: SupportedCreationMethods.MANUAL, - name: t('pluginTrigger.subscription.addType.options.manual.description'), // 使用 description 作为标题 - tooltip: , - show: supportedMethods.includes(SupportedCreationMethods.MANUAL), - }, - ] + const allOptions = useMemo(() => { + const showCustomBadge = oauthConfig?.custom_enabled && oauthConfig?.custom_configured + + return [ + { + value: SupportedCreationMethods.OAUTH, + label: t('pluginTrigger.subscription.addType.options.oauth.title'), + tag: !showCustomBadge ? null : + {t('plugin.auth.custom')} + , + extra: , + show: supportedMethods.includes(SupportedCreationMethods.OAUTH), + }, + { + value: SupportedCreationMethods.APIKEY, + label: t('pluginTrigger.subscription.addType.options.apiKey.title'), + show: supportedMethods.includes(SupportedCreationMethods.APIKEY), + }, + { + value: SupportedCreationMethods.MANUAL, + label: t('pluginTrigger.subscription.addType.options.manual.description'), // 使用 description 作为标题 + extra: , + show: supportedMethods.includes(SupportedCreationMethods.MANUAL), + }, + ] + }, [t, oauthConfig, supportedMethods, methodType]) const onChooseCreateType = (type: SupportedCreationMethods) => { if (type === SupportedCreationMethods.OAUTH) { @@ -130,7 +115,7 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU type: 'success', message: t('pluginTrigger.modal.oauth.authorized'), }) - setSelectedCreateType(SupportedCreationMethods.OAUTH) + setSelectedCreateInfo({ type: SupportedCreationMethods.OAUTH, builder: response.subscription_builder }) } }) }, @@ -147,7 +132,7 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU } } else { - setSelectedCreateType(type) + setSelectedCreateInfo({ type }) } } @@ -164,9 +149,23 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU return null return <> - { + + options={allOptions.filter(option => option.show)} + value={methodType} + onChange={value => onChooseCreateType(value as any)} + containerProps={{ + open: methodType === DEFAULT_METHOD ? undefined : false, + placement: 'bottom-start', + offset: 4, + triggerPopupSameWidth: buttonType === CreateButtonType.FULL_BUTTON, + }} + triggerProps={{ + className: cn('h-8 bg-transparent px-0 hover:bg-transparent', methodType !== DEFAULT_METHOD && 'pointer-events-none', buttonType === CreateButtonType.FULL_BUTTON && 'grow'), + }} + popupProps={{ + wrapperClassName: 'z-[1000]', + }} + CustomTrigger={() => { return buttonType === CreateButtonType.FULL_BUTTON ? ( - ) : - - + ) : ( + + + + ) }} - triggerClassName='h-8' - popupClassName={cn('z-[1000]')} - value={methodType} - items={allOptions.filter(option => option.show)} - onSelect={item => onChooseCreateType(item.value as any)} + CustomOption={option => ( + <> +
+ {option.label} + {option.tag} +
+ {option.extra} + + )} /> - {selectedCreateType && ( + {selectedCreateInfo && ( setSelectedCreateType(null)} + createType={selectedCreateInfo.type} + builder={selectedCreateInfo.builder} + onClose={() => setSelectedCreateInfo(null)} /> )} {isShowClientSettingsModal && ( setSelectedCreateType(SupportedCreationMethods.OAUTH)} + showOAuthCreateModal={builder => setSelectedCreateInfo({ type: SupportedCreationMethods.OAUTH, builder })} /> )} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/manual.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/manual.tsx deleted file mode 100644 index 63555570b4..0000000000 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/manual.tsx +++ /dev/null @@ -1,219 +0,0 @@ -'use client' -import React, { useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { - RiCloseLine, - RiLoader2Line, -} from '@remixicon/react' -import Modal from '@/app/components/base/modal' -import Button from '@/app/components/base/button' -import Input from '@/app/components/base/input' -import Toast from '@/app/components/base/toast' -import { - useBuildTriggerSubscription, - useCreateTriggerSubscriptionBuilder, - useTriggerSubscriptionBuilderLogs, -} from '@/service/use-triggers' -import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' -import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types' -import { BaseForm } from '@/app/components/base/form/components/base' -import ActionButton from '@/app/components/base/action-button' -import { CopyFeedbackNew } from '@/app/components/base/copy-feedback' -import type { FormRefObject } from '@/app/components/base/form/types' -import LogViewer from '../log-viewer' -import { usePluginStore } from '../../store' - -type Props = { - onClose: () => void - onSuccess: () => void -} - -export const ManualCreateModal = ({ onClose, onSuccess }: Props) => { - const { t } = useTranslation() - const detail = usePluginStore(state => state.detail) - - const [subscriptionName, setSubscriptionName] = useState('') - const [subscriptionBuilder, setSubscriptionBuilder] = useState() - - const { mutate: createBuilder /* isPending: isCreatingBuilder */ } = useCreateTriggerSubscriptionBuilder() - const { mutate: buildSubscription, isPending: isBuilding } = useBuildTriggerSubscription() - - const providerName = `${detail?.plugin_id}/${detail?.declaration.name}` - const propertiesSchema = detail?.declaration.trigger.subscription_schema.properties_schema || [] - const propertiesFormRef = React.useRef(null) - - const { data: logData } = useTriggerSubscriptionBuilderLogs( - providerName, - subscriptionBuilder?.id || '', - { - enabled: !!subscriptionBuilder?.id, - refetchInterval: 3000, - }, - ) - - const logs = logData?.logs || [] - - useEffect(() => { - if (!subscriptionBuilder) { - createBuilder( - { - provider: providerName, - credential_type: TriggerCredentialTypeEnum.Unauthorized, - }, - { - onSuccess: (response) => { - const builder = response.subscription_builder - setSubscriptionBuilder(builder) - }, - onError: (error) => { - Toast.notify({ - type: 'error', - message: t('pluginTrigger.modal.errors.createFailed'), - }) - console.error('Failed to create subscription builder:', error) - }, - }, - ) - } - }, [createBuilder, providerName, subscriptionBuilder, t]) - - const handleCreate = () => { - if (!subscriptionName.trim()) { - Toast.notify({ - type: 'error', - message: t('pluginTrigger.modal.form.subscriptionName.required'), - }) - return - } - - if (!subscriptionBuilder) - return - - const formValues = propertiesFormRef.current?.getFormValues({}) || { values: {}, isCheckValidated: false } - if (!formValues.isCheckValidated) { - Toast.notify({ - type: 'error', - message: t('pluginTrigger.modal.form.properties.required'), - }) - return - } - - buildSubscription( - { - provider: providerName, - subscriptionBuilderId: subscriptionBuilder.id, - params: { - name: subscriptionName, - properties: formValues.values, - }, - }, - { - onSuccess: () => { - Toast.notify({ - type: 'success', - message: 'Subscription created successfully', - }) - onSuccess() - onClose() - }, - onError: (error: any) => { - Toast.notify({ - type: 'error', - message: error?.message || t('pluginTrigger.modal.errors.createFailed'), - }) - }, - }, - ) - } - - return ( - -
-

- {t('pluginTrigger.modal.manual.title')} -

- - - -
- -
-
- - setSubscriptionName(e.target.value)} - placeholder={t('pluginTrigger.modal.form.subscriptionName.placeholder')} - /> -
- -
- -
- - -
-
- {t('pluginTrigger.modal.form.callbackUrl.description')} -
-
- {propertiesSchema.length > 0 && ( -
- -
- )} - -
-
-
- REQUESTS HISTORY -
-
-
- -
-
- -
-
- Awaiting request from {detail?.declaration.name}... -
-
- - -
-
- -
- - -
- - ) -} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx index 1a92bd6547..bff06b6ff9 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx @@ -24,7 +24,7 @@ import { usePluginStore } from '../../store' type Props = { oauthConfig?: TriggerOAuthConfig onClose: () => void - showOAuthCreateModal: () => void + showOAuthCreateModal: (builder: TriggerSubscriptionBuilder) => void } enum AuthorizationStatusEnum { @@ -68,7 +68,7 @@ export const OAuthClientSettingsModal = ({ oauthConfig, onClose, showOAuthCreate message: t('pluginTrigger.modal.oauth.authorization.authSuccess'), }) onClose() - showOAuthCreateModal() + showOAuthCreateModal(response.subscription_builder) } }) }, @@ -198,7 +198,7 @@ export const OAuthClientSettingsModal = ({ oauthConfig, onClose, showOAuthCreate /> ))}
- {oauthConfig?.redirect_uri && ( + {clientType === ClientTypeEnum.Custom && oauthConfig?.redirect_uri && (
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth.tsx deleted file mode 100644 index 33bab4eb80..0000000000 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth.tsx +++ /dev/null @@ -1,289 +0,0 @@ -'use client' -import React, { useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { - RiClipboardLine, - RiCloseLine, - RiInformation2Fill, -} from '@remixicon/react' -import Modal from '@/app/components/base/modal' -import Button from '@/app/components/base/button' -import Input from '@/app/components/base/input' -import Toast from '@/app/components/base/toast' -import Form from '@/app/components/base/form/form-scenarios/auth' -import type { FormRefObject } from '@/app/components/base/form/types' -import { - useBuildTriggerSubscription, - useInitiateTriggerOAuth, - useVerifyTriggerSubscriptionBuilder, -} from '@/service/use-triggers' -import ActionButton from '@/app/components/base/action-button' -import type { TriggerOAuthConfig, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' -import { usePluginStore } from '../../store' - -type Props = { - oauthConfig?: TriggerOAuthConfig - onClose: () => void - onSuccess: () => void -} - -enum OAuthStepEnum { - Setup = 'setup', - Configuration = 'configuration', -} - -enum AuthorizationStatusEnum { - Pending = 'pending', - Success = 'success', - Failed = 'failed', -} - -export const OAuthCreateModal = ({ oauthConfig, onClose, onSuccess }: Props) => { - const { t } = useTranslation() - const detail = usePluginStore(state => state.detail) - const [currentStep, setCurrentStep] = useState(OAuthStepEnum.Setup) - const [subscriptionName, setSubscriptionName] = useState('') - const [authorizationUrl, setAuthorizationUrl] = useState('') - const [subscriptionBuilder, setSubscriptionBuilder] = useState() - const [authorizationStatus, setAuthorizationStatus] = useState() - - const clientFormRef = React.useRef(null) - const parametersFormRef = React.useRef(null) - - const providerName = `${detail?.plugin_id}/${detail?.declaration.name}` - const clientSchema = detail?.declaration.trigger?.oauth_schema?.client_schema || [] - const parametersSchema = detail?.declaration.trigger?.subscription_schema?.parameters_schema || [] - - const { mutate: initiateOAuth } = useInitiateTriggerOAuth() - const { mutate: verifyBuilder } = useVerifyTriggerSubscriptionBuilder() - const { mutate: buildSubscription, isPending: isBuilding } = useBuildTriggerSubscription() - - useEffect(() => { - initiateOAuth(providerName, { - onSuccess: (response) => { - setAuthorizationUrl(response.authorization_url) - setSubscriptionBuilder(response.subscription_builder) - }, - onError: (error: any) => { - Toast.notify({ - type: 'error', - message: error?.message || t('pluginTrigger.modal.errors.authFailed'), - }) - }, - }) - }, [initiateOAuth, providerName, t]) - - useEffect(() => { - if (currentStep === OAuthStepEnum.Setup && subscriptionBuilder && authorizationStatus === AuthorizationStatusEnum.Pending) { - const pollInterval = setInterval(() => { - verifyBuilder( - { - provider: providerName, - subscriptionBuilderId: subscriptionBuilder.id, - }, - { - onSuccess: () => { - setAuthorizationStatus(AuthorizationStatusEnum.Success) - setCurrentStep(OAuthStepEnum.Configuration) - Toast.notify({ - type: 'success', - message: t('pluginTrigger.modal.oauth.authorization.authSuccess'), - }) - clearInterval(pollInterval) - }, - onError: () => { - // Continue polling - auth might still be in progress - }, - }, - ) - }, 3000) - - return () => clearInterval(pollInterval) - } - }, [currentStep, subscriptionBuilder, authorizationStatus, verifyBuilder, providerName, t]) - - const handleAuthorize = () => { - const clientFormValues = clientFormRef.current?.getFormValues({}) || { values: {}, isCheckValidated: false } - const clientParams = clientFormValues.values - - if (!Object.keys(clientParams).length) { - Toast.notify({ - type: 'error', - message: t('pluginTrigger.modal.oauth.authorization.authFailed'), - }) - return - } - setAuthorizationStatus(AuthorizationStatusEnum.Pending) - if (authorizationUrl) { - // Open authorization URL in new window - window.open(authorizationUrl, '_blank', 'width=500,height=600') - } - } - - const handleCreate = () => { - if (!subscriptionName.trim()) { - Toast.notify({ - type: 'error', - message: t('pluginTrigger.modal.form.subscriptionName.required'), - }) - return - } - - if (!subscriptionBuilder) - return - - const parameters = parametersFormRef.current?.getFormValues({})?.values - - buildSubscription( - { - provider: providerName, - subscriptionBuilderId: subscriptionBuilder.id, - params: { - name: subscriptionName, - parameters, - } as Record, - }, - { - onSuccess: () => { - Toast.notify({ - type: 'success', - message: t('pluginTrigger.modal.oauth.configuration.success'), - }) - onSuccess() - onClose() - }, - onError: (error: any) => { - Toast.notify({ - type: 'error', - message: error?.message || t('pluginTrigger.modal.errors.createFailed'), - }) - }, - }, - ) - } - - return ( - -
-

- {t('pluginTrigger.modal.oauth.title')} -

- - - -
- -
- {currentStep === OAuthStepEnum.Setup && ( - <> - {oauthConfig?.redirect_uri && ( -
-
- -
-
-
- {t('pluginTrigger.modal.oauthRedirectInfo')} -
-
- {oauthConfig.redirect_uri} -
- -
-
- )} - - {clientSchema.length > 0 && ( - - )} - - )} - - {currentStep === OAuthStepEnum.Configuration && ( -
-
- - setSubscriptionName(e.target.value)} - placeholder={t('pluginTrigger.modal.form.subscriptionName.placeholder')} - /> -
- - {subscriptionBuilder?.endpoint && ( -
- - -
- {t('pluginTrigger.modal.form.callbackUrl.description')} -
-
- )} - - {parametersSchema.length > 0 && ( - - )} -
- )} -
- -
- - - {currentStep === OAuthStepEnum.Setup && ( - - )} - - {currentStep === OAuthStepEnum.Configuration && ( - - )} -
-
- ) -} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/index.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/index.tsx index 875c662705..4d1c725b7e 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/index.tsx @@ -40,7 +40,7 @@ export const SubscriptionList = () => {
{ hasSubscriptions - &&
+ &&
{t('pluginTrigger.subscription.listNum', { num: subscriptions?.length || 0 })}