From 018273c6b296db0ce4e9461435804051dc0eac89 Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Fri, 29 Aug 2025 01:06:40 +0530 Subject: [PATCH] (feat:connector) refactor, updated routes FE --- application/api/connector/routes.py | 44 ++----- frontend/src/components/ConnectorAuth.tsx | 112 +++++++++++++++++ frontend/src/upload/Upload.tsx | 146 +++++----------------- 3 files changed, 150 insertions(+), 152 deletions(-) create mode 100644 frontend/src/components/ConnectorAuth.tsx diff --git a/application/api/connector/routes.py b/application/api/connector/routes.py index df4c73f4..bcfa634d 100644 --- a/application/api/connector/routes.py +++ b/application/api/connector/routes.py @@ -1,7 +1,7 @@ import datetime import json -import os -from functools import wraps + + from bson.objectid import ObjectId from flask import ( Blueprint, @@ -13,7 +13,7 @@ from flask import ( from flask_restx import fields, Namespace, Resource -from application.agents.tools.tool_manager import ToolManager + from application.api.user.tasks import ( ingest_connector_task, @@ -21,16 +21,16 @@ from application.api.user.tasks import ( from application.core.mongo_db import MongoDB from application.core.settings import settings from application.api import api -from application.storage.storage_creator import StorageCreator -from application.tts.google_tts import GoogleTTS + + from application.utils import ( check_required_fields ) -from application.utils import num_tokens_from_string -from application.vectorstore.vector_creator import VectorCreator + + from application.parser.connectors.connector_creator import ConnectorCreator -storage = StorageCreator.get_storage() + mongo = MongoDB.get_client() db = mongo[settings.MONGO_DB_NAME] @@ -40,9 +40,6 @@ connector = Blueprint("connector", __name__) connectors_ns = Namespace("connectors", description="Connector operations", path="/") api.add_namespace(connectors_ns) -current_dir = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -) @connectors_ns.route("/api/connectors/upload") @@ -438,8 +435,7 @@ class ConnectorFiles(Resource): 'name': metadata.get('file_name', 'Unknown File'), 'type': metadata.get('mime_type', 'unknown'), 'size': metadata.get('size', 'Unknown'), - 'modifiedTime': metadata.get('modified_time', 'Unknown'), - 'iconUrl': get_file_icon(metadata.get('mime_type', '')) + 'modifiedTime': metadata.get('modified_time', 'Unknown') }) return make_response(jsonify({"success": True, "files": files, "total": len(files)}), 200) @@ -511,25 +507,3 @@ class ConnectorDisconnect(Resource): return make_response(jsonify({"success": False, "error": str(e)}), 500) -def get_file_icon(mime_type): - """Return appropriate icon URL based on file MIME type""" - icon_map = { - 'application/vnd.google-apps.document': '/icons/google-docs.png', - 'application/vnd.google-apps.spreadsheet': '/icons/google-sheets.png', - 'application/vnd.google-apps.presentation': '/icons/google-slides.png', - 'application/pdf': '/icons/pdf.png', - 'text/plain': '/icons/text.png', - 'application/msword': '/icons/word.png', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '/icons/word.png', - 'application/vnd.ms-excel': '/icons/excel.png', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '/icons/excel.png', - 'application/vnd.ms-powerpoint': '/icons/powerpoint.png', - 'application/vnd.openxmlformats-officedocument.presentationml.presentation': '/icons/powerpoint.png', - 'image/jpeg': '/icons/image.png', - 'image/png': '/icons/image.png', - 'image/gif': '/icons/image.png', - 'video/mp4': '/icons/video.png', - 'application/zip': '/icons/archive.png', - 'application/x-zip-compressed': '/icons/archive.png', - } - return icon_map.get(mime_type, '/icons/generic-file.png') diff --git a/frontend/src/components/ConnectorAuth.tsx b/frontend/src/components/ConnectorAuth.tsx new file mode 100644 index 00000000..22566521 --- /dev/null +++ b/frontend/src/components/ConnectorAuth.tsx @@ -0,0 +1,112 @@ +import React, { useRef } from 'react'; +import { useSelector } from 'react-redux'; +import { selectToken } from '../preferences/preferenceSlice'; + +interface ConnectorAuthProps { + provider: string; + onSuccess: (data: { session_token: string; user_email: string }) => void; + onError: (error: string) => void; + label?: string; +} + +const providerLabel = (provider: string) => { + const map: Record = { + google_drive: 'Google Drive', + }; + return map[provider] || provider.replace(/_/g, ' '); +}; + +const ConnectorAuth: React.FC = ({ provider, onSuccess, onError, label }) => { + const token = useSelector(selectToken); + const completedRef = useRef(false); + const intervalRef = useRef(null); + + const cleanup = () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + window.removeEventListener('message', handleAuthMessage as any); + }; + + const handleAuthMessage = (event: MessageEvent) => { + const successGeneric = event.data?.type === 'connector_auth_success'; + const successProvider = event.data?.type === `${provider}_auth_success` || event.data?.type === 'google_drive_auth_success'; + const errorProvider = event.data?.type === `${provider}_auth_error` || event.data?.type === 'google_drive_auth_error'; + + if (successGeneric || successProvider) { + completedRef.current = true; + cleanup(); + onSuccess({ + session_token: event.data.session_token, + user_email: event.data.user_email || 'Connected User', + }); + } else if (errorProvider) { + completedRef.current = true; + cleanup(); + onError(event.data.error || 'Authentication failed'); + } + }; + + const handleAuth = async () => { + try { + completedRef.current = false; + cleanup(); + + const apiHost = import.meta.env.VITE_API_HOST; + const authResponse = await fetch(`${apiHost}/api/connectors/auth?provider=${provider}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + + if (!authResponse.ok) { + throw new Error(`Failed to get authorization URL: ${authResponse.status}`); + } + + const authData = await authResponse.json(); + if (!authData.success || !authData.authorization_url) { + throw new Error(authData.error || 'Failed to get authorization URL'); + } + + const authWindow = window.open( + authData.authorization_url, + `${provider}-auth`, + 'width=500,height=600,scrollbars=yes,resizable=yes' + ); + if (!authWindow) { + throw new Error('Failed to open authentication window. Please allow popups.'); + } + + window.addEventListener('message', handleAuthMessage as any); + + const checkClosed = window.setInterval(() => { + if (authWindow.closed) { + clearInterval(checkClosed); + window.removeEventListener('message', handleAuthMessage as any); + if (!completedRef.current) { + onError('Authentication was cancelled'); + } + } + }, 1000); + intervalRef.current = checkClosed; + } catch (error) { + onError(error instanceof Error ? error.message : 'Authentication failed'); + } + }; + + const buttonLabel = label || `Connect ${providerLabel(provider)}`; + + return ( + + ); +}; + +export default ConnectorAuth; + diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index c2cf87ec..8020a0d5 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -27,6 +27,7 @@ import { } from './types/ingestor'; import FileIcon from '../assets/file.svg'; import FolderIcon from '../assets/folder.svg'; +import ConnectorAuth from '../components/ConnectorAuth'; function Upload({ receivedFile = [], @@ -329,8 +330,7 @@ function Upload({ data?.find( (d: Doc) => d.type?.toLowerCase() === 'local', ), - ), - ); + )); }); setProgress( (progress) => @@ -514,13 +514,13 @@ function Upload({ try { const apiHost = import.meta.env.VITE_API_HOST; - const validateResponse = await fetch(`${apiHost}/api/google-drive/validate-session`, { + const validateResponse = await fetch(`${apiHost}/api/connectors/validate-session`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, - body: JSON.stringify({ session_token: sessionToken }) + body: JSON.stringify({ provider: 'google_drive', session_token: sessionToken }) }); if (!validateResponse.ok) { @@ -547,94 +547,6 @@ function Upload({ } }; - const handleGoogleDriveConnect = async () => { - console.log('Google Drive connect button clicked'); - setIsAuthenticating(true); - setAuthError(''); - - const existingToken = localStorage.getItem('google_drive_session_token'); - if (existingToken) { - fetchUserEmailAndLoadFiles(existingToken); - setIsAuthenticating(false); - return; - } - - try { - const apiHost = import.meta.env.VITE_API_HOST; - - const authResponse = await fetch(`${apiHost}/api/google-drive/auth`, { - headers: { - 'Authorization': `Bearer ${token}` - } - }); - - if (!authResponse.ok) { - throw new Error(`Failed to get authorization URL: ${authResponse.status}`); - } - - const authData = await authResponse.json(); - - if (!authData.success || !authData.authorization_url) { - throw new Error(authData.error || 'Failed to get authorization URL'); - } - - console.log('Opening Google OAuth window...'); - - const authWindow = window.open( - authData.authorization_url, - 'google-drive-auth', - 'width=500,height=600,scrollbars=yes,resizable=yes' - ); - - if (!authWindow) { - throw new Error('Failed to open authentication window. Please allow popups.'); - } - - const handleAuthMessage = (event: MessageEvent) => { - console.log('Received message event:', event.data); - - if (event.data.type === 'google_drive_auth_success') { - console.log('OAuth success received:', event.data); - setUserEmail(event.data.user_email || 'Connected User'); - setIsGoogleDriveConnected(true); - setIsAuthenticating(false); - setAuthError(''); - - if (event.data.session_token) { - localStorage.setItem('google_drive_session_token', event.data.session_token); - } - - window.removeEventListener('message', handleAuthMessage); - - loadGoogleDriveFiles(event.data.session_token, null); - } else if (event.data.type === 'google_drive_auth_error') { - console.error('OAuth error received:', event.data); - setAuthError(event.data.error || 'Authentication failed. Please make sure to grant all requested permissions, including offline access. You may need to revoke previous access and re-authorize.'); - setIsAuthenticating(false); - setIsGoogleDriveConnected(false); - window.removeEventListener('message', handleAuthMessage); - } - }; - - window.addEventListener('message', handleAuthMessage); - const checkClosed = setInterval(() => { - if (authWindow.closed) { - clearInterval(checkClosed); - window.removeEventListener('message', handleAuthMessage); - - if (!isGoogleDriveConnected && !isAuthenticating) { - setAuthError('Authentication was cancelled'); - } - } - }, 1000); - - } catch (error) { - console.error('Error during Google Drive authentication:', error); - setAuthError(error instanceof Error ? error.message : 'Authentication failed'); - setIsAuthenticating(false); - } - }; - const loadGoogleDriveFiles = async (sessionToken: string, folderId?: string | null) => { setIsLoadingFiles(true); @@ -648,13 +560,13 @@ function Upload({ requestBody.folder_id = folderId; } - const filesResponse = await fetch(`${apiHost}/api/google-drive/files`, { + const filesResponse = await fetch(`${apiHost}/api/connectors/files`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, - body: JSON.stringify(requestBody) + body: JSON.stringify({ ...requestBody, provider: 'google_drive' }) }); if (!filesResponse.ok) { @@ -919,7 +831,7 @@ function Upload({ {files.map((file) => (

{file.name} @@ -973,25 +885,25 @@ function Upload({ )} {!isGoogleDriveConnected ? ( - + { + setUserEmail(data.user_email); + setIsGoogleDriveConnected(true); + setIsAuthenticating(false); + setAuthError(''); + + if (data.session_token) { + localStorage.setItem('google_drive_session_token', data.session_token); + loadGoogleDriveFiles(data.session_token, null); + } + }} + onError={(error) => { + setAuthError(error); + setIsAuthenticating(false); + setIsGoogleDriveConnected(false); + }} + /> ) : (

{/* Connection Status */} @@ -1013,13 +925,13 @@ function Upload({ setAuthError(''); const apiHost = import.meta.env.VITE_API_HOST; - fetch(`${apiHost}/api/google-drive/disconnect`, { + fetch(`${apiHost}/api/connectors/disconnect`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, - body: JSON.stringify({ session_token: localStorage.getItem('google_drive_session_token') }) + body: JSON.stringify({ provider: 'google_drive', session_token: localStorage.getItem('google_drive_session_token') }) }).catch(err => console.error('Error disconnecting from Google Drive:', err)); }} className="text-white hover:text-gray-200 text-xs underline" @@ -1111,7 +1023,7 @@ function Upload({ )}