diff --git a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx index 737e06f1b..97686bcb7 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx @@ -1,7 +1,5 @@ "use client"; -import { cn } from "@/lib/utils"; -import { useAtomValue } from "jotai"; import { ChevronDown, ChevronRight, @@ -11,15 +9,11 @@ import { FolderClosed, Image, Presentation, - RefreshCw, X, } from "lucide-react"; import type { FC } from "react"; import { useCallback, useEffect, useState } from "react"; -import { toast } from "sonner"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { ComposioDriveFolderTree } from "@/components/connectors/composio-drive-folder-tree"; -import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Select, @@ -29,7 +23,6 @@ import { SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; -import { authenticatedFetch } from "@/lib/auth-utils"; import type { ConnectorConfigProps } from "../index"; interface SelectedFolder { @@ -89,13 +82,10 @@ function getFileIconFromName(fileName: string, className: string = "size-3.5 shr return ; } -const COMPOSIO_REAUTH_ENDPOINT = "/api/v1/auth/composio/connector/reauth"; - export const ComposioDriveConfig: FC = ({ connector, onConfigChange, }) => { - const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const isIndexable = connector.config?.is_indexable as boolean; const existingFolders = @@ -107,40 +97,10 @@ export const ComposioDriveConfig: FC = ({ const [selectedFolders, setSelectedFolders] = useState(existingFolders); const [selectedFiles, setSelectedFiles] = useState(existingFiles); const [indexingOptions, setIndexingOptions] = useState(existingIndexingOptions); - const [reauthing, setReauthing] = useState(false); const [authError, setAuthError] = useState(false); const isAuthExpired = connector.config?.auth_expired === true || authError; - const handleReauth = useCallback(async () => { - if (!searchSpaceId) return; - setReauthing(true); - try { - const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; - const url = new URL(`${backendUrl}${COMPOSIO_REAUTH_ENDPOINT}`); - url.searchParams.set("connector_id", String(connector.id)); - url.searchParams.set("space_id", String(searchSpaceId)); - url.searchParams.set("return_url", window.location.pathname); - const response = await authenticatedFetch(url.toString()); - if (!response.ok) { - const data = await response.json().catch(() => ({})); - toast.error(data.detail ?? "Failed to initiate re-authentication."); - return; - } - const data = await response.json(); - if (data.auth_url) { - window.location.href = data.auth_url; - } else if (data.success) { - toast.success("Authentication refreshed successfully."); - setAuthError(false); - } - } catch { - toast.error("Failed to initiate re-authentication."); - } finally { - setReauthing(false); - } - }, [searchSpaceId, connector.id]); - const handleAuthError = useCallback(() => { setAuthError(true); }, []); @@ -276,24 +236,13 @@ export const ComposioDriveConfig: FC = ({ )} - {isAuthExpired && ( -
-

- Your Google Drive authentication has expired. -

- -
- )} + {isAuthExpired && ( +

+ Your Google Drive authentication has expired. Please re-authenticate using the button below. +

+ )} - {isEditMode ? ( + {isEditMode ? (
)} - + - {(pickerError || isAuthExpired) && ( -
- {pickerError && !isAuthExpired && ( -

{pickerError}

- )} - {isAuthExpired && ( -
-

- Your Google Drive authentication has expired. -

- -
- )} -
- )} + {pickerError && !isAuthExpired && ( +

{pickerError}

+ )} + + {isAuthExpired && ( +

+ Your Google Drive authentication has expired. Please re-authenticate using the button below. +

+ )} {/* Indexing Options */} diff --git a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/views/connector-edit-view.tsx b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/views/connector-edit-view.tsx index f4ac27504..e950cb648 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/views/connector-edit-view.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/views/connector-edit-view.tsx @@ -1,11 +1,16 @@ "use client"; +import { useAtomValue } from "jotai"; import { ArrowLeft, Info, RefreshCw, Trash2 } from "lucide-react"; import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { toast } from "sonner"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { Button } from "@/components/ui/button"; import { Spinner } from "@/components/ui/spinner"; +import { EnumConnectorName } from "@/contracts/enums/connector"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import type { SearchSourceConnector } from "@/contracts/types/connector.types"; +import { authenticatedFetch } from "@/lib/auth-utils"; import { cn } from "@/lib/utils"; import { DateRangeSelector } from "../../components/date-range-selector"; import { PeriodicSyncConfig } from "../../components/periodic-sync-config"; @@ -13,6 +18,17 @@ import { SummaryConfig } from "../../components/summary-config"; import { getConnectorDisplayName } from "../../tabs/all-connectors-tab"; import { getConnectorConfigComponent } from "../index"; +const REAUTH_ENDPOINTS: Partial> = { + [EnumConnectorName.LINEAR_CONNECTOR]: "/api/v1/auth/linear/connector/reauth", + [EnumConnectorName.NOTION_CONNECTOR]: "/api/v1/auth/notion/connector/reauth", + [EnumConnectorName.GOOGLE_DRIVE_CONNECTOR]: "/api/v1/auth/google/drive/connector/reauth", + [EnumConnectorName.GOOGLE_GMAIL_CONNECTOR]: "/api/v1/auth/google/gmail/connector/reauth", + [EnumConnectorName.GOOGLE_CALENDAR_CONNECTOR]: "/api/v1/auth/google/calendar/connector/reauth", + [EnumConnectorName.COMPOSIO_GOOGLE_DRIVE_CONNECTOR]: "/api/v1/auth/composio/connector/reauth", + [EnumConnectorName.COMPOSIO_GMAIL_CONNECTOR]: "/api/v1/auth/composio/connector/reauth", + [EnumConnectorName.COMPOSIO_GOOGLE_CALENDAR_CONNECTOR]: "/api/v1/auth/composio/connector/reauth", +}; + interface ConnectorEditViewProps { connector: SearchSourceConnector; startDate: Date | undefined; @@ -60,6 +76,41 @@ export const ConnectorEditView: FC = ({ onConfigChange, onNameChange, }) => { + const searchSpaceIdAtom = useAtomValue(activeSearchSpaceIdAtom); + const isAuthExpired = connector.config?.auth_expired === true; + const reauthEndpoint = REAUTH_ENDPOINTS[connector.connector_type]; + const [reauthing, setReauthing] = useState(false); + + const handleReauth = useCallback(async () => { + const spaceId = searchSpaceId ?? searchSpaceIdAtom; + if (!spaceId || !reauthEndpoint) return; + setReauthing(true); + try { + const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; + const url = new URL(`${backendUrl}${reauthEndpoint}`); + url.searchParams.set("connector_id", String(connector.id)); + url.searchParams.set("space_id", String(spaceId)); + url.searchParams.set("return_url", window.location.pathname); + const response = await authenticatedFetch(url.toString()); + if (!response.ok) { + const data = await response.json().catch(() => ({})); + toast.error(data.detail ?? "Failed to initiate re-authentication."); + return; + } + const data = await response.json(); + if (data.auth_url) { + window.location.href = data.auth_url; + } else if (data.success) { + toast.success(data.message ?? "Authentication refreshed successfully."); + window.location.reload(); + } + } catch { + toast.error("Failed to initiate re-authentication."); + } finally { + setReauthing(false); + } + }, [searchSpaceId, searchSpaceIdAtom, reauthEndpoint, connector.id]); + // Get connector-specific config component const ConnectorConfigComponent = useMemo( () => getConnectorConfigComponent(connector.connector_type), @@ -169,29 +220,30 @@ export const ConnectorEditView: FC = ({

- {/* Quick Index Button - shown for indexable connectors that have been configured */} - {connector.is_indexable && - onQuickIndex && ( - - )} + {/* Quick Index Button - hidden when auth is expired */} + {connector.is_indexable && + onQuickIndex && + !isAuthExpired && ( + + )} @@ -349,6 +401,16 @@ export const ConnectorEditView: FC = ({ Disconnect )} + {isAuthExpired && reauthEndpoint ? ( + + ) : ( + )} );