mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-07-13 22:53:01 +02:00
Add confluence connector page
This commit is contained in:
@ -6,7 +6,7 @@ const nextConfig = {
|
|||||||
output: "standalone",
|
output: "standalone",
|
||||||
redirects: async () => {
|
redirects: async () => {
|
||||||
// In production, something else (nginx in the one box setup) should take
|
// In production, something else (nginx in the one box setup) should take
|
||||||
// care of this redirect. TODO (chris): better support setups where
|
// care of this redirect. TODO (chris): better support setups where
|
||||||
// web_server and api_server are on different machines.
|
// web_server and api_server are on different machines.
|
||||||
if (process.env.NODE_ENV === "production") return [];
|
if (process.env.NODE_ENV === "production") return [];
|
||||||
|
|
||||||
|
9
web/package-lock.json
generated
9
web/package-lock.json
generated
@ -20,6 +20,7 @@
|
|||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.23",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-icons": "^4.8.0",
|
||||||
"swr": "^2.1.5",
|
"swr": "^2.1.5",
|
||||||
"tailwindcss": "^3.3.1",
|
"tailwindcss": "^3.3.1",
|
||||||
"typescript": "5.0.3",
|
"typescript": "5.0.3",
|
||||||
@ -3256,6 +3257,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||||
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/react-icons": {
|
||||||
|
"version": "4.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz",
|
||||||
|
"integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"postcss": "^8.4.23",
|
"postcss": "^8.4.23",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-icons": "^4.8.0",
|
||||||
"swr": "^2.1.5",
|
"swr": "^2.1.5",
|
||||||
"tailwindcss": "^3.3.1",
|
"tailwindcss": "^3.3.1",
|
||||||
"typescript": "5.0.3",
|
"typescript": "5.0.3",
|
||||||
|
62
web/src/app/admin/connectors/confluence/page.tsx
Normal file
62
web/src/app/admin/connectors/confluence/page.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import { IndexForm } from "@/components/admin/connectors/Form";
|
||||||
|
import { ConfluenceIcon } from "@/components/icons/icons";
|
||||||
|
import { TextFormField } from "@/components/admin/connectors/Field";
|
||||||
|
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto">
|
||||||
|
<div className="mb-4">
|
||||||
|
<HealthCheckBanner />
|
||||||
|
</div>
|
||||||
|
<div className="border-solid border-gray-600 border-b mb-4 pb-2 flex">
|
||||||
|
<ConfluenceIcon size="32" />
|
||||||
|
<h1 className="text-3xl font-bold pl-2">Confluence</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* TODO: make this periodic */}
|
||||||
|
<h2 className="text-xl font-bold mb-2 mt-6 ml-auto mr-auto">
|
||||||
|
Request Indexing
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm mb-4">
|
||||||
|
To use the Confluence connector, you must first follow the guide
|
||||||
|
described{" "}
|
||||||
|
<a
|
||||||
|
className="text-blue-500"
|
||||||
|
href="https://docs.danswer.dev/connectors/slack#setting-up"
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</a>{" "}
|
||||||
|
to give the Danswer backend read access to your documents. Once that is
|
||||||
|
setup, specify any link to a Confluence page below and click
|
||||||
|
"Index" to Index. Based on the provided link, we will index
|
||||||
|
the ENTIRE SPACE, not just the specified page. For example, entering{" "}
|
||||||
|
<i>https://danswer.atlassian.net/wiki/spaces/SD/overview</i> and
|
||||||
|
clicking the Index button will index the whole <i>SD</i> Confluence
|
||||||
|
space.
|
||||||
|
</p>
|
||||||
|
<div className="border-solid border-gray-600 border rounded-md p-6">
|
||||||
|
<IndexForm
|
||||||
|
source="confluence"
|
||||||
|
formBody={
|
||||||
|
<>
|
||||||
|
<TextFormField name="wiki_page_url" label="Confluence URL:" />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
validationSchema={Yup.object().shape({
|
||||||
|
wiki_page_url: Yup.string().required(
|
||||||
|
"Please enter any link to your confluence e.g. https://danswer.atlassian.net/wiki/spaces/SD/overview"
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
initialValues={{
|
||||||
|
wiki_page_url: "",
|
||||||
|
}}
|
||||||
|
onSubmit={(isSuccess) => console.log(isSuccess)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -7,6 +7,7 @@ import {
|
|||||||
GoogleDriveIcon,
|
GoogleDriveIcon,
|
||||||
SlackIcon,
|
SlackIcon,
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
|
ConfluenceIcon,
|
||||||
} from "@/components/icons/icons";
|
} from "@/components/icons/icons";
|
||||||
import { DISABLE_AUTH } from "@/lib/constants";
|
import { DISABLE_AUTH } from "@/lib/constants";
|
||||||
import { getCurrentUserSS } from "@/lib/userSS";
|
import { getCurrentUserSS } from "@/lib/userSS";
|
||||||
@ -41,7 +42,7 @@ export default async function AdminLayout({
|
|||||||
{
|
{
|
||||||
name: (
|
name: (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<NotebookIcon size="16" />
|
<NotebookIcon size="18" />
|
||||||
<div className="ml-1">Status</div>
|
<div className="ml-1">Status</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@ -88,6 +89,15 @@ export default async function AdminLayout({
|
|||||||
),
|
),
|
||||||
link: "/admin/connectors/google-drive",
|
link: "/admin/connectors/google-drive",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: (
|
||||||
|
<div className="flex">
|
||||||
|
<ConfluenceIcon size="16" />
|
||||||
|
<div className="ml-1">Confluence</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
link: "/admin/connectors/confluence",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -96,7 +106,7 @@ export default async function AdminLayout({
|
|||||||
{
|
{
|
||||||
name: (
|
name: (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<KeyIcon size="16" />
|
<KeyIcon size="18" />
|
||||||
<div className="ml-1">OpenAI</div>
|
<div className="ml-1">OpenAI</div>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
@ -3,24 +3,27 @@ import { ErrorMessage, Field } from "formik";
|
|||||||
interface TextFormFieldProps {
|
interface TextFormFieldProps {
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
subtext?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TextFormField = ({
|
export const TextFormField = ({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
|
subtext,
|
||||||
type = "text",
|
type = "text",
|
||||||
}: TextFormFieldProps) => {
|
}: TextFormFieldProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label htmlFor={name} className="block mb-1">
|
<label htmlFor={name} className="block">
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
|
{subtext && <p className="text-xs">{subtext}</p>}
|
||||||
<Field
|
<Field
|
||||||
type={type}
|
type={type}
|
||||||
name={name}
|
name={name}
|
||||||
id={name}
|
id={name}
|
||||||
className="border bg-slate-700 text-gray-200 border-gray-300 rounded w-full py-2 px-3"
|
className="border bg-slate-700 text-gray-200 border-gray-300 rounded w-full py-2 px-3 mt-1"
|
||||||
/>
|
/>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
name={name}
|
name={name}
|
||||||
|
@ -18,7 +18,10 @@ export const submitIndexRequest = async (
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ connector_specific_config: values, input_type: inputType }),
|
body: JSON.stringify({
|
||||||
|
connector_specific_config: values,
|
||||||
|
input_type: inputType,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ValidSources } from "@/lib/types";
|
import { Notebook, Key, Trash, Info } from "@phosphor-icons/react";
|
||||||
import {
|
import { SiConfluence, SiGithub, SiGoogledrive, SiSlack } from "react-icons/si";
|
||||||
Globe,
|
import { FaGlobe } from "react-icons/fa";
|
||||||
SlackLogo,
|
|
||||||
GithubLogo,
|
|
||||||
GoogleDriveLogo,
|
|
||||||
Notebook,
|
|
||||||
Key,
|
|
||||||
Trash,
|
|
||||||
Info,
|
|
||||||
} from "@phosphor-icons/react";
|
|
||||||
|
|
||||||
interface IconProps {
|
interface IconProps {
|
||||||
size?: string;
|
size?: string;
|
||||||
@ -44,28 +36,35 @@ export const GlobeIcon = ({
|
|||||||
size = "16",
|
size = "16",
|
||||||
className = defaultTailwindCSS,
|
className = defaultTailwindCSS,
|
||||||
}: IconProps) => {
|
}: IconProps) => {
|
||||||
return <Globe size={size} className={className} />;
|
return <FaGlobe size={size} className={className} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SlackIcon = ({
|
export const SlackIcon = ({
|
||||||
size = "16",
|
size = "16",
|
||||||
className = defaultTailwindCSS,
|
className = defaultTailwindCSS,
|
||||||
}: IconProps) => {
|
}: IconProps) => {
|
||||||
return <SlackLogo size={size} className={className} />;
|
return <SiSlack size={size} className={className} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GithubIcon = ({
|
export const GithubIcon = ({
|
||||||
size = "16",
|
size = "16",
|
||||||
className = defaultTailwindCSS,
|
className = defaultTailwindCSS,
|
||||||
}: IconProps) => {
|
}: IconProps) => {
|
||||||
return <GithubLogo size={size} className={className} />;
|
return <SiGithub size={size} className={className} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GoogleDriveIcon = ({
|
export const GoogleDriveIcon = ({
|
||||||
size = "16",
|
size = "16",
|
||||||
className = defaultTailwindCSS,
|
className = defaultTailwindCSS,
|
||||||
}: IconProps) => {
|
}: IconProps) => {
|
||||||
return <GoogleDriveLogo size={size} className={className} />;
|
return <SiGoogledrive size={size} className={className} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ConfluenceIcon = ({
|
||||||
|
size = "16",
|
||||||
|
className = defaultTailwindCSS,
|
||||||
|
}: IconProps) => {
|
||||||
|
return <SiConfluence size={size} className={className} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InfoIcon = ({
|
export const InfoIcon = ({
|
||||||
|
@ -82,13 +82,13 @@ export const SearchResultsDisplay: React.FC<SearchResultsDisplayProps> = ({
|
|||||||
{dedupedQuotes.map((quoteInfo) => (
|
{dedupedQuotes.map((quoteInfo) => (
|
||||||
<a
|
<a
|
||||||
key={quoteInfo.document_id}
|
key={quoteInfo.document_id}
|
||||||
className="p-2 ml-1 border border-gray-800 rounded-lg text-sm flex max-w-[230px] hover:bg-gray-800"
|
className="p-2 ml-1 border border-gray-800 rounded-lg text-sm flex max-w-[280px] hover:bg-gray-800"
|
||||||
href={quoteInfo.link}
|
href={quoteInfo.link}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{getSourceIcon(quoteInfo.source_type, "20")}
|
{getSourceIcon(quoteInfo.source_type, "20")}
|
||||||
<p className="truncate break-all ml-0.5">
|
<p className="truncate break-all ml-2">
|
||||||
{quoteInfo.semantic_identifier || quoteInfo.document_id}
|
{quoteInfo.semantic_identifier || quoteInfo.document_id}
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
@ -132,7 +132,7 @@ export const SearchResultsDisplay: React.FC<SearchResultsDisplayProps> = ({
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{getSourceIcon(doc.source_type, "20")}
|
{getSourceIcon(doc.source_type, "20")}
|
||||||
<p className="truncate break-all ml-0.5">
|
<p className="truncate break-all ml-2">
|
||||||
{doc.semantic_identifier || doc.document_id}
|
{doc.semantic_identifier || doc.document_id}
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ValidSources } from "@/lib/types";
|
import { ValidSources } from "@/lib/types";
|
||||||
import {
|
import {
|
||||||
|
ConfluenceIcon,
|
||||||
GithubIcon,
|
GithubIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
GoogleDriveIcon,
|
GoogleDriveIcon,
|
||||||
@ -38,6 +39,12 @@ export const getSourceMetadata = (sourceType: ValidSources): SourceMetadata => {
|
|||||||
displayName: "Github PRs",
|
displayName: "Github PRs",
|
||||||
adminPageLink: "/admin/connectors/github",
|
adminPageLink: "/admin/connectors/github",
|
||||||
};
|
};
|
||||||
|
case "confluence":
|
||||||
|
return {
|
||||||
|
icon: ConfluenceIcon,
|
||||||
|
displayName: "Confluence",
|
||||||
|
adminPageLink: "/admin/connectors/confluence",
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
throw new Error("Invalid source type");
|
throw new Error("Invalid source type");
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,10 @@ export interface User {
|
|||||||
role: "basic" | "admin";
|
role: "basic" | "admin";
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ValidSources = "web" | "github" | "slack" | "google_drive";
|
export type ValidSources =
|
||||||
|
| "web"
|
||||||
|
| "github"
|
||||||
|
| "slack"
|
||||||
|
| "google_drive"
|
||||||
|
| "confluence";
|
||||||
export type ValidInputTypes = "load_state" | "poll" | "event";
|
export type ValidInputTypes = "load_state" | "poll" | "event";
|
||||||
|
Reference in New Issue
Block a user