From 6b2cd9705fb1e9a7270b6bc5de4f156e90b6d288 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Sat, 26 Jul 2025 14:46:47 +0200 Subject: [PATCH] Add UI implementations --- .../routes/search_source_connectors_routes.py | 74 ++++ .../connectors/[connector_id]/edit/page.tsx | 27 ++ .../add/confluence-connector/page.tsx | 324 ++++++++++++++++++ .../[search_space_id]/connectors/add/page.tsx | 9 + .../documents/(manage)/page.tsx | 2 + .../components/editConnector/types.ts | 6 + surfsense_web/hooks/useConnectorEditPage.ts | 18 +- 7 files changed, 454 insertions(+), 6 deletions(-) create mode 100644 surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx diff --git a/surfsense_backend/app/routes/search_source_connectors_routes.py b/surfsense_backend/app/routes/search_source_connectors_routes.py index 4c3d691..aedab8b 100644 --- a/surfsense_backend/app/routes/search_source_connectors_routes.py +++ b/surfsense_backend/app/routes/search_source_connectors_routes.py @@ -36,6 +36,7 @@ from app.schemas import ( SearchSourceConnectorUpdate, ) from app.tasks.connectors_indexing_tasks import ( + index_confluence_pages, index_discord_messages, index_github_repos, index_jira_issues, @@ -457,6 +458,21 @@ async def index_connector_content( ) response_message = "Jira indexing started in the background." + elif connector.connector_type == SearchSourceConnectorType.CONFLUENCE_CONNECTOR: + # Run indexing in background + logger.info( + f"Triggering Confluence indexing for connector {connector_id} into search space {search_space_id} from {indexing_from} to {indexing_to}" + ) + background_tasks.add_task( + run_confluence_indexing_with_new_session, + connector_id, + search_space_id, + str(user.id), + indexing_from, + indexing_to, + ) + response_message = "Confluence indexing started in the background." + elif connector.connector_type == SearchSourceConnectorType.DISCORD_CONNECTOR: # Run indexing in background logger.info( @@ -884,3 +900,61 @@ async def run_jira_indexing( exc_info=True, ) # Optionally update status in DB to indicate failure + + +# Add new helper functions for Confluence indexing +async def run_confluence_indexing_with_new_session( + connector_id: int, + search_space_id: int, + user_id: str, + start_date: str, + end_date: str, +): + """Wrapper to run Confluence indexing with its own database session.""" + logger.info( + f"Background task started: Indexing Confluence connector {connector_id} into space {search_space_id} from {start_date} to {end_date}" + ) + async with async_session_maker() as session: + await run_confluence_indexing( + session, connector_id, search_space_id, user_id, start_date, end_date + ) + logger.info(f"Background task finished: Indexing Confluence connector {connector_id}") + + +async def run_confluence_indexing( + session: AsyncSession, + connector_id: int, + search_space_id: int, + user_id: str, + start_date: str, + end_date: str, +): + """Runs the Confluence indexing task and updates the timestamp.""" + try: + indexed_count, error_message = await index_confluence_pages( + session, + connector_id, + search_space_id, + user_id, + start_date, + end_date, + update_last_indexed=False, + ) + if error_message: + logger.error( + f"Confluence indexing failed for connector {connector_id}: {error_message}" + ) + # Optionally update status in DB to indicate failure + else: + logger.info( + f"Confluence indexing successful for connector {connector_id}. Indexed {indexed_count} documents." + ) + # Update the last indexed timestamp only on success + await update_connector_last_indexed(session, connector_id) + await session.commit() # Commit timestamp update + except Exception as e: + logger.error( + f"Critical error in run_confluence_indexing for connector {connector_id}: {e}", + exc_info=True, + ) + # Optionally update status in DB to indicate failure diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx index 918a625..bd8b61c 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx @@ -208,6 +208,33 @@ export default function EditConnectorPage() { )} + {/* == Confluence == */} + {connector.connector_type === "CONFLUENCE_CONNECTOR" && ( +
+ + + +
+ )} + {/* == Linkup == */} {connector.connector_type === "LINKUP_API" && ( { + return url.includes("atlassian.net") || url.includes("confluence"); + }, + { + message: "Please enter a valid Confluence instance URL", + }, + ), + email: z.string().email({ + message: "Please enter a valid email address.", + }), + api_token: z.string().min(10, { + message: "Confluence API Token is required and must be valid.", + }), +}); + +// Define the type for the form values +type ConfluenceConnectorFormValues = z.infer; + +export default function ConfluenceConnectorPage() { + const router = useRouter(); + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [isSubmitting, setIsSubmitting] = useState(false); + const { createConnector } = useSearchSourceConnectors(); + + // Initialize the form + const form = useForm({ + resolver: zodResolver(confluenceConnectorFormSchema), + defaultValues: { + name: "Confluence Connector", + base_url: "", + email: "", + api_token: "", + }, + }); + + // Handle form submission + const onSubmit = async (values: ConfluenceConnectorFormValues) => { + setIsSubmitting(true); + try { + await createConnector({ + name: values.name, + connector_type: "CONFLUENCE_CONNECTOR", + config: { + CONFLUENCE_BASE_URL: values.base_url, + CONFLUENCE_EMAIL: values.email, + CONFLUENCE_API_TOKEN: values.api_token, + }, + is_indexable: true, + last_indexed_at: null, + }); + + toast.success("Confluence connector created successfully!"); + + // Navigate back to connectors page + router.push(`/dashboard/${searchSpaceId}/connectors`); + } catch (error) { + console.error("Error creating connector:", error); + toast.error( + error instanceof Error ? error.message : "Failed to create connector", + ); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+ + + + + + Connect + Documentation + + + + + + Connect to Confluence + + Connect your Confluence instance to index pages and comments from your spaces. + + + + + + + You'll need to create an API token from your{" "} + + Atlassian Account Settings + + + + +
+ + ( + + Connector Name + + + + + A friendly name to identify this connector. + + + + )} + /> + + ( + + Confluence Instance URL + + + + + Your Confluence instance URL. For Atlassian Cloud, this is + typically https://yourcompany.atlassian.net + + + + )} + /> + + ( + + Email Address + + + + + Your Atlassian account email address. + + + + )} + /> + + ( + + API Token + + + + + Your Confluence API Token will be encrypted and stored securely. + + + + )} + /> + +
+ +
+ + +
+
+
+ + + + + Confluence Integration Guide + + Learn how to set up and use the Confluence connector. + + + +
+

What gets indexed?

+
    +
  • All pages from accessible spaces
  • +
  • Page content and metadata
  • +
  • Comments on pages (both footer and inline comments)
  • +
  • Page titles and descriptions
  • +
+
+ +
+

Setup Instructions

+
    +
  1. Go to your Atlassian Account Settings
  2. +
  3. Navigate to Security → API tokens
  4. +
  5. Create a new API token with appropriate permissions
  6. +
  7. Copy the token and paste it in the form above
  8. +
  9. Ensure your account has read access to the spaces you want to index
  10. +
+
+ +
+

Permissions Required

+
    +
  • Read access to Confluence spaces
  • +
  • View pages and comments
  • +
  • Access to space metadata
  • +
+
+ + + + + The connector will only index content that your account has permission to view. + Make sure your API token has the necessary permissions for the spaces you want to index. + + +
+
+
+
+
+
+ ); +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx index 3d0e59d..417036d 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx @@ -13,6 +13,7 @@ import { CollapsibleTrigger, } from "@/components/ui/collapsible"; import { + IconBook, IconBrandDiscord, IconBrandGithub, IconBrandNotion, @@ -141,6 +142,14 @@ const connectorCategories: ConnectorCategory[] = [ icon: , status: "available", }, + { + id: "confluence-connector", + title: "Confluence", + description: + "Connect to Confluence to search pages, comments and documentation.", + icon: , + status: "available", + }, ], }, { diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx index 1b66684..24a046f 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx @@ -57,6 +57,7 @@ import { IconBrandDiscord, IconBrandGithub, IconBrandNotion, + IconBrandPagekit, IconBrandSlack, IconBrandYoutube, IconLayoutKanban, @@ -180,6 +181,7 @@ const documentTypeIcons = { LINEAR_CONNECTOR: IconLayoutKanban, JIRA_CONNECTOR: IconTicket, DISCORD_CONNECTOR: IconBrandDiscord, + CONFLUENCE_CONNECTOR: IconBrandPagekit, } as const; const columns: ColumnDef[] = [ diff --git a/surfsense_web/components/editConnector/types.ts b/surfsense_web/components/editConnector/types.ts index ad16010..a54453a 100644 --- a/surfsense_web/components/editConnector/types.ts +++ b/surfsense_web/components/editConnector/types.ts @@ -32,5 +32,11 @@ export const editConnectorSchema = z.object({ LINEAR_API_KEY: z.string().optional(), LINKUP_API_KEY: z.string().optional(), DISCORD_BOT_TOKEN: z.string().optional(), + CONFLUENCE_BASE_URL: z.string().optional(), + CONFLUENCE_EMAIL: z.string().optional(), + CONFLUENCE_API_TOKEN: z.string().optional(), + JIRA_BASE_URL: z.string().optional(), + JIRA_EMAIL: z.string().optional(), + JIRA_API_TOKEN: z.string().optional(), }); export type EditConnectorFormValues = z.infer; diff --git a/surfsense_web/hooks/useConnectorEditPage.ts b/surfsense_web/hooks/useConnectorEditPage.ts index ad0992a..27790ac 100644 --- a/surfsense_web/hooks/useConnectorEditPage.ts +++ b/surfsense_web/hooks/useConnectorEditPage.ts @@ -35,15 +35,21 @@ export function useConnectorEditPage(connectorId: number, searchSpaceId: string) }); const editForm = useForm({ resolver: zodResolver(editConnectorSchema), - defaultValues: { - name: "", - SLACK_BOT_TOKEN: "", - NOTION_INTEGRATION_TOKEN: "", - SERPER_API_KEY: "", + defaultValues: { + name: "", + SLACK_BOT_TOKEN: "", + NOTION_INTEGRATION_TOKEN: "", + SERPER_API_KEY: "", TAVILY_API_KEY: "", LINEAR_API_KEY: "", DISCORD_BOT_TOKEN: "", - }, + CONFLUENCE_BASE_URL: "", + CONFLUENCE_EMAIL: "", + CONFLUENCE_API_TOKEN: "", + JIRA_BASE_URL: "", + JIRA_EMAIL: "", + JIRA_API_TOKEN: "", + }, }); // Effect to load initial data