mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 16:43:16 +00:00
Merge branch 'main' of https://github.com/siiddhantt/DocsGPT into pr/1930
This commit is contained in:
@@ -4,6 +4,13 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import userService from '../api/services/userService';
|
||||
import {
|
||||
getSessionToken,
|
||||
setSessionToken,
|
||||
removeSessionToken,
|
||||
} from '../utils/providerUtils';
|
||||
import { formatDate } from '../utils/dateTimeUtils';
|
||||
import { formatBytes } from '../utils/stringUtils';
|
||||
import FileUpload from '../assets/file_upload.svg';
|
||||
import WebsiteCollect from '../assets/website_collect.svg';
|
||||
import Dropdown from '../components/Dropdown';
|
||||
@@ -25,6 +32,9 @@ import {
|
||||
IngestorFormSchemas,
|
||||
IngestorType,
|
||||
} from './types/ingestor';
|
||||
import FileIcon from '../assets/file.svg';
|
||||
import FolderIcon from '../assets/folder.svg';
|
||||
import ConnectorAuth from '../components/ConnectorAuth';
|
||||
|
||||
function Upload({
|
||||
receivedFile = [],
|
||||
@@ -48,6 +58,23 @@ function Upload({
|
||||
const [activeTab, setActiveTab] = useState<string | null>(renderTab);
|
||||
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||
|
||||
// Google Drive state
|
||||
const [isGoogleDriveConnected, setIsGoogleDriveConnected] = useState(false);
|
||||
const [googleDriveFiles, setGoogleDriveFiles] = useState<any[]>([]);
|
||||
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
|
||||
const [isLoadingFiles, setIsLoadingFiles] = useState(false);
|
||||
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
||||
const [userEmail, setUserEmail] = useState<string>('');
|
||||
const [authError, setAuthError] = useState<string>('');
|
||||
const [currentFolderId, setCurrentFolderId] = useState<string | null>(null);
|
||||
const [folderPath, setFolderPath] = useState<
|
||||
Array<{ id: string | null; name: string }>
|
||||
>([{ id: null, name: 'My Drive' }]);
|
||||
|
||||
const [nextPageToken, setNextPageToken] = useState<string | null>(null);
|
||||
const [hasMoreFiles, setHasMoreFiles] = useState<boolean>(false);
|
||||
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const renderFormFields = () => {
|
||||
const schema = IngestorFormSchemas[ingestor.type];
|
||||
if (!schema) return null;
|
||||
@@ -204,6 +231,7 @@ function Upload({
|
||||
{ label: 'Link', value: 'url' },
|
||||
{ label: 'GitHub', value: 'github' },
|
||||
{ label: 'Reddit', value: 'reddit' },
|
||||
{ label: 'Google Drive', value: 'google_drive' },
|
||||
];
|
||||
|
||||
const sourceDocs = useSelector(selectSourceDocs);
|
||||
@@ -428,29 +456,40 @@ function Upload({
|
||||
formData.append('user', 'local');
|
||||
formData.append('source', ingestor.type);
|
||||
|
||||
const defaultConfig = IngestorDefaultConfigs[ingestor.type].config;
|
||||
let configData;
|
||||
|
||||
const mergedConfig = { ...defaultConfig, ...ingestor.config };
|
||||
const filteredConfig = Object.entries(mergedConfig).reduce(
|
||||
(acc, [key, value]) => {
|
||||
const field = IngestorFormSchemas[ingestor.type].find(
|
||||
(f) => f.name === key,
|
||||
);
|
||||
// Include the field if:
|
||||
// 1. It's required, or
|
||||
// 2. It's optional and has a non-empty value
|
||||
if (
|
||||
field?.required ||
|
||||
(value !== undefined && value !== null && value !== '')
|
||||
) {
|
||||
acc[key] = value;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
);
|
||||
if (ingestor.type === 'google_drive') {
|
||||
const sessionToken = getSessionToken(ingestor.type);
|
||||
|
||||
formData.append('data', JSON.stringify(filteredConfig));
|
||||
const selectedItems = googleDriveFiles.filter((file) =>
|
||||
selectedFiles.includes(file.id),
|
||||
);
|
||||
const selectedFolderIds = selectedItems
|
||||
.filter(
|
||||
(item) =>
|
||||
item.type === 'application/vnd.google-apps.folder' || item.isFolder,
|
||||
)
|
||||
.map((folder) => folder.id);
|
||||
|
||||
const selectedFileIds = selectedItems
|
||||
.filter(
|
||||
(item) =>
|
||||
item.type !== 'application/vnd.google-apps.folder' &&
|
||||
!item.isFolder,
|
||||
)
|
||||
.map((file) => file.id);
|
||||
|
||||
configData = {
|
||||
file_ids: selectedFileIds,
|
||||
folder_ids: selectedFolderIds,
|
||||
recursive: ingestor.config.recursive,
|
||||
session_token: sessionToken || null,
|
||||
};
|
||||
} else {
|
||||
configData = { ...ingestor.config };
|
||||
}
|
||||
|
||||
formData.append('data', JSON.stringify(configData));
|
||||
|
||||
const apiHost: string = import.meta.env.VITE_API_HOST;
|
||||
const xhr = new XMLHttpRequest();
|
||||
@@ -477,6 +516,181 @@ function Upload({
|
||||
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
|
||||
xhr.send(formData);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (ingestor.type === 'google_drive') {
|
||||
const sessionToken = getSessionToken(ingestor.type);
|
||||
|
||||
if (sessionToken) {
|
||||
// Auto-authenticate if session token exists
|
||||
setIsGoogleDriveConnected(true);
|
||||
setAuthError('');
|
||||
|
||||
// Fetch user email and files using the existing session token
|
||||
|
||||
fetchUserEmailAndLoadFiles(sessionToken);
|
||||
}
|
||||
}
|
||||
}, [ingestor.type]);
|
||||
|
||||
const fetchUserEmailAndLoadFiles = async (sessionToken: string) => {
|
||||
try {
|
||||
const apiHost = import.meta.env.VITE_API_HOST;
|
||||
|
||||
const validateResponse = await fetch(
|
||||
`${apiHost}/api/connectors/validate-session`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
provider: 'google_drive',
|
||||
session_token: sessionToken,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (!validateResponse.ok) {
|
||||
removeSessionToken(ingestor.type);
|
||||
setIsGoogleDriveConnected(false);
|
||||
setAuthError('Session expired. Please reconnect to Google Drive.');
|
||||
return;
|
||||
}
|
||||
|
||||
const validateData = await validateResponse.json();
|
||||
|
||||
if (validateData.success) {
|
||||
setUserEmail(validateData.user_email || 'Connected User');
|
||||
// reset pagination state and files
|
||||
setGoogleDriveFiles([]);
|
||||
|
||||
setNextPageToken(null);
|
||||
setHasMoreFiles(false);
|
||||
loadGoogleDriveFiles(sessionToken, null, null, false);
|
||||
} else {
|
||||
removeSessionToken(ingestor.type);
|
||||
setIsGoogleDriveConnected(false);
|
||||
setAuthError(
|
||||
validateData.error ||
|
||||
'Session expired. Please reconnect your Google Drive account and make sure to grant offline access.',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error validating Google Drive session:', error);
|
||||
setAuthError('Failed to validate session. Please reconnect.');
|
||||
setIsGoogleDriveConnected(false);
|
||||
}
|
||||
};
|
||||
|
||||
const loadGoogleDriveFiles = async (
|
||||
sessionToken: string,
|
||||
folderId?: string | null,
|
||||
pageToken?: string | null,
|
||||
append = false,
|
||||
) => {
|
||||
setIsLoadingFiles(true);
|
||||
|
||||
try {
|
||||
const apiHost = import.meta.env.VITE_API_HOST;
|
||||
const requestBody: any = {
|
||||
session_token: sessionToken,
|
||||
limit: 10,
|
||||
};
|
||||
if (folderId) {
|
||||
requestBody.folder_id = folderId;
|
||||
}
|
||||
if (pageToken) {
|
||||
requestBody.page_token = pageToken;
|
||||
}
|
||||
|
||||
const filesResponse = await fetch(`${apiHost}/api/connectors/files`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ ...requestBody, provider: 'google_drive' }),
|
||||
});
|
||||
|
||||
if (!filesResponse.ok) {
|
||||
throw new Error(`Failed to load files: ${filesResponse.status}`);
|
||||
}
|
||||
|
||||
const filesData = await filesResponse.json();
|
||||
|
||||
if (filesData.success && Array.isArray(filesData.files)) {
|
||||
setGoogleDriveFiles((prev) =>
|
||||
append ? [...prev, ...filesData.files] : filesData.files,
|
||||
);
|
||||
setNextPageToken(filesData.next_page_token || null);
|
||||
setHasMoreFiles(Boolean(filesData.has_more));
|
||||
} else {
|
||||
throw new Error(filesData.error || 'Failed to load files');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading Google Drive files:', error);
|
||||
setAuthError(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Failed to load files. Please make sure your Google Drive account is properly connected and you granted offline access during authorization.',
|
||||
);
|
||||
} finally {
|
||||
setIsLoadingFiles(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle file selection
|
||||
const handleFileSelect = (fileId: string) => {
|
||||
setSelectedFiles((prev) => {
|
||||
if (prev.includes(fileId)) {
|
||||
return prev.filter((id) => id !== fileId);
|
||||
} else {
|
||||
return [...prev, fileId];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleFolderClick = (folderId: string, folderName: string) => {
|
||||
const sessionToken = getSessionToken(ingestor.type);
|
||||
if (sessionToken) {
|
||||
setCurrentFolderId(folderId);
|
||||
setFolderPath((prev) => [...prev, { id: folderId, name: folderName }]);
|
||||
|
||||
setGoogleDriveFiles([]);
|
||||
setNextPageToken(null);
|
||||
setHasMoreFiles(false);
|
||||
setSelectedFiles([]);
|
||||
loadGoogleDriveFiles(sessionToken, folderId, null, false);
|
||||
}
|
||||
};
|
||||
|
||||
const navigateBack = (index: number) => {
|
||||
const sessionToken = getSessionToken(ingestor.type);
|
||||
if (sessionToken) {
|
||||
const newPath = folderPath.slice(0, index + 1);
|
||||
const targetFolderId = newPath[newPath.length - 1]?.id;
|
||||
|
||||
setCurrentFolderId(targetFolderId as string | null);
|
||||
setFolderPath(newPath);
|
||||
|
||||
setGoogleDriveFiles([]);
|
||||
setNextPageToken(null);
|
||||
setHasMoreFiles(false);
|
||||
setSelectedFiles([]);
|
||||
loadGoogleDriveFiles(sessionToken, targetFolderId ?? null, null, false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectAll = () => {
|
||||
if (selectedFiles.length === googleDriveFiles.length) {
|
||||
setSelectedFiles([]);
|
||||
} else {
|
||||
setSelectedFiles(googleDriveFiles.map((file) => file.id));
|
||||
}
|
||||
};
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
multiple: true,
|
||||
@@ -515,6 +729,10 @@ function Upload({
|
||||
if (!remoteName?.trim()) {
|
||||
return true;
|
||||
}
|
||||
if (ingestor.type === 'google_drive') {
|
||||
return !isGoogleDriveConnected || selectedFiles.length === 0;
|
||||
}
|
||||
|
||||
const formFields: FormField[] = IngestorFormSchemas[ingestor.type];
|
||||
for (const field of formFields) {
|
||||
if (field.required) {
|
||||
@@ -636,7 +854,7 @@ function Upload({
|
||||
{files.map((file) => (
|
||||
<p
|
||||
key={file.name}
|
||||
className="text-gray-6000 truncate overflow-hidden text-ellipsis"
|
||||
className="text-gray-6000 truncate overflow-hidden text-ellipsis dark:text-[#ececf1]"
|
||||
title={file.name}
|
||||
>
|
||||
{file.name}
|
||||
@@ -679,6 +897,253 @@ function Upload({
|
||||
required={true}
|
||||
labelBgClassName="bg-white dark:bg-charleston-green-2"
|
||||
/>
|
||||
{ingestor.type === 'google_drive' && (
|
||||
<div className="space-y-4">
|
||||
{authError && (
|
||||
<div className="rounded-lg border border-red-200 bg-red-50 p-3 dark:border-red-600 dark:bg-red-900/20">
|
||||
<p className="text-sm text-red-600 dark:text-red-400">
|
||||
⚠️ {authError}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isGoogleDriveConnected ? (
|
||||
<ConnectorAuth
|
||||
provider="google_drive"
|
||||
onSuccess={(data) => {
|
||||
setUserEmail(data.user_email);
|
||||
setIsGoogleDriveConnected(true);
|
||||
setIsAuthenticating(false);
|
||||
setAuthError('');
|
||||
|
||||
if (data.session_token) {
|
||||
setSessionToken(ingestor.type, data.session_token);
|
||||
loadGoogleDriveFiles(data.session_token, null);
|
||||
}
|
||||
}}
|
||||
onError={(error) => {
|
||||
setAuthError(error);
|
||||
setIsAuthenticating(false);
|
||||
setIsGoogleDriveConnected(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{/* Connection Status */}
|
||||
<div className="flex w-full items-center justify-between rounded-lg bg-green-500 px-4 py-2 text-sm text-white">
|
||||
<div className="flex items-center gap-2">
|
||||
<svg className="h-4 w-4" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Connected as {userEmail}</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
removeSessionToken(ingestor.type);
|
||||
|
||||
setIsGoogleDriveConnected(false);
|
||||
setGoogleDriveFiles([]);
|
||||
setSelectedFiles([]);
|
||||
setUserEmail('');
|
||||
setAuthError('');
|
||||
|
||||
const apiHost = import.meta.env.VITE_API_HOST;
|
||||
fetch(`${apiHost}/api/connectors/disconnect`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
provider: ingestor.type,
|
||||
session_token: getSessionToken(ingestor.type),
|
||||
}),
|
||||
}).catch((err) =>
|
||||
console.error(
|
||||
'Error disconnecting from Google Drive:',
|
||||
err,
|
||||
),
|
||||
);
|
||||
}}
|
||||
className="text-xs text-white underline hover:text-gray-200"
|
||||
>
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* File Browser */}
|
||||
<div className="rounded-lg border border-gray-200 dark:border-gray-600">
|
||||
<div className="rounded-t-lg border-b border-gray-200 bg-gray-50 p-3 dark:border-gray-600 dark:bg-gray-800">
|
||||
{/* Breadcrumb navigation */}
|
||||
<div className="mb-2 flex items-center gap-1">
|
||||
{folderPath.map((path, index) => (
|
||||
<div
|
||||
key={path.id || 'root'}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{index > 0 && (
|
||||
<span className="text-gray-400">/</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => navigateBack(index)}
|
||||
className="text-sm text-blue-600 hover:text-blue-800 hover:underline dark:text-blue-400"
|
||||
disabled={index === folderPath.length - 1}
|
||||
>
|
||||
{path.name}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Select Files from Google Drive
|
||||
</h4>
|
||||
{googleDriveFiles.length > 0 && (
|
||||
<button
|
||||
onClick={handleSelectAll}
|
||||
className="text-xs text-blue-600 hover:text-blue-800 dark:text-blue-400"
|
||||
>
|
||||
{selectedFiles.length === googleDriveFiles.length
|
||||
? 'Deselect All'
|
||||
: 'Select All'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{selectedFiles.length > 0 && (
|
||||
<p className="mt-1 text-xs text-gray-500">
|
||||
{selectedFiles.length} file
|
||||
{selectedFiles.length !== 1 ? 's' : ''} selected
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="max-h-72 overflow-y-auto"
|
||||
ref={scrollContainerRef}
|
||||
>
|
||||
{isLoadingFiles && googleDriveFiles.length === 0 ? (
|
||||
<div className="p-4 text-center">
|
||||
<div className="inline-flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-blue-500 border-t-transparent"></div>
|
||||
Loading files...
|
||||
</div>
|
||||
</div>
|
||||
) : googleDriveFiles.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
No files found in your Google Drive
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="divide-y divide-gray-200 dark:divide-gray-600">
|
||||
{googleDriveFiles.map((file) => (
|
||||
<div
|
||||
key={file.id}
|
||||
className={`p-3 transition-colors ${
|
||||
selectedFiles.includes(file.id)
|
||||
? 'bg-blue-50 dark:bg-blue-900/20'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-shrink-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedFiles.includes(
|
||||
file.id,
|
||||
)}
|
||||
onChange={() =>
|
||||
handleFileSelect(file.id)
|
||||
}
|
||||
className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
{file.type ===
|
||||
'application/vnd.google-apps.folder' ||
|
||||
file.isFolder ? (
|
||||
<div
|
||||
className="cursor-pointer text-lg hover:text-blue-600"
|
||||
onClick={() =>
|
||||
handleFolderClick(file.id, file.name)
|
||||
}
|
||||
>
|
||||
<img
|
||||
src={FolderIcon}
|
||||
alt="Folder"
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-lg">
|
||||
<img
|
||||
src={FileIcon}
|
||||
alt="File"
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
<p
|
||||
className={`truncate text-sm font-medium dark:text-[#ececf1] ${
|
||||
file.type ===
|
||||
'application/vnd.google-apps.folder' ||
|
||||
file.isFolder
|
||||
? 'cursor-pointer hover:text-blue-600'
|
||||
: ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (
|
||||
file.type ===
|
||||
'application/vnd.google-apps.folder' ||
|
||||
file.isFolder
|
||||
) {
|
||||
handleFolderClick(
|
||||
file.id,
|
||||
file.name,
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{file.name}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{file.size &&
|
||||
`${formatBytes(file.size)} • `}
|
||||
Modified {formatDate(file.modifiedTime)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center border-t border-gray-100 p-4 dark:border-gray-800">
|
||||
{isLoadingFiles && (
|
||||
<div className="inline-flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-blue-500 border-t-transparent"></div>
|
||||
Loading more files...
|
||||
</div>
|
||||
)}
|
||||
{!hasMoreFiles && !isLoadingFiles && (
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
All files loaded
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="hidden" aria-hidden="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{renderFormFields()}
|
||||
{IngestorFormSchemas[ingestor.type].some(
|
||||
(field) => field.advanced,
|
||||
@@ -719,7 +1184,9 @@ function Upload({
|
||||
: 'bg-purple-30 hover:bg-violets-are-blue cursor-pointer text-white'
|
||||
}`}
|
||||
>
|
||||
{t('modals.uploadDoc.train')}
|
||||
{ingestor.type === 'google_drive' && selectedFiles.length > 0
|
||||
? `Train with ${selectedFiles.length} file${selectedFiles.length !== 1 ? 's' : ''}`
|
||||
: t('modals.uploadDoc.train')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -727,6 +1194,41 @@ function Upload({
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const scrollContainer = scrollContainerRef.current;
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!scrollContainer) return;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
|
||||
const isNearBottom = scrollHeight - scrollTop - clientHeight < 50;
|
||||
|
||||
if (isNearBottom && hasMoreFiles && !isLoadingFiles && nextPageToken) {
|
||||
const sessionToken = getSessionToken(ingestor.type);
|
||||
if (sessionToken) {
|
||||
loadGoogleDriveFiles(
|
||||
sessionToken,
|
||||
currentFolderId,
|
||||
nextPageToken,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scrollContainer?.addEventListener('scroll', handleScroll);
|
||||
|
||||
return () => {
|
||||
scrollContainer?.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [
|
||||
hasMoreFiles,
|
||||
isLoadingFiles,
|
||||
nextPageToken,
|
||||
currentFolderId,
|
||||
ingestor.type,
|
||||
]);
|
||||
|
||||
return (
|
||||
<WrapperModal
|
||||
isPerformingTask={progress !== undefined && progress.percentage < 100}
|
||||
|
||||
@@ -22,7 +22,19 @@ export interface UrlIngestorConfig extends BaseIngestorConfig {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type IngestorType = 'crawler' | 'github' | 'reddit' | 'url';
|
||||
export interface GoogleDriveIngestorConfig extends BaseIngestorConfig {
|
||||
folder_id?: string;
|
||||
file_ids?: string;
|
||||
recursive?: boolean;
|
||||
token_info?: any;
|
||||
}
|
||||
|
||||
export type IngestorType =
|
||||
| 'crawler'
|
||||
| 'github'
|
||||
| 'reddit'
|
||||
| 'url'
|
||||
| 'google_drive';
|
||||
|
||||
export interface IngestorConfig {
|
||||
type: IngestorType;
|
||||
@@ -31,7 +43,8 @@ export interface IngestorConfig {
|
||||
| RedditIngestorConfig
|
||||
| GithubIngestorConfig
|
||||
| CrawlerIngestorConfig
|
||||
| UrlIngestorConfig;
|
||||
| UrlIngestorConfig
|
||||
| GoogleDriveIngestorConfig;
|
||||
}
|
||||
|
||||
export type IngestorFormData = {
|
||||
@@ -109,6 +122,14 @@ export const IngestorFormSchemas: Record<IngestorType, FormField[]> = {
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
google_drive: [
|
||||
{
|
||||
name: 'recursive',
|
||||
label: 'Include subfolders',
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const IngestorDefaultConfigs: Record<
|
||||
@@ -143,4 +164,12 @@ export const IngestorDefaultConfigs: Record<
|
||||
repo_url: '',
|
||||
} as GithubIngestorConfig,
|
||||
},
|
||||
google_drive: {
|
||||
name: '',
|
||||
config: {
|
||||
folder_id: '',
|
||||
file_ids: '',
|
||||
recursive: true,
|
||||
} as GoogleDriveIngestorConfig,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user