mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-04 22:02:16 +02:00
feat: add Notion OAuth integration and connector routes
- Introduced Notion OAuth support with new environment variables for client ID, client secret, and redirect URI. - Implemented Notion connector routes for OAuth flow, including authorization and callback handling. - Updated existing components to accommodate Notion integration, including validation changes and connector configuration. - Enhanced the Notion indexer to utilize OAuth access tokens instead of integration tokens. - Adjusted UI components to reflect the new Notion connector without requiring special configuration.
This commit is contained in:
parent
2b01120c2b
commit
c5b184d475
14 changed files with 333 additions and 76 deletions
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Info, Webhook } from "lucide-react";
|
||||
import { Webhook } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { useRef } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import { JiraConnectForm } from "./components/jira-connect-form";
|
|||
import { LinearConnectForm } from "./components/linear-connect-form";
|
||||
import { LinkupApiConnectForm } from "./components/linkup-api-connect-form";
|
||||
import { LumaConnectForm } from "./components/luma-connect-form";
|
||||
import { NotionConnectForm } from "./components/notion-connect-form";
|
||||
import { SearxngConnectForm } from "./components/searxng-connect-form";
|
||||
import { SlackConnectForm } from "./components/slack-connect-form";
|
||||
import { TavilyApiConnectForm } from "./components/tavily-api-connect-form";
|
||||
|
|
@ -59,8 +58,6 @@ export function getConnectFormComponent(connectorType: string): ConnectFormCompo
|
|||
return SlackConnectForm;
|
||||
case "DISCORD_CONNECTOR":
|
||||
return DiscordConnectForm;
|
||||
case "NOTION_CONNECTOR":
|
||||
return NotionConnectForm;
|
||||
case "CONFLUENCE_CONNECTOR":
|
||||
return ConfluenceConnectForm;
|
||||
case "BOOKSTACK_CONNECTOR":
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { JiraConfig } from "./components/jira-config";
|
|||
import { LinearConfig } from "./components/linear-config";
|
||||
import { LinkupApiConfig } from "./components/linkup-api-config";
|
||||
import { LumaConfig } from "./components/luma-config";
|
||||
import { NotionConfig } from "./components/notion-config";
|
||||
import { SearxngConfig } from "./components/searxng-config";
|
||||
import { SlackConfig } from "./components/slack-config";
|
||||
import { TavilyApiConfig } from "./components/tavily-api-config";
|
||||
|
|
@ -56,8 +55,6 @@ export function getConnectorConfigComponent(
|
|||
return SlackConfig;
|
||||
case "DISCORD_CONNECTOR":
|
||||
return DiscordConfig;
|
||||
case "NOTION_CONNECTOR":
|
||||
return NotionConfig;
|
||||
case "CONFLUENCE_CONNECTOR":
|
||||
return ConfluenceConfig;
|
||||
case "BOOKSTACK_CONNECTOR":
|
||||
|
|
@ -72,7 +69,7 @@ export function getConnectorConfigComponent(
|
|||
return LumaConfig;
|
||||
case "CIRCLEBACK_CONNECTOR":
|
||||
return CirclebackConfig;
|
||||
// OAuth connectors (Gmail, Calendar, Airtable) and others don't need special config UI
|
||||
// OAuth connectors (Gmail, Calendar, Airtable, Notion) and others don't need special config UI
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ export const ConnectorConnectView: FC<ConnectorConnectViewProps> = ({
|
|||
ELASTICSEARCH_CONNECTOR: "elasticsearch-connect-form",
|
||||
SLACK_CONNECTOR: "slack-connect-form",
|
||||
DISCORD_CONNECTOR: "discord-connect-form",
|
||||
NOTION_CONNECTOR: "notion-connect-form",
|
||||
CONFLUENCE_CONNECTOR: "confluence-connect-form",
|
||||
BOOKSTACK_CONNECTOR: "bookstack-connect-form",
|
||||
GITHUB_CONNECTOR: "github-connect-form",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [hasMoreContent, setHasMoreContent] = useState(false);
|
||||
const [showDisconnectConfirm, setShowDisconnectConfirm] = useState(false);
|
||||
const [isQuickIndexing, setIsQuickIndexing] = useState(false);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const checkScrollState = useCallback(() => {
|
||||
|
|
@ -94,6 +95,13 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
};
|
||||
}, [checkScrollState]);
|
||||
|
||||
// Reset local quick indexing state when indexing completes
|
||||
useEffect(() => {
|
||||
if (!isIndexing) {
|
||||
setIsQuickIndexing(false);
|
||||
}
|
||||
}, [isIndexing]);
|
||||
|
||||
const handleDisconnectClick = () => {
|
||||
setShowDisconnectConfirm(true);
|
||||
};
|
||||
|
|
@ -107,6 +115,13 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
setShowDisconnectConfirm(false);
|
||||
};
|
||||
|
||||
const handleQuickIndex = useCallback(() => {
|
||||
if (onQuickIndex) {
|
||||
setIsQuickIndexing(true);
|
||||
onQuickIndex();
|
||||
}
|
||||
}, [onQuickIndex]);
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col min-h-0 overflow-hidden">
|
||||
{/* Fixed Header */}
|
||||
|
|
@ -146,11 +161,11 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={onQuickIndex}
|
||||
disabled={isIndexing || isSaving || isDisconnecting}
|
||||
onClick={handleQuickIndex}
|
||||
disabled={isQuickIndexing || isIndexing || isSaving || isDisconnecting}
|
||||
className="text-xs sm:text-sm bg-slate-400/10 dark:bg-white/10 hover:bg-slate-400/20 dark:hover:bg-white/20 border-slate-400/20 dark:border-white/20 w-full sm:w-auto"
|
||||
>
|
||||
{isIndexing ? (
|
||||
{isQuickIndexing || isIndexing ? (
|
||||
<>
|
||||
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
||||
Indexing...
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
|
||||
import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
|
@ -43,6 +44,9 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
|
|||
onStartIndexing,
|
||||
onSkip,
|
||||
}) => {
|
||||
const searchParams = useSearchParams();
|
||||
const isFromOAuth = searchParams.get("view") === "configure";
|
||||
|
||||
// Get connector-specific config component
|
||||
const ConnectorConfigComponent = useMemo(
|
||||
() => (connector ? getConnectorConfigComponent(connector.connector_type) : null),
|
||||
|
|
@ -94,15 +98,17 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
|
|||
isScrolled && "shadow-sm"
|
||||
)}
|
||||
>
|
||||
{/* Back button */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSkip}
|
||||
className="flex items-center gap-2 text-xs sm:text-sm text-muted-foreground hover:text-foreground mb-6 w-fit"
|
||||
>
|
||||
<ArrowLeft className="size-4" />
|
||||
Back to connectors
|
||||
</button>
|
||||
{/* Back button - only show if not from OAuth */}
|
||||
{!isFromOAuth && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSkip}
|
||||
className="flex items-center gap-2 text-xs sm:text-sm text-muted-foreground hover:text-foreground mb-6 w-fit"
|
||||
>
|
||||
<ArrowLeft className="size-4" />
|
||||
Back to connectors
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Success header */}
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
|
|
@ -187,15 +193,7 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
|
|||
</div>
|
||||
|
||||
{/* Fixed Footer - Action buttons */}
|
||||
<div className="flex-shrink-0 flex items-center justify-between px-6 sm:px-12 py-6 bg-muted">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={onSkip}
|
||||
disabled={isStartingIndexing}
|
||||
className="text-xs sm:text-sm"
|
||||
>
|
||||
Skip for now
|
||||
</Button>
|
||||
<div className="flex-shrink-0 flex items-center justify-end px-6 sm:px-12 py-6 bg-muted">
|
||||
<Button
|
||||
onClick={onStartIndexing}
|
||||
disabled={isStartingIndexing}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@ export const OAUTH_CONNECTORS = [
|
|||
connectorType: EnumConnectorName.AIRTABLE_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/airtable/connector/add/",
|
||||
},
|
||||
{
|
||||
id: "notion-connector",
|
||||
title: "Notion",
|
||||
description: "Search your Notion pages",
|
||||
connectorType: EnumConnectorName.NOTION_CONNECTOR,
|
||||
authEndpoint: "/api/v1/auth/notion/connector/add/",
|
||||
},
|
||||
] as const;
|
||||
|
||||
// Content Sources (tools that extract and import content from external sources)
|
||||
|
|
@ -62,12 +69,6 @@ export const OTHER_CONNECTORS = [
|
|||
description: "Search Discord messages",
|
||||
connectorType: EnumConnectorName.DISCORD_CONNECTOR,
|
||||
},
|
||||
{
|
||||
id: "notion-connector",
|
||||
title: "Notion",
|
||||
description: "Search Notion pages",
|
||||
connectorType: EnumConnectorName.NOTION_CONNECTOR,
|
||||
},
|
||||
{
|
||||
id: "confluence-connector",
|
||||
title: "Confluence",
|
||||
|
|
@ -143,7 +144,7 @@ export const OTHER_CONNECTORS = [
|
|||
{
|
||||
id: "circleback-connector",
|
||||
title: "Circleback",
|
||||
description: "Receive meeting notes via webhook",
|
||||
description: "Receive meeting notes, transcripts",
|
||||
connectorType: EnumConnectorName.CIRCLEBACK_CONNECTOR,
|
||||
},
|
||||
] as const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue