import { v4 as uuidv4 } from 'uuid'; let CLIENT_ID = ''; async function getCredentials() { if (CLIENT_ID) return; const response = await fetch('/api/config'); if (!response.ok) { throw new Error('Failed to fetch OneDrive credentials'); } const config = await response.json(); CLIENT_ID = config.onedrive?.client_id; if (!CLIENT_ID) { throw new Error('OneDrive client ID not configured'); } } function loadMsalScript(): Promise { return new Promise((resolve, reject) => { const win = window; if (win.msal) { resolve(); return; } const script = document.createElement('script'); script.src = 'https://alcdn.msauth.net/browser/2.19.0/js/msal-browser.min.js'; script.async = true; script.onload = () => resolve(); script.onerror = () => reject(new Error('Failed to load MSAL script')); document.head.appendChild(script); }); } let msalInstance: any; // Initialize MSAL authentication async function initializeMsal() { if (!CLIENT_ID) { await getCredentials(); } const msalParams = { auth: { authority: 'https://login.microsoftonline.com/consumers', clientId: CLIENT_ID } }; try { await loadMsalScript(); const win = window; msalInstance = new win.msal.PublicClientApplication(msalParams); if (msalInstance.initialize) { await msalInstance.initialize(); } } catch (error) { console.error('MSAL initialization error:', error); } } // Retrieve OneDrive access token async function getToken(): Promise { const authParams = { scopes: ['OneDrive.ReadWrite'] }; let accessToken = ''; try { await initializeMsal(); const resp = await msalInstance.acquireTokenSilent(authParams); accessToken = resp.accessToken; } catch (err) { const resp = await msalInstance.loginPopup(authParams); msalInstance.setActiveAccount(resp.account); if (resp.idToken) { const resp2 = await msalInstance.acquireTokenSilent(authParams); accessToken = resp2.accessToken; } } return accessToken; } const baseUrl = 'https://onedrive.live.com/picker'; const params = { sdk: '8.0', entry: { oneDrive: { files: {} } }, authentication: {}, messaging: { origin: window?.location?.origin, channelId: uuidv4() }, typesAndSources: { mode: 'files', pivots: { oneDrive: true, recent: true } } }; // Download file from OneDrive async function downloadOneDriveFile(fileInfo: any): Promise { const accessToken = await getToken(); if (!accessToken) { throw new Error('Unable to retrieve OneDrive access token.'); } const fileInfoUrl = `${fileInfo['@sharePoint.endpoint']}/drives/${fileInfo.parentReference.driveId}/items/${fileInfo.id}`; const response = await fetch(fileInfoUrl, { headers: { Authorization: `Bearer ${accessToken}` } }); if (!response.ok) { throw new Error('Failed to fetch file information.'); } const fileData = await response.json(); const downloadUrl = fileData['@content.downloadUrl']; const downloadResponse = await fetch(downloadUrl); if (!downloadResponse.ok) { throw new Error('Failed to download file.'); } return await downloadResponse.blob(); } // Open OneDrive file picker and return selected file metadata export async function openOneDrivePicker(): Promise { if (typeof window === 'undefined') { throw new Error('Not in browser environment'); } return new Promise((resolve, reject) => { let pickerWindow: Window | null = null; let channelPort: MessagePort | null = null; const handleWindowMessage = (event: MessageEvent) => { if (event.source !== pickerWindow) return; const message = event.data; if (message?.type === 'initialize' && message?.channelId === params.messaging.channelId) { channelPort = event.ports?.[0]; if (!channelPort) return; channelPort.addEventListener('message', handlePortMessage); channelPort.start(); channelPort.postMessage({ type: 'activate' }); } }; const handlePortMessage = async (portEvent: MessageEvent) => { const portData = portEvent.data; switch (portData.type) { case 'notification': break; case 'command': { channelPort?.postMessage({ type: 'acknowledge', id: portData.id }); const command = portData.data; switch (command.command) { case 'authenticate': { try { const newToken = await getToken(); if (newToken) { channelPort?.postMessage({ type: 'result', id: portData.id, data: { result: 'token', token: newToken } }); } else { throw new Error('Could not retrieve auth token'); } } catch (err) { console.error(err); channelPort?.postMessage({ result: 'error', error: { code: 'tokenError', message: 'Failed to get token' }, isExpected: true }); } break; } case 'close': { cleanup(); resolve(null); break; } case 'pick': { channelPort?.postMessage({ type: 'result', id: portData.id, data: { result: 'success' } }); cleanup(); resolve(command); break; } default: { console.warn('Unsupported command:', command); channelPort?.postMessage({ result: 'error', error: { code: 'unsupportedCommand', message: command.command }, isExpected: true }); break; } } break; } } }; function cleanup() { window.removeEventListener('message', handleWindowMessage); if (channelPort) { channelPort.removeEventListener('message', handlePortMessage); } if (pickerWindow) { pickerWindow.close(); pickerWindow = null; } } const initializePicker = async () => { try { const authToken = await getToken(); if (!authToken) { return reject(new Error('Failed to acquire access token')); } pickerWindow = window.open('', 'OneDrivePicker', 'width=800,height=600'); if (!pickerWindow) { return reject(new Error('Failed to open OneDrive picker window')); } const queryString = new URLSearchParams({ filePicker: JSON.stringify(params) }); const url = `${baseUrl}?${queryString.toString()}`; const form = pickerWindow.document.createElement('form'); form.setAttribute('action', url); form.setAttribute('method', 'POST'); const input = pickerWindow.document.createElement('input'); input.setAttribute('type', 'hidden'); input.setAttribute('name', 'access_token'); input.setAttribute('value', authToken); form.appendChild(input); pickerWindow.document.body.appendChild(form); form.submit(); window.addEventListener('message', handleWindowMessage); } catch (err) { if (pickerWindow) pickerWindow.close(); reject(err); } }; initializePicker(); }); } // Pick and download file from OneDrive export async function pickAndDownloadFile(): Promise<{ blob: Blob; name: string } | null> { try { const pickerResult = await openOneDrivePicker(); if (!pickerResult || !pickerResult.items || pickerResult.items.length === 0) { return null; } const selectedFile = pickerResult.items[0]; const blob = await downloadOneDriveFile(selectedFile); return { blob, name: selectedFile.name }; } catch (error) { console.error('Error occurred during OneDrive file pick/download:', error); throw error; } } export { downloadOneDriveFile };