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 ? (
+
+ ) : (
+ )}
);