From 7b746a4f999124693917a4ad085cc2e7f42ec15c Mon Sep 17 00:00:00 2001 From: Billy Bao Date: Thu, 4 Dec 2025 17:47:02 +0800 Subject: [PATCH] add front end --- rag/svr/sync_data_source.py | 20 +- .../component/confluence-token-field.tsx | 191 ++++++++++++++++++ 2 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 web/src/pages/user-setting/data-source/component/confluence-token-field.tsx diff --git a/rag/svr/sync_data_source.py b/rag/svr/sync_data_source.py index 525a8bb10..4349b6f55 100644 --- a/rag/svr/sync_data_source.py +++ b/rag/svr/sync_data_source.py @@ -157,9 +157,23 @@ class Confluence(SyncBase): from common.data_source.config import DocumentSource from common.data_source.interfaces import StaticCredentialsProvider - space = (self.conf.get("space") or "").strip() - page_id = (self.conf.get("page_id") or "").strip() - index_recursively = bool(self.conf.get("index_recursively", False)) + index_mode = (self.conf.get("index_mode") or "everything").lower() + if index_mode not in {"everything", "space", "page"}: + index_mode = "everything" + + space = "" + page_id = "" + + index_recursively = False + if index_mode == "space": + space = (self.conf.get("space") or "").strip() + if not space: + raise ValueError("Space Key is required when indexing a specific Confluence space.") + elif index_mode == "page": + page_id = (self.conf.get("page_id") or "").strip() + if not page_id: + raise ValueError("Page ID is required when indexing a specific Confluence page.") + index_recursively = bool(self.conf.get("index_recursively", False)) self.connector = ConfluenceConnector( wiki_base=self.conf["wiki_base"], diff --git a/web/src/pages/user-setting/data-source/component/confluence-token-field.tsx b/web/src/pages/user-setting/data-source/component/confluence-token-field.tsx new file mode 100644 index 000000000..5fe50b931 --- /dev/null +++ b/web/src/pages/user-setting/data-source/component/confluence-token-field.tsx @@ -0,0 +1,191 @@ +import { useEffect, useMemo } from 'react'; +import { ControllerRenderProps, useFormContext } from 'react-hook-form'; + +import { Checkbox } from '@/components/ui/checkbox'; +import { Input } from '@/components/ui/input'; +import { cn } from '@/lib/utils'; + +/* ---------------- Token Field ---------------- */ + +export type ConfluenceTokenFieldProps = ControllerRenderProps & { + fieldType: 'username' | 'token'; + placeholder?: string; + disabled?: boolean; +}; + +const ConfluenceTokenField = ({ + fieldType, + value, + onChange, + placeholder, + disabled, + ...rest +}: ConfluenceTokenFieldProps) => { + return ( +
+ onChange(e.target.value)} + placeholder={ + placeholder || + (fieldType === 'token' + ? 'Enter your Confluence access token' + : 'Confluence username or email') + } + disabled={disabled} + {...rest} + /> +
+ ); +}; + +/* ---------------- Indexing Mode Field ---------------- */ + +type ConfluenceIndexingMode = 'everything' | 'space' | 'page'; + +export type ConfluenceIndexingModeFieldProps = ControllerRenderProps; + +export const ConfluenceIndexingModeField = ( + fieldProps: ConfluenceIndexingModeFieldProps, +) => { + const { value, onChange, disabled } = fieldProps; + const { watch, setValue } = useFormContext(); + + const mode = useMemo( + () => (value as ConfluenceIndexingMode) || 'everything', + [value], + ); + + const spaceValue = watch('config.space'); + const pageIdValue = watch('config.page_id'); + const indexRecursively = watch('config.index_recursively'); + + useEffect(() => { + if (!value) onChange('everything'); + }, [value, onChange]); + + const handleModeChange = (nextMode?: string) => { + const normalized = (nextMode || 'everything') as ConfluenceIndexingMode; + onChange(normalized); + + if (normalized === 'everything') { + setValue('config.space', '', { shouldDirty: true, shouldTouch: true }); + setValue('config.page_id', '', { shouldDirty: true, shouldTouch: true }); + setValue('config.index_recursively', false, { + shouldDirty: true, + shouldTouch: true, + }); + } else if (normalized === 'space') { + setValue('config.page_id', '', { shouldDirty: true, shouldTouch: true }); + setValue('config.index_recursively', false, { + shouldDirty: true, + shouldTouch: true, + }); + } else if (normalized === 'page') { + setValue('config.space', '', { shouldDirty: true, shouldTouch: true }); + } + }; + + return ( +
+
+ {INDEX_MODE_OPTIONS.map((option) => { + const isActive = option.value === mode; + return ( + + ); + })} +
+ + {mode === 'everything' && ( +

+ This connector will index all pages the provided credentials have + access to. +

+ )} + + {mode === 'space' && ( +
+
+ Space Key +
+ + setValue('config.space', e.target.value, { + shouldDirty: true, + shouldTouch: true, + }) + } + placeholder="e.g. KB" + disabled={disabled} + /> +

+ The Confluence space key to index. +

+
+ )} + + {mode === 'page' && ( +
+
Page ID
+ + setValue('config.page_id', e.target.value, { + shouldDirty: true, + shouldTouch: true, + }) + } + placeholder="e.g. 123456" + disabled={disabled} + /> +

+ The Confluence page ID to index. +

+ +
+ + setValue('config.index_recursively', Boolean(checked), { + shouldDirty: true, + shouldTouch: true, + }) + } + disabled={disabled} + /> + + Index child pages recursively + +
+
+ )} +
+ ); +}; + +const INDEX_MODE_OPTIONS = [ + { label: 'Everything', value: 'everything' }, + { label: 'Space', value: 'space' }, + { label: 'Page', value: 'page' }, +]; + +export default ConfluenceTokenField;