diff --git a/web/src/app/admin/connectors/[connector]/pages/gdrive/Credential.tsx b/web/src/app/admin/connectors/[connector]/pages/gdrive/Credential.tsx index 40e2da9cb..d139d9e24 100644 --- a/web/src/app/admin/connectors/[connector]/pages/gdrive/Credential.tsx +++ b/web/src/app/admin/connectors/[connector]/pages/gdrive/Credential.tsx @@ -493,10 +493,11 @@ export const DriveAuthSection = ({ label="Primary Admin Email:" subtext="Enter the email of an admin/owner of the Google Organization that owns the Google Drive(s) you want to index." /> + ss
- +
)} diff --git a/web/src/app/admin/connectors/[connector]/pages/gmail/Credential.tsx b/web/src/app/admin/connectors/[connector]/pages/gmail/Credential.tsx index 102e12839..2c04f6ab6 100644 --- a/web/src/app/admin/connectors/[connector]/pages/gmail/Credential.tsx +++ b/web/src/app/admin/connectors/[connector]/pages/gmail/Credential.tsx @@ -1,4 +1,4 @@ -import { Button } from "@/components/Button"; +import { Button } from "@/components/ui/button"; import { PopupSpec } from "@/components/admin/connectors/Popup"; import React, { useState, useEffect } from "react"; import { useSWRConfig } from "swr"; @@ -8,7 +8,11 @@ import { adminDeleteCredential } from "@/lib/credential"; import { setupGmailOAuth } from "@/lib/gmail"; import { GMAIL_AUTH_IS_ADMIN_COOKIE_NAME } from "@/lib/constants"; import Cookies from "js-cookie"; -import { TextFormField } from "@/components/admin/connectors/Field"; +import { + TextFormField, + SectionHeader, + SubLabel, +} from "@/components/admin/connectors/Field"; import { Form, Formik } from "formik"; import { User } from "@/lib/types"; import CardSection from "@/components/admin/CardSection"; @@ -20,10 +24,19 @@ import { import { refreshAllGoogleData } from "@/lib/googleConnector"; import { ValidSources } from "@/lib/types"; import { buildSimilarCredentialInfoURL } from "@/app/admin/connector/[ccPairId]/lib"; +import { + FiFile, + FiUpload, + FiTrash2, + FiCheck, + FiLink, + FiAlertTriangle, +} from "react-icons/fi"; +import { cn } from "@/lib/utils"; type GmailCredentialJsonTypes = "authorized_user" | "service_account"; -const DriveJsonUpload = ({ +const GmailCredentialUpload = ({ setPopup, onSuccess, }: { @@ -31,129 +44,152 @@ const DriveJsonUpload = ({ onSuccess?: () => void; }) => { const { mutate } = useSWRConfig(); - const [credentialJsonStr, setCredentialJsonStr] = useState< - string | undefined - >(); + const [isUploading, setIsUploading] = useState(false); + const [fileName, setFileName] = useState(); + + const handleFileUpload = async (file: File) => { + setIsUploading(true); + setFileName(file.name); + + const reader = new FileReader(); + reader.onload = async (loadEvent) => { + if (!loadEvent?.target?.result) { + setIsUploading(false); + return; + } + + const credentialJsonStr = loadEvent.target.result as string; + + // Check credential type + let credentialFileType: GmailCredentialJsonTypes; + try { + const appCredentialJson = JSON.parse(credentialJsonStr); + if (appCredentialJson.web) { + credentialFileType = "authorized_user"; + } else if (appCredentialJson.type === "service_account") { + credentialFileType = "service_account"; + } else { + throw new Error( + "Unknown credential type, expected one of 'OAuth Web application' or 'Service Account'" + ); + } + } catch (e) { + setPopup({ + message: `Invalid file provided - ${e}`, + type: "error", + }); + setIsUploading(false); + return; + } + + if (credentialFileType === "authorized_user") { + const response = await fetch( + "/api/manage/admin/connector/gmail/app-credential", + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: credentialJsonStr, + } + ); + if (response.ok) { + setPopup({ + message: "Successfully uploaded app credentials", + type: "success", + }); + mutate("/api/manage/admin/connector/gmail/app-credential"); + if (onSuccess) { + onSuccess(); + } + } else { + const errorMsg = await response.text(); + setPopup({ + message: `Failed to upload app credentials - ${errorMsg}`, + type: "error", + }); + } + } + + if (credentialFileType === "service_account") { + const response = await fetch( + "/api/manage/admin/connector/gmail/service-account-key", + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: credentialJsonStr, + } + ); + if (response.ok) { + setPopup({ + message: "Successfully uploaded service account key", + type: "success", + }); + mutate("/api/manage/admin/connector/gmail/service-account-key"); + if (onSuccess) { + onSuccess(); + } + } else { + const errorMsg = await response.text(); + setPopup({ + message: `Failed to upload service account key - ${errorMsg}`, + type: "error", + }); + } + } + setIsUploading(false); + }; + + reader.readAsText(file); + }; return ( - <> - { - if (!event.target.files) { - return; - } - const file = event.target.files[0]; - const reader = new FileReader(); - - reader.onload = function (loadEvent) { - if (!loadEvent?.target?.result) { - return; - } - const fileContents = loadEvent.target.result; - setCredentialJsonStr(fileContents as string); - }; - - reader.readAsText(file); - }} - /> - - - +
+
+
+ +
+
+
); }; -interface DriveJsonUploadSectionProps { +interface GmailJsonUploadSectionProps { setPopup: (popupSpec: PopupSpec | null) => void; appCredentialData?: { client_id: string }; serviceAccountCredentialData?: { service_account_email: string }; @@ -167,7 +203,7 @@ export const GmailJsonUploadSection = ({ serviceAccountCredentialData, isAdmin, onSuccess, -}: DriveJsonUploadSectionProps) => { +}: GmailJsonUploadSectionProps) => { const { mutate } = useSWRConfig(); const router = useRouter(); const [localServiceAccountData, setLocalServiceAccountData] = useState( @@ -192,154 +228,198 @@ export const GmailJsonUploadSection = ({ if (localServiceAccountData?.service_account_email) { return ( -
-
- Found existing service account key with the following Email: -

- {localServiceAccountData.service_account_email} -

-
- {isAdmin ? ( - <> -
- If you want to update these credentials, delete the existing - credentials through the button below, and then upload a new - credentials JSON. +
+ Gmail Service Account Credentials +
+
+
+ + Service Account Email: + +

+ {localServiceAccountData.service_account_email} +

-
+ + {isAdmin ? ( +
+

+ If you want to update these credentials, delete the existing + credentials below, then upload new credentials. +

+ - - ) : ( - <> -
- To change these credentials, please contact an administrator. + if (response.ok) { + mutate( + "/api/manage/admin/connector/gmail/service-account-key" + ); + // Also mutate the credential endpoints to ensure Step 2 is reset + mutate(buildSimilarCredentialInfoURL(ValidSources.Gmail)); + setPopup({ + message: "Successfully deleted service account key", + type: "success", + }); + // Immediately update local state + setLocalServiceAccountData(undefined); + handleSuccess(); + } else { + const errorMsg = await response.text(); + setPopup({ + message: `Failed to delete service account key - ${errorMsg}`, + type: "error", + }); + } + }} + > + Delete Credentials +
- - )} + ) : ( +

+ To change these credentials, please contact an administrator. +

+ )} +
); } if (localAppCredentialData?.client_id) { return ( -
-
- Found existing app credentials with the following Client ID: -

{localAppCredentialData.client_id}

-
- {isAdmin ? ( - <> -
- If you want to update these credentials, delete the existing - credentials through the button below, and then upload a new - credentials JSON. +
+ Gmail OAuth Application Credentials +
+
+
+ + + Found existing OAuth credentials + +
+
+ + Client ID: + +

+ {localAppCredentialData.client_id} +

- - - ) : ( -
- To change these credentials, please contact an administrator.
- )} + + {isAdmin ? ( +
+

+ If you want to update these credentials, delete the existing + credentials below, then upload new credentials. +

+ +
+ ) : ( +

+ To change these credentials, please contact an administrator. +

+ )} +
); } if (!isAdmin) { return ( -
-

- Curators are unable to set up the Gmail credentials. To add a Gmail - connector, please contact an administrator. -

+
+
+ +

+ Curators are unable to set up the Gmail credentials. To add a Gmail + connector, please contact an administrator. +

+
); } return ( -
-

- Follow the guide{" "} - - here - {" "} - to either (1) setup a Google OAuth App in your company workspace or (2) - create a Service Account. -
-
- Download the credentials JSON if choosing option (1) or the Service - Account key JSON if choosing option (2), and upload it here. -

- +
+ Setup Gmail Credentials +
+

+ Follow these steps to connect your Gmail: +

+
    +
  1. + Create credentials - You have + two options: +
      +
    • Set up a Google OAuth App in your company workspace
    • +
    • Create a Service Account with appropriate permissions
    • +
    +
  2. +
  3. + Download credentials - Save the + JSON file to your computer +
  4. +
  5. + Upload credentials - Select the + JSON file below to automatically upload +
  6. +
+ + + +
); }; -interface DriveCredentialSectionProps { +interface GmailCredentialSectionProps { gmailPublicCredential?: Credential; gmailServiceAccountCredential?: Credential; serviceAccountKeyData?: { service_account_email: string }; @@ -387,7 +467,7 @@ export const GmailAuthSection = ({ refreshCredentials, connectorExists, user, -}: DriveCredentialSectionProps) => { +}: GmailCredentialSectionProps) => { const router = useRouter(); const [isAuthenticating, setIsAuthenticating] = useState(false); const [localServiceAccountData, setLocalServiceAccountData] = useState( @@ -420,144 +500,179 @@ export const GmailAuthSection = ({ localGmailPublicCredential || localGmailServiceAccountCredential; if (existingCredential) { return ( - <> -

- Uploaded and authenticated credential already exists! -

- - +
+ Gmail Authentication Status +
+
+ +
+ Authentication Complete +

+ Your Gmail credentials have been uploaded and authenticated + successfully. +

+
+
+ +
+
); } if (localServiceAccountData?.service_account_email) { return (
- { - formikHelpers.setSubmitting(true); - try { - const response = await fetch( - "/api/manage/admin/connector/gmail/service-account-credential", - { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - google_primary_admin: values.google_primary_admin, - }), - } - ); + Complete Gmail Authentication +
+
+

+ Enter the email of an admin/owner of the Google Organization that + owns the Gmail account(s) you want to index. +

+
- if (response.ok) { + { + formikHelpers.setSubmitting(true); + try { + const response = await fetch( + "/api/manage/admin/connector/gmail/service-account-credential", + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + google_primary_admin: values.google_primary_admin, + }), + } + ); + + if (response.ok) { + setPopup({ + message: "Successfully created service account credential", + type: "success", + }); + refreshCredentials(); + } else { + const errorMsg = await response.text(); + setPopup({ + message: `Failed to create service account credential - ${errorMsg}`, + type: "error", + }); + } + } catch (error) { setPopup({ - message: "Successfully created service account credential", - type: "success", - }); - refreshCredentials(); - } else { - const errorMsg = await response.text(); - setPopup({ - message: `Failed to create service account credential - ${errorMsg}`, + message: `Failed to create service account credential - ${error}`, type: "error", }); + } finally { + formikHelpers.setSubmitting(false); } - } catch (error) { - setPopup({ - message: `Failed to create service account credential - ${error}`, - type: "error", - }); - } finally { - formikHelpers.setSubmitting(false); - } - }} - > - {({ isSubmitting }) => ( -
- -
- -
- - )} -
+ }} + > + {({ isSubmitting }) => ( +
+ +
+ +
+ + )} + +
); } if (localAppCredentialData?.client_id) { return ( -
-

- Next, you must provide credentials via OAuth. This gives us read - access to the emails you have access to in your Gmail account. -

- + }} + > + {isAuthenticating ? "Authenticating..." : "Authenticate with Gmail"} + +
); } // case where no keys have been uploaded in step 1 return ( -

- Please upload either a OAuth Client Credential JSON or a Gmail Service - Account Key JSON in Step 1 before moving onto Step 2. -

+
+ Gmail Authentication +
+
+ +

+ Please upload either an OAuth Client Credential JSON or a Gmail + Service Account Key JSON first before authenticating. +

+
+
+
); };