refactor: completely remove Local Folder connector references and update folder sync logic

This commit is contained in:
Anish Sarkar 2026-04-02 22:21:16 +05:30
parent 493d720b89
commit 149ccb97dd
8 changed files with 60 additions and 35 deletions

View file

@ -25,7 +25,6 @@ export enum EnumConnectorName {
YOUTUBE_CONNECTOR = "YOUTUBE_CONNECTOR", YOUTUBE_CONNECTOR = "YOUTUBE_CONNECTOR",
CIRCLEBACK_CONNECTOR = "CIRCLEBACK_CONNECTOR", CIRCLEBACK_CONNECTOR = "CIRCLEBACK_CONNECTOR",
OBSIDIAN_CONNECTOR = "OBSIDIAN_CONNECTOR", OBSIDIAN_CONNECTOR = "OBSIDIAN_CONNECTOR",
LOCAL_FOLDER_CONNECTOR = "LOCAL_FOLDER_CONNECTOR",
DROPBOX_CONNECTOR = "DROPBOX_CONNECTOR", DROPBOX_CONNECTOR = "DROPBOX_CONNECTOR",
MCP_CONNECTOR = "MCP_CONNECTOR", MCP_CONNECTOR = "MCP_CONNECTOR",
COMPOSIO_GOOGLE_DRIVE_CONNECTOR = "COMPOSIO_GOOGLE_DRIVE_CONNECTOR", COMPOSIO_GOOGLE_DRIVE_CONNECTOR = "COMPOSIO_GOOGLE_DRIVE_CONNECTOR",

View file

@ -3,7 +3,6 @@ import {
BookOpen, BookOpen,
File, File,
FileText, FileText,
FolderSync,
Globe, Globe,
Microscope, Microscope,
Search, Search,
@ -76,8 +75,6 @@ export const getConnectorIcon = (connectorType: EnumConnectorName | string, clas
return <Image src="/connectors/circleback.svg" alt="Circleback" {...imgProps} />; return <Image src="/connectors/circleback.svg" alt="Circleback" {...imgProps} />;
case EnumConnectorName.MCP_CONNECTOR: case EnumConnectorName.MCP_CONNECTOR:
return <Image src="/connectors/modelcontextprotocol.svg" alt="MCP" {...imgProps} />; return <Image src="/connectors/modelcontextprotocol.svg" alt="MCP" {...imgProps} />;
case EnumConnectorName.LOCAL_FOLDER_CONNECTOR:
return <FolderSync {...iconProps} />;
case EnumConnectorName.OBSIDIAN_CONNECTOR: case EnumConnectorName.OBSIDIAN_CONNECTOR:
return <Image src="/connectors/obsidian.svg" alt="Obsidian" {...imgProps} />; return <Image src="/connectors/obsidian.svg" alt="Obsidian" {...imgProps} />;
case EnumConnectorName.COMPOSIO_GOOGLE_DRIVE_CONNECTOR: case EnumConnectorName.COMPOSIO_GOOGLE_DRIVE_CONNECTOR:

View file

@ -30,7 +30,6 @@ export const searchSourceConnectorTypeEnum = z.enum([
"DROPBOX_CONNECTOR", "DROPBOX_CONNECTOR",
"MCP_CONNECTOR", "MCP_CONNECTOR",
"OBSIDIAN_CONNECTOR", "OBSIDIAN_CONNECTOR",
"LOCAL_FOLDER_CONNECTOR",
"COMPOSIO_GOOGLE_DRIVE_CONNECTOR", "COMPOSIO_GOOGLE_DRIVE_CONNECTOR",
"COMPOSIO_GMAIL_CONNECTOR", "COMPOSIO_GMAIL_CONNECTOR",
"COMPOSIO_GOOGLE_CALENDAR_CONNECTOR", "COMPOSIO_GOOGLE_CALENDAR_CONNECTOR",

View file

@ -1,41 +1,73 @@
"use client"; "use client";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { connectorsApiService } from "@/lib/apis/connectors-api.service"; import { documentsApiService } from "@/lib/apis/documents-api.service";
interface FileChangedEvent {
rootFolderId: number | null;
searchSpaceId: number;
folderPath: string;
folderName: string;
relativePath: string;
fullPath: string;
action: string;
timestamp: number;
}
const DEBOUNCE_MS = 2000; const DEBOUNCE_MS = 2000;
export function useFolderSync() { export function useFolderSync() {
const pendingRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map()); const queueRef = useRef<FileChangedEvent[]>([]);
const processingRef = useRef(false);
const debounceTimers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());
async function processQueue() {
if (processingRef.current) return;
processingRef.current = true;
while (queueRef.current.length > 0) {
const event = queueRef.current.shift()!;
try {
await documentsApiService.folderIndexFile(event.searchSpaceId, {
folder_path: event.folderPath,
folder_name: event.folderName,
search_space_id: event.searchSpaceId,
target_file_path: event.fullPath,
});
} catch (err) {
console.error("[FolderSync] Failed to trigger re-index:", err);
}
}
processingRef.current = false;
}
useEffect(() => { useEffect(() => {
const api = typeof window !== "undefined" ? window.electronAPI : null; const api = typeof window !== "undefined" ? window.electronAPI : null;
if (!api?.onFileChanged) return; if (!api?.onFileChanged) return;
const cleanup = api.onFileChanged((event) => { // Signal to main process that the renderer is ready to receive events
const key = `${event.connectorId}:${event.fullPath}`; api.signalRendererReady?.();
const existing = pendingRef.current.get(key); const cleanup = api.onFileChanged((event: FileChangedEvent) => {
const key = `${event.folderPath}:${event.fullPath}`;
const existing = debounceTimers.current.get(key);
if (existing) clearTimeout(existing); if (existing) clearTimeout(existing);
const timeout = setTimeout(async () => { const timeout = setTimeout(() => {
pendingRef.current.delete(key); debounceTimers.current.delete(key);
try { queueRef.current.push(event);
await connectorsApiService.indexFile(event.connectorId, event.fullPath); processQueue();
} catch (err) {
console.error("[FolderSync] Failed to trigger re-index:", err);
}
}, DEBOUNCE_MS); }, DEBOUNCE_MS);
pendingRef.current.set(key, timeout); debounceTimers.current.set(key, timeout);
}); });
return () => { return () => {
cleanup(); cleanup();
for (const timeout of pendingRef.current.values()) { for (const timeout of debounceTimers.current.values()) {
clearTimeout(timeout); clearTimeout(timeout);
} }
pendingRef.current.clear(); debounceTimers.current.clear();
}; };
}, []); }, []);
} }

View file

@ -405,17 +405,6 @@ class ConnectorsApiService {
); );
}; };
// =============================================================================
// Local Folder Connector Methods
// =============================================================================
indexFile = async (connectorId: number, filePath: string) => {
return baseApiService.post(
`/api/v1/search-source-connectors/${connectorId}/index-file`,
undefined,
{ body: { file_path: filePath } }
);
};
} }
export type { SlackChannel, DiscordChannel }; export type { SlackChannel, DiscordChannel };

View file

@ -395,6 +395,14 @@ class DocumentsApiService {
); );
}; };
folderIndex = async (searchSpaceId: number, body: { folder_path: string; folder_name: string; search_space_id: number; exclude_patterns?: string[]; file_extensions?: string[]; root_folder_id?: number; enable_summary?: boolean }) => {
return baseApiService.post(`/api/v1/documents/folder-index`, undefined, { body });
};
folderIndexFile = async (searchSpaceId: number, body: { folder_path: string; folder_name: string; search_space_id: number; target_file_path: string; enable_summary?: boolean }) => {
return baseApiService.post(`/api/v1/documents/folder-index-file`, undefined, { body });
};
/** /**
* Delete a document * Delete a document
*/ */

View file

@ -30,7 +30,6 @@ export const getConnectorTypeDisplay = (type: string): string => {
YOUTUBE_CONNECTOR: "YouTube", YOUTUBE_CONNECTOR: "YouTube",
CIRCLEBACK_CONNECTOR: "Circleback", CIRCLEBACK_CONNECTOR: "Circleback",
OBSIDIAN_CONNECTOR: "Obsidian", OBSIDIAN_CONNECTOR: "Obsidian",
LOCAL_FOLDER_CONNECTOR: "Local Folder",
DROPBOX_CONNECTOR: "Dropbox", DROPBOX_CONNECTOR: "Dropbox",
MCP_CONNECTOR: "MCP Server", MCP_CONNECTOR: "MCP Server",
}; };

View file

@ -5,15 +5,16 @@ interface WatchedFolderConfig {
name: string; name: string;
excludePatterns: string[]; excludePatterns: string[];
fileExtensions: string[] | null; fileExtensions: string[] | null;
connectorId: number; rootFolderId: number | null;
searchSpaceId: number; searchSpaceId: number;
active: boolean; active: boolean;
} }
interface FolderSyncFileChangedEvent { interface FolderSyncFileChangedEvent {
connectorId: number; rootFolderId: number | null;
searchSpaceId: number; searchSpaceId: number;
folderPath: string; folderPath: string;
folderName: string;
relativePath: string; relativePath: string;
fullPath: string; fullPath: string;
action: "add" | "change" | "unlink"; action: "add" | "change" | "unlink";
@ -21,7 +22,7 @@ interface FolderSyncFileChangedEvent {
} }
interface FolderSyncWatcherReadyEvent { interface FolderSyncWatcherReadyEvent {
connectorId: number; rootFolderId: number | null;
folderPath: string; folderPath: string;
} }
@ -49,6 +50,7 @@ interface ElectronAPI {
onWatcherReady: (callback: (data: FolderSyncWatcherReadyEvent) => void) => () => void; onWatcherReady: (callback: (data: FolderSyncWatcherReadyEvent) => void) => () => void;
pauseWatcher: () => Promise<void>; pauseWatcher: () => Promise<void>;
resumeWatcher: () => Promise<void>; resumeWatcher: () => Promise<void>;
signalRendererReady: () => Promise<void>;
} }
declare global { declare global {