Implemented sync functionality with sheets and email sending

This commit is contained in:
Roman Krček
2025-07-08 15:30:37 +02:00
parent 39bd172798
commit 5bd642b947
10 changed files with 1208 additions and 65 deletions

View File

@@ -98,6 +98,122 @@ export async function getUserInfo(accessToken: string): Promise<{ email: string;
}
}
/**
* Authenticate with Google using OAuth popup flow
* @returns Authentication result with success status and tokens
*/
export async function authenticateWithGoogle(): Promise<{
success: boolean;
refreshToken?: string;
userEmail?: string;
error?: string;
}> {
if (!browser) {
return { success: false, error: 'Not in browser environment' };
}
return new Promise((resolve) => {
try {
// Open popup window for OAuth
const popup = window.open(
'/auth/google',
'google-auth',
'width=500,height=600,scrollbars=yes,resizable=yes,left=' +
Math.round(window.screen.width / 2 - 250) + ',top=' +
Math.round(window.screen.height / 2 - 300)
);
if (!popup) {
resolve({ success: false, error: 'Failed to open popup window. Please allow popups for this site.' });
return;
}
let authCompleted = false;
let popupTimer: number | null = null;
// Store current timestamp to detect changes in localStorage
const startTimestamp = localStorage.getItem('google_auth_timestamp') ?? '0';
// Poll localStorage for auth completion
const pollInterval = setInterval(() => {
try {
const currentTimestamp = localStorage.getItem('google_auth_timestamp');
// If timestamp has changed, auth is complete
if (currentTimestamp && currentTimestamp !== startTimestamp) {
handleAuthSuccess();
}
} catch (e) {
console.error('Error checking auth timestamp:', e);
}
}, 500); // Poll every 500ms
// Common handler for authentication success
function handleAuthSuccess() {
if (authCompleted) return; // Prevent duplicate handling
authCompleted = true;
// Clean up timers
clearInterval(pollInterval);
if (popupTimer) clearTimeout(popupTimer);
// Get tokens from localStorage
const refreshToken = localStorage.getItem('google_refresh_token');
const userEmail = localStorage.getItem('google_user_email');
if (refreshToken) {
resolve({
success: true,
refreshToken,
userEmail: userEmail ?? undefined
});
} else {
resolve({ success: false, error: 'No refresh token found after authentication' });
}
}
// Clean up function to handle all cleanup in one place
const cleanUp = () => {
clearInterval(pollInterval);
if (popupTimer) clearTimeout(popupTimer);
};
// Set a timeout for initial auth check
popupTimer = setTimeout(() => {
if (!authCompleted) {
cleanUp();
// Check if tokens were stored by the popup before it was closed
const refreshToken = localStorage.getItem('google_refresh_token');
const userEmail = localStorage.getItem('google_user_email');
if (refreshToken) {
resolve({
success: true,
refreshToken,
userEmail: userEmail ?? undefined
});
} else {
resolve({ success: false, error: 'Authentication timeout or cancelled' });
}
}
}, 30 * 1000) as unknown as number;
// Final cleanup timeout
setTimeout(() => {
if (!authCompleted) {
cleanUp();
resolve({ success: false, error: 'Authentication timeout' });
}
}, 60 * 1000);
} catch (error) {
console.error('Error connecting to Google:', error);
resolve({ success: false, error: error instanceof Error ? error.message : 'Unknown error' });
}
});
}
/**
* Revoke a Google access token
* @param accessToken - Google access token to revoke