123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- // Google Drive Picker API configuration
- let API_KEY = '';
- let CLIENT_ID = '';
- // Function to fetch credentials from backend config
- async function getCredentials() {
- const response = await fetch('/api/retrieval/config');
- if (!response.ok) {
- throw new Error('Failed to fetch Google Drive credentials');
- }
- const config = await response.json();
- API_KEY = config.google_drive?.api_key;
- CLIENT_ID = config.google_drive?.client_id;
- }
- const SCOPE = [
- 'https://www.googleapis.com/auth/drive.readonly',
- 'https://www.googleapis.com/auth/drive.file'
- ];
- // Validate required credentials
- const validateCredentials = () => {
- if (!API_KEY || !CLIENT_ID) {
- throw new Error('Google Drive API credentials not configured');
- }
- if (API_KEY === '' || CLIENT_ID === '') {
- throw new Error('Please configure valid Google Drive API credentials');
- }
- };
- let pickerApiLoaded = false;
- let oauthToken: string | null = null;
- let initialized = false;
- export const loadGoogleDriveApi = () => {
- return new Promise((resolve, reject) => {
- if (typeof gapi === 'undefined') {
- const script = document.createElement('script');
- script.src = 'https://apis.google.com/js/api.js';
- script.onload = () => {
- gapi.load('picker', () => {
- pickerApiLoaded = true;
- resolve(true);
- });
- };
- script.onerror = reject;
- document.body.appendChild(script);
- } else {
- gapi.load('picker', () => {
- pickerApiLoaded = true;
- resolve(true);
- });
- }
- });
- };
- export const loadGoogleAuthApi = () => {
- return new Promise((resolve, reject) => {
- if (typeof google === 'undefined') {
- const script = document.createElement('script');
- script.src = 'https://accounts.google.com/gsi/client';
- script.onload = resolve;
- script.onerror = reject;
- document.body.appendChild(script);
- } else {
- resolve(true);
- }
- });
- };
- export const getAuthToken = async () => {
- if (!oauthToken) {
- return new Promise((resolve, reject) => {
- const tokenClient = google.accounts.oauth2.initTokenClient({
- client_id: CLIENT_ID,
- scope: SCOPE.join(' '),
- callback: (response: any) => {
- if (response.access_token) {
- oauthToken = response.access_token;
- resolve(oauthToken);
- } else {
- reject(new Error('Failed to get access token'));
- }
- },
- error_callback: (error: any) => {
- reject(new Error(error.message || 'OAuth error occurred'));
- }
- });
- tokenClient.requestAccessToken();
- });
- }
- return oauthToken;
- };
- const initialize = async () => {
- if (!initialized) {
- await getCredentials();
- validateCredentials();
- await Promise.all([loadGoogleDriveApi(), loadGoogleAuthApi()]);
- initialized = true;
- }
- };
- export const createPicker = () => {
- return new Promise(async (resolve, reject) => {
- try {
- console.log('Initializing Google Drive Picker...');
- await initialize();
- console.log('Getting auth token...');
- const token = await getAuthToken();
- if (!token) {
- console.error('Failed to get OAuth token');
- throw new Error('Unable to get OAuth token');
- }
- console.log('Auth token obtained successfully');
- const picker = new google.picker.PickerBuilder()
- .enableFeature(google.picker.Feature.NAV_HIDDEN)
- .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
- .addView(
- new google.picker.DocsView()
- .setIncludeFolders(false)
- .setSelectFolderEnabled(false)
- .setMimeTypes(
- 'application/pdf,text/plain,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.google-apps.document,application/vnd.google-apps.spreadsheet,application/vnd.google-apps.presentation'
- )
- )
- .setOAuthToken(token)
- .setDeveloperKey(API_KEY)
- // Remove app ID setting as it's not needed and can cause 404 errors
- .setCallback(async (data: any) => {
- if (data[google.picker.Response.ACTION] === google.picker.Action.PICKED) {
- try {
- const doc = data[google.picker.Response.DOCUMENTS][0];
- const fileId = doc[google.picker.Document.ID];
- const fileName = doc[google.picker.Document.NAME];
- const fileUrl = doc[google.picker.Document.URL];
- if (!fileId || !fileName) {
- throw new Error('Required file details missing');
- }
- // Construct download URL based on MIME type
- const mimeType = doc[google.picker.Document.MIME_TYPE];
- let downloadUrl;
- let exportFormat;
- if (mimeType.includes('google-apps')) {
- // Handle Google Workspace files
- if (mimeType.includes('document')) {
- exportFormat = 'text/plain';
- } else if (mimeType.includes('spreadsheet')) {
- exportFormat = 'text/csv';
- } else if (mimeType.includes('presentation')) {
- exportFormat = 'text/plain';
- } else {
- exportFormat = 'application/pdf';
- }
- downloadUrl = `https://www.googleapis.com/drive/v3/files/${fileId}/export?mimeType=${encodeURIComponent(exportFormat)}`;
- } else {
- // Regular files use direct download URL
- downloadUrl = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`;
- }
- // Create a Blob from the file download
- const response = await fetch(downloadUrl, {
- headers: {
- Authorization: `Bearer ${token}`,
- Accept: '*/*'
- }
- });
- if (!response.ok) {
- const errorText = await response.text();
- console.error('Download failed:', {
- status: response.status,
- statusText: response.statusText,
- error: errorText
- });
- throw new Error(`Failed to download file (${response.status}): ${errorText}`);
- }
- const blob = await response.blob();
- const result = {
- id: fileId,
- name: fileName,
- url: downloadUrl,
- blob: blob,
- headers: {
- Authorization: `Bearer ${token}`,
- Accept: '*/*'
- }
- };
- resolve(result);
- } catch (error) {
- reject(error);
- }
- } else if (data[google.picker.Response.ACTION] === google.picker.Action.CANCEL) {
- resolve(null);
- }
- })
- .build();
- picker.setVisible(true);
- } catch (error) {
- console.error('Google Drive Picker error:', error);
- reject(error);
- }
- });
- };
|