diff --git a/surfsense_web/components/tool-ui/google-drive/create-file.tsx b/surfsense_web/components/tool-ui/google-drive/create-file.tsx index cf0a01319..4bcb42a95 100644 --- a/surfsense_web/components/tool-ui/google-drive/create-file.tsx +++ b/surfsense_web/components/tool-ui/google-drive/create-file.tsx @@ -7,8 +7,10 @@ import { FileIcon, Loader2Icon, PencilIcon, + RefreshCwIcon, XIcon, } from "lucide-react"; +import { useParams } from "next/navigation"; import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -20,6 +22,7 @@ import { SelectValue, } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; +import { authenticatedFetch } from "@/lib/auth-utils"; interface GoogleDriveAccount { id: number; @@ -57,7 +60,17 @@ interface ErrorResult { message: string; } -type CreateGoogleDriveFileResult = InterruptResult | SuccessResult | ErrorResult; +interface InsufficientPermissionsResult { + status: "insufficient_permissions"; + connector_id: number; + message: string; +} + +type CreateGoogleDriveFileResult = + | InterruptResult + | SuccessResult + | ErrorResult + | InsufficientPermissionsResult; function isInterruptResult(result: unknown): result is InterruptResult { return ( @@ -77,6 +90,15 @@ function isErrorResult(result: unknown): result is ErrorResult { ); } +function isInsufficientPermissionsResult(result: unknown): result is InsufficientPermissionsResult { + return ( + typeof result === "object" && + result !== null && + "status" in result && + (result as InsufficientPermissionsResult).status === "insufficient_permissions" + ); +} + const FILE_TYPE_LABELS: Record = { google_doc: "Google Doc", google_sheet: "Google Sheet", @@ -390,6 +412,54 @@ function ApprovalCard({ ); } +function InsufficientPermissionsCard({ result }: { result: InsufficientPermissionsResult }) { + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [loading, setLoading] = useState(false); + + async function handleReauth() { + setLoading(true); + try { + const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; + const response = await authenticatedFetch( + `${backendUrl}/api/v1/auth/google/drive/connector/reauth?connector_id=${result.connector_id}&space_id=${searchSpaceId}` + ); + const data = await response.json(); + if (data.auth_url) { + window.location.href = data.auth_url; + } + } finally { + setLoading(false); + } + } + + return ( +
+
+
+ +
+
+

+ Additional permissions required +

+
+
+
+

{result.message}

+ +
+
+ ); +} + function ErrorCard({ result }: { result: ErrorResult }) { return (
@@ -483,6 +553,9 @@ export const CreateGoogleDriveFileToolUI = makeAssistantToolUI< return null; } + if (isInsufficientPermissionsResult(result)) + return ; + if (isErrorResult(result)) return ; return ; diff --git a/surfsense_web/components/tool-ui/google-drive/trash-file.tsx b/surfsense_web/components/tool-ui/google-drive/trash-file.tsx index 39cd9cadd..1742326b2 100644 --- a/surfsense_web/components/tool-ui/google-drive/trash-file.tsx +++ b/surfsense_web/components/tool-ui/google-drive/trash-file.tsx @@ -6,11 +6,14 @@ import { CheckIcon, InfoIcon, Loader2Icon, + RefreshCwIcon, Trash2Icon, XIcon, } from "lucide-react"; +import { useParams } from "next/navigation"; import { useState } from "react"; import { Button } from "@/components/ui/button"; +import { authenticatedFetch } from "@/lib/auth-utils"; interface GoogleDriveAccount { id: number; @@ -58,11 +61,18 @@ interface NotFoundResult { message: string; } +interface InsufficientPermissionsResult { + status: "insufficient_permissions"; + connector_id: number; + message: string; +} + type TrashGoogleDriveFileResult = | InterruptResult | SuccessResult | ErrorResult - | NotFoundResult; + | NotFoundResult + | InsufficientPermissionsResult; function isInterruptResult(result: unknown): result is InterruptResult { return ( @@ -91,6 +101,15 @@ function isNotFoundResult(result: unknown): result is NotFoundResult { ); } +function isInsufficientPermissionsResult(result: unknown): result is InsufficientPermissionsResult { + return ( + typeof result === "object" && + result !== null && + "status" in result && + (result as InsufficientPermissionsResult).status === "insufficient_permissions" + ); +} + const MIME_TYPE_LABELS: Record = { "application/vnd.google-apps.document": "Google Doc", "application/vnd.google-apps.spreadsheet": "Google Sheet", @@ -114,9 +133,7 @@ function ApprovalCard({ const account = interruptData.context?.account; const file = interruptData.context?.file; - const fileLabel = file?.mime_type - ? (MIME_TYPE_LABELS[file.mime_type] ?? "File") - : "File"; + const fileLabel = file?.mime_type ? (MIME_TYPE_LABELS[file.mime_type] ?? "File") : "File"; return (
-
- File to Trash -
+
File to Trash
{file.name}
{fileLabel}
@@ -197,7 +212,8 @@ function ApprovalCard({ {!decided && (

- ⚠️ The file will be moved to Google Drive trash. You can restore it from trash within 30 days. + ⚠️ The file will be moved to Google Drive trash. You can restore it from trash within 30 + days.

)} @@ -262,6 +278,54 @@ function ApprovalCard({ ); } +function InsufficientPermissionsCard({ result }: { result: InsufficientPermissionsResult }) { + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [loading, setLoading] = useState(false); + + async function handleReauth() { + setLoading(true); + try { + const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000"; + const response = await authenticatedFetch( + `${backendUrl}/api/v1/auth/google/drive/connector/reauth?connector_id=${result.connector_id}&space_id=${searchSpaceId}` + ); + const data = await response.json(); + if (data.auth_url) { + window.location.href = data.auth_url; + } + } finally { + setLoading(false); + } + } + + return ( +
+
+
+ +
+
+

+ Additional permissions required +

+
+
+
+

{result.message}

+ +
+
+ ); +} + function ErrorCard({ result }: { result: ErrorResult }) { return (
@@ -351,6 +415,9 @@ export const TrashGoogleDriveFileToolUI = makeAssistantToolUI< return null; } + if (isInsufficientPermissionsResult(result)) + return ; + if (isNotFoundResult(result)) return ; if (isErrorResult(result)) return ;