diff --git a/application/api/user/routes.py b/application/api/user/routes.py index 15024545..7eae66f6 100644 --- a/application/api/user/routes.py +++ b/application/api/user/routes.py @@ -3965,12 +3965,24 @@ class DirectoryStructure(Resource): ) directory_structure = doc.get("directory_structure", {}) + base_path = doc.get("file_path", "") + + provider = None + remote_data = doc.get("remote_data") + try: + if isinstance(remote_data, str) and remote_data: + remote_data_obj = json.loads(remote_data) + provider = remote_data_obj.get("provider") + except Exception as e: + current_app.logger.warning( + f"Failed to parse remote_data for doc {doc_id}: {e}") return make_response( jsonify({ "success": True, "directory_structure": directory_structure, - "base_path": doc.get("file_path", "") + "base_path": base_path, + "provider": provider, }), 200 ) diff --git a/frontend/src/api/services/userService.ts b/frontend/src/api/services/userService.ts index 7d365b3d..6e375951 100644 --- a/frontend/src/api/services/userService.ts +++ b/frontend/src/api/services/userService.ts @@ -1,5 +1,6 @@ import apiClient from '../client'; import endpoints from '../endpoints'; +import { getSessionToken } from '../../utils/providerUtils'; const userService = { getConfig: (): Promise => apiClient.get(endpoints.USER.CONFIG, null), @@ -104,14 +105,14 @@ const userService = { apiClient.get(endpoints.USER.DIRECTORY_STRUCTURE(docId), token), manageSourceFiles: (data: FormData, token: string | null): Promise => apiClient.postFormData(endpoints.USER.MANAGE_SOURCE_FILES, data, token), - syncConnector: (docId: string, token: string | null): Promise => { - const sessionToken = localStorage.getItem('google_drive_session_token'); + syncConnector: (docId: string, provider: string, token: string | null): Promise => { + const sessionToken = getSessionToken(provider); return apiClient.post( endpoints.USER.SYNC_CONNECTOR, - { - source_id: docId, + { + source_id: docId, session_token: sessionToken, - provider: 'google_drive' + provider: provider }, token ); diff --git a/frontend/src/components/ConnectorTreeComponent.tsx b/frontend/src/components/ConnectorTreeComponent.tsx index 9e8ccdf9..972c55f7 100644 --- a/frontend/src/components/ConnectorTreeComponent.tsx +++ b/frontend/src/components/ConnectorTreeComponent.tsx @@ -61,6 +61,9 @@ const ConnectorTreeComponent: React.FC = ({ const searchDropdownRef = useRef(null); const [isSyncing, setIsSyncing] = useState(false); const [syncProgress, setSyncProgress] = useState(0); + const [sourceProvider, setSourceProvider] = useState(''); + const [syncDone, setSyncDone] = useState(false); + useOutsideAlerter( searchDropdownRef, @@ -81,13 +84,16 @@ const ConnectorTreeComponent: React.FC = ({ }; const handleSync = async () => { + if (isSyncing) return; + const provider = sourceProvider; + setIsSyncing(true); setSyncProgress(0); try { - const response = await userService.syncConnector(docId, token); + const response = await userService.syncConnector(docId, provider, token); const data = await response.json(); if (data.success) { @@ -115,7 +121,14 @@ const ConnectorTreeComponent: React.FC = ({ const refreshData = await refreshResponse.json(); if (refreshData && refreshData.directory_structure) { setDirectoryStructure(refreshData.directory_structure); + setCurrentPath([]); } + if (refreshData && refreshData.provider) { + setSourceProvider(refreshData.provider); + } + + setSyncDone(true); + setTimeout(() => setSyncDone(false), 5000); } catch (err) { console.error('Error refreshing directory structure:', err); } @@ -124,8 +137,13 @@ const ConnectorTreeComponent: React.FC = ({ console.error('Sync task failed:', statusData.result); break; } else if (statusData.status === 'PROGRESS') { - const progress = statusData.meta?.current || 0; - setSyncProgress(Math.max(10, progress)); // Ensure minimum 10% after start + + const progress = Number((statusData.result && statusData.result.current != null) + ? statusData.result.current + : (statusData.meta && statusData.meta.current != null) + ? statusData.meta.current + : 0); + setSyncProgress(Math.max(10, progress)); } await new Promise((resolve) => setTimeout(resolve, pollInterval)); @@ -149,16 +167,21 @@ const ConnectorTreeComponent: React.FC = ({ const fetchDirectoryStructure = async () => { try { setLoading(true); - const response = await userService.getDirectoryStructure(docId, token); - const data = await response.json(); - if (data && data.directory_structure) { - setDirectoryStructure(data.directory_structure); + const directoryResponse = await userService.getDirectoryStructure(docId, token); + const directoryData = await directoryResponse.json(); + + if (directoryData && directoryData.directory_structure) { + setDirectoryStructure(directoryData.directory_structure); } else { setError('Invalid response format'); } + + if (directoryData && directoryData.provider) { + setSourceProvider(directoryData.provider); + } } catch (err) { - setError('Failed to load directory structure'); + setError('Failed to load source information'); console.error(err); } finally { setLoading(false); @@ -247,7 +270,7 @@ const ConnectorTreeComponent: React.FC = ({ ): { totalSize: number; totalTokens: number } => { let totalSize = 0; let totalTokens = 0; - + Object.entries(structure).forEach(([_, node]) => { if (node.type) { // It's a file @@ -260,10 +283,10 @@ const ConnectorTreeComponent: React.FC = ({ totalTokens += stats.totalTokens; } }); - + return { totalSize, totalTokens }; }; - + const handleBackNavigation = () => { if (selectedFile) { setSelectedFile(null); @@ -287,24 +310,21 @@ const ConnectorTreeComponent: React.FC = ({ > left-arrow - -
- + +
+ {sourceName} {currentPath.length > 0 && ( <> - / + / {currentPath.map((dir, index) => ( - + {index < currentPath.length - 1 && ( - / + / )} ))} @@ -326,14 +346,16 @@ const ConnectorTreeComponent: React.FC = ({ ? 'bg-gray-300 text-gray-600 cursor-not-allowed dark:bg-gray-600 dark:text-gray-400' : 'bg-purple-30 hover:bg-violets-are-blue text-white' }`} - title={isSyncing ? `${t('settings.sources.syncing')} ${syncProgress}%` : t('settings.sources.sync')} + title={isSyncing + ? `${t('settings.sources.syncing')} ${syncProgress}%` + : (syncDone ? 'Done' : t('settings.sources.sync'))} > {t('settings.sources.sync')} - {isSyncing ? `${syncProgress}%` : t('settings.sources.sync')} + {isSyncing ? `${syncProgress}%` : (syncDone ? 'Done' : t('settings.sources.sync'))}
@@ -379,25 +401,25 @@ const ConnectorTreeComponent: React.FC = ({ const sortedEntries = Object.entries(directory).sort(([nameA, nodeA], [nameB, nodeB]) => { const isFileA = !!nodeA.type; const isFileB = !!nodeB.type; - + if (isFileA !== isFileB) { return isFileA ? 1 : -1; // Directories first } - + return nameA.localeCompare(nameB); // Alphabetical within each group }); - + // Process directories const directoryRows = sortedEntries .filter(([_, node]) => !node.type) .map(([name, node]) => { const itemId = `dir-${name}`; const menuRef = getMenuRef(itemId); - + // Calculate directory stats const dirStats = calculateDirectoryStats(node as DirectoryStructure); - + return ( = ({ .map(([name, node]) => { const itemId = `file-${name}`; const menuRef = getMenuRef(itemId); - + return ( = ({ } }} placeholder={t('settings.sources.searchFiles')} - className={`w-full h-[38px] border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] - ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} + className={`w-full h-[38px] border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] + ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none dark:text-[#E0E0E0]`} /> diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index 8020a0d5..c780e68c 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -4,6 +4,7 @@ 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 FileUpload from '../assets/file_upload.svg'; import WebsiteCollect from '../assets/website_collect.svg'; import Dropdown from '../components/Dropdown'; @@ -62,6 +63,8 @@ function Upload({ const [currentFolderId, setCurrentFolderId] = useState(null); const [folderPath, setFolderPath] = useState>([{id: null, name: 'My Drive'}]); + + const renderFormFields = () => { const schema = IngestorFormSchemas[ingestor.type]; if (!schema) return null; @@ -445,7 +448,7 @@ function Upload({ let configData; if (ingestor.type === 'google_drive') { - const sessionToken = localStorage.getItem('google_drive_session_token'); + const sessionToken = getSessionToken(ingestor.type); const selectedItems = googleDriveFiles.filter(file => selectedFiles.includes(file.id)); const selectedFolderIds = selectedItems @@ -497,7 +500,7 @@ function Upload({ useEffect(() => { if (ingestor.type === 'google_drive') { - const sessionToken = localStorage.getItem('google_drive_session_token'); + const sessionToken = getSessionToken(ingestor.type); if (sessionToken) { // Auto-authenticate if session token exists @@ -524,7 +527,7 @@ function Upload({ }); if (!validateResponse.ok) { - localStorage.removeItem('google_drive_session_token'); + removeSessionToken(ingestor.type); setIsGoogleDriveConnected(false); setAuthError('Session expired. Please reconnect to Google Drive.'); return; @@ -536,7 +539,7 @@ function Upload({ setUserEmail(validateData.user_email || 'Connected User'); loadGoogleDriveFiles(sessionToken, null); } else { - localStorage.removeItem('google_drive_session_token'); + removeSessionToken(ingestor.type); setIsGoogleDriveConnected(false); setAuthError(validateData.error || 'Session expired. Please reconnect your Google Drive account and make sure to grant offline access.'); } @@ -640,7 +643,7 @@ function Upload({ }; const handleFolderClick = (folderId: string, folderName: string) => { - const sessionToken = localStorage.getItem('google_drive_session_token'); + const sessionToken = getSessionToken(ingestor.type); if (sessionToken) { setCurrentFolderId(folderId); setFolderPath(prev => [...prev, {id: folderId, name: folderName}]); @@ -649,7 +652,7 @@ function Upload({ }; const navigateBack = (index: number) => { - const sessionToken = localStorage.getItem('google_drive_session_token'); + const sessionToken = getSessionToken(ingestor.type); if (sessionToken) { const newPath = folderPath.slice(0, index + 1); const targetFolderId = newPath[newPath.length - 1]?.id; @@ -894,7 +897,7 @@ function Upload({ setAuthError(''); if (data.session_token) { - localStorage.setItem('google_drive_session_token', data.session_token); + setSessionToken(ingestor.type, data.session_token); loadGoogleDriveFiles(data.session_token, null); } }} @@ -916,7 +919,7 @@ function Upload({