EE Connector Deletion Bugfix + Refactor (#2042)

---------

Co-authored-by: Weves <chrisweaver101@gmail.com>
This commit is contained in:
Nathan Schwerdfeger
2024-08-11 20:33:07 -07:00
committed by GitHub
parent 79523f2e0a
commit c7e5b11c63
49 changed files with 998 additions and 800 deletions

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@tremor/react";
import { CCPairFullInfo } from "./types";
import { CCPairFullInfo, ConnectorCredentialPairStatus } from "./types";
import { usePopup } from "@/components/admin/connectors/Popup";
import { FiTrash } from "react-icons/fi";
import { deleteCCPair } from "@/lib/documentDeletion";
@@ -16,7 +16,7 @@ export function DeletionButton({ ccPair }: { ccPair: CCPairFullInfo }) {
ccPair?.latest_deletion_attempt?.status === "STARTED";
let tooltip: string;
if (ccPair.connector.disabled) {
if (ccPair.status !== ConnectorCredentialPairStatus.ACTIVE) {
if (isDeleting) {
tooltip = "This connector is currently being deleted";
} else {
@@ -41,7 +41,9 @@ export function DeletionButton({ ccPair }: { ccPair: CCPairFullInfo }) {
)
}
icon={FiTrash}
disabled={!ccPair.connector.disabled || isDeleting}
disabled={
ccPair.status === ConnectorCredentialPairStatus.ACTIVE || isDeleting
}
tooltip={tooltip}
>
Delete

View File

@@ -1,11 +1,11 @@
"use client";
import { Button } from "@tremor/react";
import { CCPairFullInfo } from "./types";
import { CCPairFullInfo, ConnectorCredentialPairStatus } from "./types";
import { usePopup } from "@/components/admin/connectors/Popup";
import { disableConnector } from "@/lib/connector";
import { mutate } from "swr";
import { buildCCPairInfoUrl } from "./lib";
import { setCCPairStatus } from "@/lib/ccPair";
export function ModifyStatusButtonCluster({
ccPair,
@@ -17,13 +17,16 @@ export function ModifyStatusButtonCluster({
return (
<>
{popup}
{ccPair.connector.disabled ? (
{ccPair.status === ConnectorCredentialPairStatus.PAUSED ? (
<Button
color="green"
size="xs"
onClick={() =>
disableConnector(ccPair.connector, setPopup, () =>
mutate(buildCCPairInfoUrl(ccPair.id))
setCCPairStatus(
ccPair.id,
ConnectorCredentialPairStatus.ACTIVE,
setPopup,
() => mutate(buildCCPairInfoUrl(ccPair.id))
)
}
tooltip="Click to start indexing again!"
@@ -35,8 +38,11 @@ export function ModifyStatusButtonCluster({
color="red"
size="xs"
onClick={() =>
disableConnector(ccPair.connector, setPopup, () =>
mutate(buildCCPairInfoUrl(ccPair.id))
setCCPairStatus(
ccPair.id,
ConnectorCredentialPairStatus.PAUSED,
setPopup,
() => mutate(buildCCPairInfoUrl(ccPair.id))
)
}
tooltip={

View File

@@ -1,6 +1,6 @@
"use client";
import { CCPairFullInfo } from "./types";
import { CCPairFullInfo, ConnectorCredentialPairStatus } from "./types";
import { HealthCheckBanner } from "@/components/health/healthcheck";
import { CCPairStatus } from "@/components/Status";
import { BackButton } from "@/components/BackButton";
@@ -92,7 +92,7 @@ function Main({ ccPairId }: { ccPairId: number }) {
}
const lastIndexAttempt = ccPair.index_attempts[0];
const isDeleting = isCurrentlyDeleting(ccPair.latest_deletion_attempt);
const isDeleting = ccPair.status === ConnectorCredentialPairStatus.DELETING;
// figure out if we need to artificially deflate the number of docs indexed.
// This is required since the total number of docs indexed by a CC Pair is
@@ -112,9 +112,6 @@ function Main({ ccPairId }: { ccPairId: number }) {
setEditableName(ccPair.name);
setIsEditing(true);
};
const deleting =
ccPair.latest_deletion_attempt?.status == "PENDING" ||
ccPair.latest_deletion_attempt?.status == "STARTED";
const resetEditing = () => {
setIsEditing(false);
@@ -169,16 +166,18 @@ function Main({ ccPairId }: { ccPairId: number }) {
ccPairId={ccPair.id}
connectorId={ccPair.connector.id}
credentialId={ccPair.credential.id}
isDisabled={ccPair.connector.disabled}
isDisabled={
ccPair.status === ConnectorCredentialPairStatus.PAUSED
}
isDeleting={isDeleting}
/>
)}
{!deleting && <ModifyStatusButtonCluster ccPair={ccPair} />}
{!isDeleting && <ModifyStatusButtonCluster ccPair={ccPair} />}
</div>
</div>
<CCPairStatus
status={lastIndexAttempt?.status || "not_started"}
disabled={ccPair.connector.disabled}
disabled={ccPair.status === ConnectorCredentialPairStatus.PAUSED}
isDeleting={isDeleting}
/>
<div className="text-sm mt-1">

View File

@@ -2,9 +2,16 @@ import { Connector } from "@/lib/connectors/connectors";
import { Credential } from "@/lib/connectors/credentials";
import { DeletionAttemptSnapshot, IndexAttemptSnapshot } from "@/lib/types";
export enum ConnectorCredentialPairStatus {
ACTIVE = "ACTIVE",
PAUSED = "PAUSED",
DELETING = "DELETING",
}
export interface CCPairFullInfo {
id: number;
name: string;
status: ConnectorCredentialPairStatus;
num_docs_indexed: number;
connector: Connector<any>;
credential: Credential<any>;

View File

@@ -189,7 +189,6 @@ export default function AddConnector({
refresh_freq: refreshFreq * 60 || null,
prune_freq: pruneFreq * 60 || null,
indexing_start: indexingStart,
disabled: false,
},
undefined,
credentialActivated ? false : true,

View File

@@ -43,7 +43,6 @@ export const submitFiles = async (
refresh_freq: null,
prune_freq: null,
indexing_start: null,
disabled: false,
});
if (connectorErrorMsg || !connector) {
setPopup({

View File

@@ -44,7 +44,6 @@ export const submitGoogleSite = async (
refresh_freq: advancedConfig.refreshFreq,
prune_freq: advancedConfig.pruneFreq,
indexing_start: advancedConfig.indexingStart,
disabled: false,
});
if (connectorErrorMsg || !connector) {
setPopup({

View File

@@ -32,6 +32,7 @@ import { Warning } from "@phosphor-icons/react";
import Cookies from "js-cookie";
import { TOGGLED_CONNECTORS_COOKIE_NAME } from "@/lib/constants";
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
import { ConnectorCredentialPairStatus } from "../../connector/[ccPairId]/types";
const columnWidths = {
first: "20%",
@@ -146,20 +147,25 @@ function ConnectorRow({
};
const getActivityBadge = () => {
if (ccPairsIndexingStatus.connector.disabled) {
if (ccPairsIndexingStatus.deletion_attempt) {
return (
<Badge
color="red"
className="w-fit px-2 py-1 rounded-full border border-red-500"
>
<div className="flex text-xs items-center gap-x-1">
<div className="w-3 h-3 rounded-full bg-red-500"></div>
Deleting
</div>
</Badge>
);
}
if (
ccPairsIndexingStatus.cc_pair_status ===
ConnectorCredentialPairStatus.DELETING
) {
return (
<Badge
color="red"
className="w-fit px-2 py-1 rounded-full border border-red-500"
>
<div className="flex text-xs items-center gap-x-1">
<div className="w-3 h-3 rounded-full bg-red-500"></div>
Deleting
</div>
</Badge>
);
} else if (
ccPairsIndexingStatus.cc_pair_status ===
ConnectorCredentialPairStatus.PAUSED
) {
return (
<Badge
color="yellow"
@@ -172,6 +178,8 @@ function ConnectorRow({
</Badge>
);
}
// ACTIVE case
switch (ccPairsIndexingStatus.last_status) {
case "in_progress":
return (
@@ -302,7 +310,10 @@ export function CCPairIndexingStatusTable({
const statuses = grouped[source];
summaries[source] = {
count: statuses.length,
active: statuses.filter((status) => !status.connector.disabled).length,
active: statuses.filter(
(status) =>
status.cc_pair_status === ConnectorCredentialPairStatus.ACTIVE
).length,
public: statuses.filter((status) => status.public_doc).length,
totalDocsIndexed: statuses.reduce(
(sum, status) => sum + status.docs_indexed,
@@ -362,6 +373,7 @@ export function CCPairIndexingStatusTable({
ccPairsIndexingStatus={{
cc_pair_id: 1,
name: "Sample File Connector",
cc_pair_status: ConnectorCredentialPairStatus.ACTIVE,
last_status: "success",
connector: {
name: "Sample File Connector",
@@ -373,7 +385,6 @@ export function CCPairIndexingStatusTable({
refresh_freq: 86400,
prune_freq: null,
indexing_start: new Date("2023-07-01T12:00:00Z"),
disabled: false,
id: 1,
credential_ids: [],
time_created: "2023-07-01T12:00:00Z",

View File

@@ -194,7 +194,6 @@ export function ConnectorForm<T extends Yup.AnyObject>({
connector_specific_config: connectorConfig,
refresh_freq: refreshFreq || 0,
prune_freq: pruneFreq ?? null,
disabled: false,
indexing_start: indexingStart || null,
});
@@ -343,7 +342,6 @@ export function UpdateConnectorForm<T extends Yup.AnyObject>({
connector_specific_config: values,
refresh_freq: existingConnector.refresh_freq,
prune_freq: existingConnector.prune_freq,
disabled: false,
indexing_start: existingConnector.indexing_start,
},
existingConnector.id

View File

@@ -1,251 +0,0 @@
import { ConnectorIndexingStatus } from "@/lib/types";
import { PopupSpec, usePopup } from "@/components/admin/connectors/Popup";
import { useState } from "react";
import { LinkBreakIcon, LinkIcon } from "@/components/icons/icons";
import { disableConnector } from "@/lib/connector";
import { AttachCredentialButtonForTable } from "@/components/admin/connectors/buttons/AttachCredentialButtonForTable";
import { DeleteColumn } from "./DeleteColumn";
import {
Table,
TableHead,
TableRow,
TableHeaderCell,
TableBody,
TableCell,
} from "@tremor/react";
import { FiCheck, FiXCircle } from "react-icons/fi";
import { Credential } from "@/lib/connectors/credentials";
interface StatusRowProps<ConnectorConfigType, ConnectorCredentialType> {
connectorIndexingStatus: ConnectorIndexingStatus<
ConnectorConfigType,
ConnectorCredentialType
>;
hasCredentialsIssue: boolean;
setPopup: (popupSpec: PopupSpec | null) => void;
onUpdate: () => void;
}
export function StatusRow<ConnectorConfigType, ConnectorCredentialType>({
connectorIndexingStatus,
hasCredentialsIssue,
setPopup,
onUpdate,
}: StatusRowProps<ConnectorConfigType, ConnectorCredentialType>) {
const [statusHovered, setStatusHovered] = useState<boolean>(false);
const connector = connectorIndexingStatus.connector;
let shouldDisplayDisabledToggle = !hasCredentialsIssue;
let statusDisplay;
switch (connectorIndexingStatus.last_status) {
case "failed":
statusDisplay = <div className="text-error">Failed</div>;
break;
default:
statusDisplay = <div className="text-success flex">Enabled!</div>;
}
if (connector.disabled) {
const deletionAttempt = connectorIndexingStatus.deletion_attempt;
if (!deletionAttempt || deletionAttempt.status === "FAILURE") {
statusDisplay = <div className="text-error">Paused</div>;
} else {
statusDisplay = <div className="text-error">Deleting...</div>;
shouldDisplayDisabledToggle = false;
}
}
return (
<div className="flex">
{statusDisplay}
{shouldDisplayDisabledToggle && (
<div
className="cursor-pointer ml-1 my-auto relative"
onMouseEnter={() => setStatusHovered(true)}
onMouseLeave={() => setStatusHovered(false)}
onClick={() => disableConnector(connector, setPopup, onUpdate)}
>
{statusHovered && (
<div className="flex flex-nowrap absolute top-0 left-0 ml-8 bg-background border border-border px-3 py-2 rounded shadow-lg">
{connector.disabled ? "Enable!" : "Pause!"}
</div>
)}
{connector.disabled ? (
<LinkIcon className="my-auto flex flex-shrink-0 text-error" />
) : (
<LinkBreakIcon
className={`my-auto flex flex-shrink-0 ${
connectorIndexingStatus.last_status === "failed"
? "text-error"
: "text-success"
}`}
/>
)}
</div>
)}
</div>
);
}
export interface ColumnSpecification<
ConnectorConfigType,
ConnectorCredentialType,
> {
header: string;
key: string;
getValue: (
ccPairStatus: ConnectorIndexingStatus<
ConnectorConfigType,
ConnectorCredentialType
>
) => JSX.Element | string | undefined;
}
export interface ConnectorsTableProps<
ConnectorConfigType,
ConnectorCredentialType,
> {
connectorIndexingStatuses: ConnectorIndexingStatus<
ConnectorConfigType,
ConnectorCredentialType
>[];
liveCredential?: Credential<ConnectorCredentialType> | null;
getCredential?: (
credential: Credential<ConnectorCredentialType>
) => JSX.Element | string;
onUpdate: () => void;
onCredentialLink?: (connectorId: number) => void;
specialColumns?: ColumnSpecification<
ConnectorConfigType,
ConnectorCredentialType
>[];
includeName?: boolean;
}
export function ConnectorsTable<ConnectorConfigType, ConnectorCredentialType>({
connectorIndexingStatuses,
liveCredential,
getCredential,
specialColumns,
onUpdate,
onCredentialLink,
includeName = false,
}: ConnectorsTableProps<ConnectorConfigType, ConnectorCredentialType>) {
const { popup, setPopup } = usePopup();
const connectorIncludesCredential =
getCredential !== undefined && onCredentialLink !== undefined;
const columns = [
...(includeName ? [{ header: "Name", key: "name" }] : []),
...(specialColumns ?? []),
{
header: "Status",
key: "status",
},
{
header: "Is Public",
key: "is_public",
},
];
if (connectorIncludesCredential) {
columns.push({
header: "Credential",
key: "credential",
});
}
columns.push({
header: "Remove",
key: "remove",
});
return (
<div>
{popup}
<Table className="overflow-visible">
<TableHead>
<TableRow>
{includeName && <TableHeaderCell>Name</TableHeaderCell>}
{specialColumns?.map(({ header }) => (
<TableHeaderCell key={header}>{header}</TableHeaderCell>
))}
<TableHeaderCell>Status</TableHeaderCell>
<TableHeaderCell>Is Public</TableHeaderCell>
{connectorIncludesCredential && (
<TableHeaderCell>Credential</TableHeaderCell>
)}
<TableHeaderCell>Remove</TableHeaderCell>
</TableRow>
</TableHead>
<TableBody>
{connectorIndexingStatuses.map((connectorIndexingStatus) => {
const connector = connectorIndexingStatus.connector;
// const credential = connectorIndexingStatus.credential;
const hasValidCredentials =
liveCredential &&
connector.credential_ids.includes(liveCredential.id);
const credentialDisplay = connectorIncludesCredential ? (
hasValidCredentials ? (
<div className="max-w-sm truncate">
{getCredential(liveCredential)}
</div>
) : liveCredential ? (
<AttachCredentialButtonForTable
onClick={() => onCredentialLink(connector.id)}
/>
) : (
<p className="text-red-700">N/A</p>
)
) : (
"-"
);
return (
<TableRow key={connectorIndexingStatus.cc_pair_id}>
{includeName && (
<TableCell className="whitespace-normal break-all">
<p className="text font-medium">
{connectorIndexingStatus.name}
</p>
</TableCell>
)}
{specialColumns?.map(({ key, getValue }) => (
<TableCell key={key}>
{getValue(connectorIndexingStatus)}
</TableCell>
))}
<TableCell>
<StatusRow
connectorIndexingStatus={connectorIndexingStatus}
hasCredentialsIssue={
!hasValidCredentials && connectorIncludesCredential
}
setPopup={setPopup}
onUpdate={onUpdate}
/>
</TableCell>
<TableCell>
{connectorIndexingStatus.public_doc ? (
<FiCheck className="my-auto text-success" size="18" />
) : (
<FiXCircle className="my-auto text-error" />
)}
</TableCell>
{connectorIncludesCredential && (
<TableCell>{credentialDisplay}</TableCell>
)}
<TableCell>
<DeleteColumn
connectorIndexingStatus={connectorIndexingStatus}
setPopup={setPopup}
onUpdate={onUpdate}
/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
);
}

View File

@@ -1,60 +0,0 @@
import { InfoIcon, TrashIcon } from "@/components/icons/icons";
import {
deleteCCPair,
scheduleDeletionJobForConnector,
} from "@/lib/documentDeletion";
import { ConnectorIndexingStatus } from "@/lib/types";
import { PopupSpec } from "../Popup";
import { useState } from "react";
import { DeleteButton } from "@/components/DeleteButton";
interface Props<ConnectorConfigType, ConnectorCredentialType> {
connectorIndexingStatus: ConnectorIndexingStatus<
ConnectorConfigType,
ConnectorCredentialType
>;
setPopup: (popupSpec: PopupSpec | null) => void;
onUpdate: () => void;
}
export function DeleteColumn<ConnectorConfigType, ConnectorCredentialType>({
connectorIndexingStatus,
setPopup,
onUpdate,
}: Props<ConnectorConfigType, ConnectorCredentialType>) {
const [deleteHovered, setDeleteHovered] = useState<boolean>(false);
const connector = connectorIndexingStatus.connector;
const credential = connectorIndexingStatus.credential;
return (
<div
className="relative"
onMouseEnter={() => setDeleteHovered(true)}
onMouseLeave={() => setDeleteHovered(false)}
>
{connectorIndexingStatus.is_deletable ? (
<div className="cursor-pointer mx-auto flex">
<DeleteButton
onClick={() =>
deleteCCPair(connector.id, credential.id, setPopup, onUpdate)
}
/>
</div>
) : (
<div>
{deleteHovered && (
<div className="w-64 z-30 whitespace-normal flex absolute mt-8 top-0 left-0 bg-background border border-border px-3 py-2 rounded shadow-lg text-xs">
<InfoIcon className="flex flex-shrink-0 mr-2" />
In order to delete a connector it must be disabled and have no
ongoing / planned index jobs.
</div>
)}
<div className="flex mx-auto text-xs">
<DeleteButton disabled />
</div>
</div>
)}
</div>
);
}

View File

@@ -1,174 +0,0 @@
import { DeletionAttemptSnapshot, ValidStatuses } from "@/lib/types";
import { usePopup } from "@/components/admin/connectors/Popup";
import { updateConnector } from "@/lib/connector";
import { AttachCredentialButtonForTable } from "@/components/admin/connectors/buttons/AttachCredentialButtonForTable";
import { scheduleDeletionJobForConnector } from "@/lib/documentDeletion";
import { ConnectorsTableProps } from "./ConnectorsTable";
import {
Table,
TableHead,
TableRow,
TableHeaderCell,
TableBody,
TableCell,
} from "@tremor/react";
import { DeleteButton } from "@/components/DeleteButton";
const SingleUseConnectorStatus = ({
indexingStatus,
deletionAttempt,
}: {
indexingStatus: ValidStatuses | null;
deletionAttempt: DeletionAttemptSnapshot | null;
}) => {
if (
deletionAttempt &&
(deletionAttempt.status === "PENDING" ||
deletionAttempt.status === "STARTED")
) {
return <div className="text-error">Deleting...</div>;
}
if (!indexingStatus || indexingStatus === "not_started") {
return <div>Not Started</div>;
}
if (indexingStatus === "in_progress") {
return <div>In Progress</div>;
}
if (indexingStatus === "success") {
return <div className="text-success">Success!</div>;
}
return <div className="text-error">Failed</div>;
};
export function SingleUseConnectorsTable<
ConnectorConfigType,
ConnectorCredentialType,
>({
connectorIndexingStatuses,
liveCredential,
getCredential,
specialColumns,
onUpdate,
onCredentialLink,
includeName = false,
}: ConnectorsTableProps<ConnectorConfigType, ConnectorCredentialType>) {
const { popup, setPopup } = usePopup();
const connectorIncludesCredential =
getCredential !== undefined && onCredentialLink !== undefined;
return (
<div>
{popup}
<Table className="overflow-visible">
<TableHead>
<TableRow>
{includeName && <TableHeaderCell>Name</TableHeaderCell>}
{specialColumns?.map(({ header }) => (
<TableHeaderCell key={header}>{header}</TableHeaderCell>
))}
<TableHeaderCell>Status</TableHeaderCell>
{connectorIncludesCredential && (
<TableHeaderCell>Credential</TableHeaderCell>
)}
<TableHeaderCell>Remove</TableHeaderCell>
</TableRow>
</TableHead>
<TableBody>
{connectorIndexingStatuses.map((connectorIndexingStatus) => {
const connector = connectorIndexingStatus.connector;
// const credential = connectorIndexingStatus.credential;
const hasValidCredentials =
liveCredential &&
connector.credential_ids.includes(liveCredential.id);
const credentialDisplay = connectorIncludesCredential ? (
hasValidCredentials ? (
<div className="max-w-sm truncate">
{getCredential(liveCredential)}
</div>
) : liveCredential ? (
<AttachCredentialButtonForTable
onClick={() => onCredentialLink(connector.id)}
/>
) : (
<p className="text-red-700">N/A</p>
)
) : (
"-"
);
return (
<TableRow key={connectorIndexingStatus.cc_pair_id}>
{includeName && (
<TableCell className="whitespace-normal break-all">
<p className="text font-medium">
{connectorIndexingStatus.name}
</p>
</TableCell>
)}
{specialColumns?.map(({ key, getValue }) => (
<TableCell className="max-w-sm" key={key}>
<div className="break-words whitespace-normal">
{getValue(connectorIndexingStatus)}
</div>
</TableCell>
))}
<TableCell>
<SingleUseConnectorStatus
indexingStatus={connectorIndexingStatus.last_status}
deletionAttempt={connectorIndexingStatus.deletion_attempt}
/>
</TableCell>
{connectorIncludesCredential && (
<TableCell>{credentialDisplay}</TableCell>
)}
<TableCell>
<div
className="cursor-pointer mx-auto flex"
onClick={async () => {
// for one-time, just disable the connector at deletion time
// this is required before deletion can happen
await updateConnector({
...connector,
disabled: !connector.disabled,
});
const deletionScheduleError =
await scheduleDeletionJobForConnector(
connector.id,
connectorIndexingStatus.credential.id
);
if (deletionScheduleError) {
setPopup({
message:
"Failed to schedule deletion of connector - " +
deletionScheduleError,
type: "error",
});
} else {
setPopup({
message: "Scheduled deletion of connector!",
type: "success",
});
}
setTimeout(() => {
setPopup(null);
}, 4000);
onUpdate();
}}
>
<DeleteButton />
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
);
}

49
web/src/lib/ccPair.ts Normal file
View File

@@ -0,0 +1,49 @@
import { ConnectorCredentialPairStatus } from "@/app/admin/connector/[ccPairId]/types";
import { PopupSpec } from "@/components/admin/connectors/Popup";
export async function setCCPairStatus(
ccPairId: number,
ccPairStatus: ConnectorCredentialPairStatus,
setPopup?: (popupSpec: PopupSpec | null) => void,
onUpdate?: () => void
) {
try {
const response = await fetch(
`/api/manage/admin/cc-pair/${ccPairId}/status`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ status: ccPairStatus }),
}
);
if (!response.ok) {
const errorMessage = (await response.json()).detail;
setPopup &&
setPopup({
message: "Failed to update connector status - " + errorMessage,
type: "error",
});
}
setPopup &&
setPopup({
message:
ccPairStatus === ConnectorCredentialPairStatus.ACTIVE
? "Enabled connector!"
: "Paused connector!",
type: "success",
});
onUpdate && onUpdate();
} catch (error) {
console.error("Error updating CC pair status:", error);
setPopup &&
setPopup({
message: "Failed to update connector status",
type: "error",
});
}
}

View File

@@ -52,26 +52,6 @@ export async function updateConnector<T>(
return await response.json();
}
export async function disableConnector(
connector: Connector<any>,
setPopup: (popupSpec: PopupSpec | null) => void,
onUpdate: () => void
) {
updateConnector({
...connector,
disabled: !connector.disabled,
}).then(() => {
setPopup({
message: connector.disabled ? "Enabled connector!" : "Paused connector!",
type: "success",
});
setTimeout(() => {
setPopup(null);
}, 4000);
onUpdate && onUpdate();
});
}
export async function deleteConnector(
connectorId: number
): Promise<string | null> {

View File

@@ -774,7 +774,6 @@ export interface ConnectorBase<T> {
refresh_freq: number | null;
prune_freq: number | null;
indexing_start: Date | null;
disabled: boolean;
}
export interface Connector<T> extends ConnectorBase<T> {

View File

@@ -1,6 +1,7 @@
import { Persona } from "@/app/admin/assistants/interfaces";
import { Credential } from "./connectors/credentials";
import { Connector } from "./connectors/connectors";
import { ConnectorCredentialPairStatus } from "@/app/admin/connector/[ccPairId]/types";
export interface UserPreferences {
chosen_assistants: number[] | null;
@@ -67,6 +68,7 @@ export interface ConnectorIndexingStatus<
> {
cc_pair_id: number;
name: string | null;
cc_pair_status: ConnectorCredentialPairStatus;
connector: Connector<ConnectorConfigType>;
credential: Credential<ConnectorCredentialType>;
public_doc: boolean;