Egnyte connector (#3420)

This commit is contained in:
Chris Weaver
2024-12-10 16:07:33 -08:00
committed by GitHub
parent fe83f676df
commit 4e4214b82c
21 changed files with 772 additions and 24 deletions

BIN
web/public/Egnyte.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -49,6 +49,7 @@ import { useRouter } from "next/navigation";
import CardSection from "@/components/admin/CardSection";
import { prepareOAuthAuthorizationRequest } from "@/lib/oauth_utils";
import { EE_ENABLED, NEXT_PUBLIC_CLOUD_ENABLED } from "@/lib/constants";
import { getConnectorOauthRedirectUrl } from "@/lib/connectors/oauth";
export interface AdvancedConfig {
refreshFreq: number;
pruneFreq: number;
@@ -442,11 +443,19 @@ export default function AddConnector({
{/* Button to pop up a form to manually enter credentials */}
<button
className="mt-6 text-sm bg-background-900 px-2 py-1.5 flex text-text-200 flex-none rounded mr-4"
onClick={() =>
setCreateConnectorToggle(
(createConnectorToggle) => !createConnectorToggle
)
}
onClick={async () => {
const redirectUrl =
await getConnectorOauthRedirectUrl(connector);
// if redirect is supported, just use it
if (redirectUrl) {
window.location.href = redirectUrl;
} else {
setCreateConnectorToggle(
(createConnectorToggle) =>
!createConnectorToggle
);
}
}}
>
Create New
</button>

View File

@@ -0,0 +1,50 @@
import { INTERNAL_URL } from "@/lib/constants";
import { NextRequest, NextResponse } from "next/server";
// TODO: deprecate this and just go directly to the backend via /api/...
// For some reason Egnyte doesn't work when using /api, so leaving this as is for now
// If we do try and remove this, make sure we test the Egnyte connector oauth flow
export async function GET(request: NextRequest) {
if (process.env.NODE_ENV !== "development") {
return NextResponse.json(
{ message: "This API is only available in development mode." },
{ status: 404 }
);
}
try {
const backendUrl = new URL(INTERNAL_URL);
// Copy path and query parameters from incoming request
backendUrl.pathname = request.nextUrl.pathname;
backendUrl.search = request.nextUrl.search;
const response = await fetch(backendUrl, {
method: "GET",
headers: request.headers,
body: request.body,
signal: request.signal,
// @ts-ignore
duplex: "half",
});
const responseData = await response.json();
if (responseData.redirect_url) {
return NextResponse.redirect(responseData.redirect_url);
}
return new NextResponse(JSON.stringify(responseData), {
status: response.status,
headers: response.headers,
});
} catch (error: unknown) {
console.error("Proxy error:", error);
return NextResponse.json(
{
message: "Proxy error",
error:
error instanceof Error ? error.message : "An unknown error occurred",
},
{ status: 500 }
);
}
}

View File

@@ -28,6 +28,7 @@ import {
ConfluenceCredentialJson,
Credential,
} from "@/lib/connectors/credentials";
import { getConnectorOauthRedirectUrl } from "@/lib/connectors/oauth";
export default function CredentialSection({
ccPair,
@@ -38,9 +39,14 @@ export default function CredentialSection({
sourceType: ValidSources;
refresh: () => void;
}) {
const makeShowCreateCredential = () => {
setShowModifyCredential(false);
setShowCreateCredential(true);
const makeShowCreateCredential = async () => {
const redirectUrl = await getConnectorOauthRedirectUrl(sourceType);
if (redirectUrl) {
window.location.href = redirectUrl;
} else {
setShowModifyCredential(false);
setShowCreateCredential(true);
}
};
const { data: credentials } = useSWR<Credential<ConfluenceCredentialJson>[]>(
@@ -150,9 +156,6 @@ export default function CredentialSection({
title="Update Credentials"
>
<ModifyCredential
showCreate={() => {
setShowCreateCredential(true);
}}
close={closeModifyCredential}
source={sourceType}
attachedConnector={ccPair.connector}

View File

@@ -144,15 +144,12 @@ export default function ModifyCredential({
attachedConnector,
credentials,
editableCredentials,
source,
defaultedCredential,
onSwap,
onSwitch,
onCreateNew = () => null,
onEditCredential,
onDeleteCredential,
showCreate,
onCreateNew,
}: {
close?: () => void;
showIfEmpty?: boolean;
@@ -161,13 +158,11 @@ export default function ModifyCredential({
credentials: Credential<any>[];
editableCredentials: Credential<any>[];
source: ValidSources;
onSwitch?: (newCredential: Credential<any>) => void;
onSwap?: (newCredential: Credential<any>, connectorId: number) => void;
onCreateNew?: () => void;
onDeleteCredential: (credential: Credential<any | null>) => void;
onEditCredential?: (credential: Credential<ConfluenceCredentialJson>) => void;
showCreate?: () => void;
}) {
const [selectedCredential, setSelectedCredential] =
useState<Credential<any> | null>(null);
@@ -244,10 +239,10 @@ export default function ModifyCredential({
{!showIfEmpty && (
<div className="flex mt-8 justify-between">
{showCreate ? (
{onCreateNew ? (
<Button
onClick={() => {
showCreate();
onCreateNew();
}}
className="bg-neutral-500 disabled:border-transparent
transition-colors duration-150 ease-in disabled:bg-neutral-300

View File

@@ -62,6 +62,7 @@ import document360Icon from "../../../public/Document360.png";
import googleSitesIcon from "../../../public/GoogleSites.png";
import zendeskIcon from "../../../public/Zendesk.svg";
import dropboxIcon from "../../../public/Dropbox.png";
import egnyteIcon from "../../../public/Egnyte.png";
import slackIcon from "../../../public/Slack.png";
import s3Icon from "../../../public/S3.png";
@@ -2725,3 +2726,17 @@ export const UserIcon = ({
</svg>
);
};
export const EgnyteIcon = ({
size = 16,
className = defaultTailwindCSS,
}: IconProps) => {
return (
<div
style={{ width: `${size}px`, height: `${size}px` }}
className={`w-[${size}px] h-[${size}px] ` + className}
>
<Image src={egnyteIcon} alt="Egnyte" width="96" height="96" />
</div>
);
};

View File

@@ -1050,6 +1050,21 @@ For example, specifying .*-support.* as a "channel" will cause the connector to
values: [],
advanced_values: [],
},
egnyte: {
description: "Configure Egnyte connector",
values: [
{
type: "text",
query: "Enter folder path to index:",
label: "Folder Path",
name: "folder_path",
optional: true,
description:
"The folder path to index (e.g., '/Shared/Documents'). Leave empty to index everything.",
},
],
advanced_values: [],
},
};
export function createConnectorInitialValues(
connector: ConfigurableSources

View File

@@ -195,6 +195,11 @@ export interface FirefliesCredentialJson {
export interface MediaWikiCredentialJson {}
export interface WikipediaCredentialJson extends MediaWikiCredentialJson {}
export interface EgnyteCredentialJson {
domain: string;
access_token: string;
}
export const credentialTemplates: Record<ValidSources, any> = {
github: { github_access_token: "" } as GithubCredentialJson,
gitlab: {
@@ -298,6 +303,10 @@ export const credentialTemplates: Record<ValidSources, any> = {
fireflies: {
fireflies_api_key: "",
} as FirefliesCredentialJson,
egnyte: {
domain: "",
access_token: "",
} as EgnyteCredentialJson,
xenforo: null,
google_sites: null,
file: null,

View File

@@ -0,0 +1,19 @@
import { ValidSources } from "../types";
export async function getConnectorOauthRedirectUrl(
connector: ValidSources
): Promise<string | null> {
const response = await fetch(
`/api/connector/oauth/authorize/${connector}?desired_return_url=${encodeURIComponent(
window.location.href
)}`
);
if (!response.ok) {
console.error(`Failed to fetch OAuth redirect URL for ${connector}`);
return null;
}
const data = await response.json();
return data.redirect_url as string;
}

View File

@@ -38,6 +38,7 @@ import {
XenforoIcon,
FreshdeskIcon,
FirefliesIcon,
EgnyteIcon,
} from "@/components/icons/icons";
import { ValidSources } from "./types";
import {
@@ -304,6 +305,12 @@ export const SOURCE_METADATA_MAP: SourceMap = {
displayName: "Not Applicable",
category: SourceCategory.Other,
},
egnyte: {
icon: EgnyteIcon,
displayName: "Egnyte",
category: SourceCategory.Storage,
docs: "https://docs.danswer.dev/connectors/egnyte",
},
} as SourceMap;
function fillSourceMetadata(

View File

@@ -309,6 +309,7 @@ export enum ValidSources {
IngestionApi = "ingestion_api",
Freshdesk = "freshdesk",
Fireflies = "fireflies",
Egnyte = "egnyte",
}
export const validAutoSyncSources = [