import { JobApplication } from '../types'; let cachedAccessToken: string | null = localStorage.getItem('ai_job_portal_google_token'); export const getCachedAccessToken = () => cachedAccessToken; const getEnvVar = (key: string) => { try { return (import.meta as any).env?.[key]; } catch (e) { return undefined; } }; const OAUTH_CLIENT_ID = getEnvVar('VITE_GOOGLE_OAUTH_CLIENT_ID') || '488548028456-tal52pta8fm2uc9t4o2u82itqvi91rp7.apps.googleusercontent.com'; /** * Direct Google OAuth 2.0 Flow via Google Identity Services * Obtains real access token directly from accounts.google.com for Google Drive/Sheets API calls. */ export const googleWorkspaceSignIn = async (): Promise<{ email: string; name: string; accessToken: string }> => { return new Promise((resolve, reject) => { try { const scope = encodeURIComponent('https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/documents'); const redirectUri = encodeURIComponent(`${window.location.origin}`); const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${OAUTH_CLIENT_ID}&redirect_uri=${redirectUri}&response_type=token&scope=${scope}&prompt=select_account`; const width = 500; const height = 650; const left = window.screenX + (window.outerWidth - width) / 2; const top = window.screenY + (window.outerHeight - height) / 2; const popup = window.open( authUrl, 'Google Workspace OAuth Sign-In', `width=${width},height=${height},left=${left},top=${top},scrollbars=yes` ); if (!popup) { throw new Error('Popup window was blocked by your browser. Please allow popups for http://localhost:3000.'); } const interval = setInterval(async () => { try { if (popup.closed) { clearInterval(interval); if (!cachedAccessToken) { reject(new Error('Sign-in window closed before completing authentication.')); } return; } const currentUrl = popup.location.href; if (currentUrl && currentUrl.includes(window.location.origin)) { const hash = popup.location.hash; if (hash && hash.includes('access_token=')) { clearInterval(interval); popup.close(); const params = new URLSearchParams(hash.substring(1)); const token = params.get('access_token'); if (token) { cachedAccessToken = token; localStorage.setItem('ai_job_portal_google_token', token); localStorage.setItem('ai_job_portal_google_connected', 'true'); try { const userRes = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', { headers: { Authorization: `Bearer ${token}` } }); if (userRes.ok) { const userData = await userRes.json(); resolve({ email: userData.email || 'dkifer19@gmail.com', name: userData.name || 'David Kifer', accessToken: token }); return; } } catch (e) {} resolve({ email: 'dkifer19@gmail.com', name: 'David Kifer', accessToken: token }); } } } } catch (e) {} }, 400); } catch (err: any) { reject(err); } }); }; export const googleSignOut = async () => { cachedAccessToken = null; localStorage.removeItem('ai_job_portal_google_token'); localStorage.removeItem('ai_job_portal_google_connected'); localStorage.removeItem('ai_job_portal_google_user'); }; // Workspace Action Handlers export async function exportApplicationsToGoogleSheets( applications: JobApplication[], accessToken?: string ): Promise<{ spreadsheetId: string; spreadsheetUrl: string; isLocalCsv?: boolean; message?: string }> { const token = accessToken || cachedAccessToken; const res = await fetch('/api/workspace/sheets/export', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ applications, accessToken: token }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error || 'Failed to export applications to Google Sheets'); } return { spreadsheetId: data.spreadsheetId, spreadsheetUrl: data.spreadsheetUrl, isLocalCsv: data.isLocalCsv, message: data.message }; } export async function createTailoredGoogleDoc( title: string, content: string, docType: 'resume' | 'coverLetter' | 'note', accessToken?: string ): Promise<{ documentId: string; documentUrl: string; isLocalDoc?: boolean; message?: string }> { const token = accessToken || cachedAccessToken; const res = await fetch('/api/workspace/docs/create', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ title, content, docType, accessToken: token }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error || 'Failed to create Google Doc'); } return { documentId: data.documentId, documentUrl: data.documentUrl, isLocalDoc: data.isLocalDoc, message: data.message }; } export async function uploadFileToGoogleDrive( fileName: string, fileContent: string, mimeType: string = 'text/plain', accessToken?: string ): Promise<{ fileId: string; driveUrl: string; isLocalFile?: boolean; message?: string }> { const token = accessToken || cachedAccessToken; const res = await fetch('/api/workspace/drive/upload', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ fileName, fileContent, mimeType, accessToken: token }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error || 'Failed to upload file to Google Drive'); } return { fileId: data.fileId, driveUrl: data.driveUrl, isLocalFile: data.isLocalFile, message: data.message }; } export async function listGoogleDriveWorkspaceFiles(accessToken?: string): Promise { const token = accessToken || cachedAccessToken; const res = await fetch('/api/workspace/drive/files', { headers: { ...(token ? { Authorization: `Bearer ${token}` } : {}) } }); const data = await res.json(); return data.files || []; } export async function saveGoogleKeepNote( title: string, content: string, color: string = 'DEFAULT', accessToken?: string ): Promise<{ noteId: string; title: string; content: string; color: string; timestamp: string }> { const token = accessToken || cachedAccessToken; const res = await fetch('/api/workspace/keep/create', { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify({ title, content, color, accessToken: token }) }); const data = await res.json(); if (!data.success) { throw new Error(data.error || 'Failed to create Keep note'); } return data.note; }