// Google Calendar OAuth wrapper import { google } from 'googleapis'; export interface GoogleCalendarEvent { id: string; summary: string; description?: string; start: { dateTime?: string; date?: string; }; end: { dateTime?: string; date?: string; }; attendees?: Array<{ email: string; displayName?: string; responseStatus?: string; }>; location?: string; colorId?: string; reminders?: { useDefault: boolean; overrides?: Array<{ method: string; minutes: number }>; }; transparency?: string; // 'opaque' | 'transparent' visibility?: string; // 'default' | 'public' | 'private' | 'confidential' recurrence?: string[]; } export interface GoogleCalendar { id: string; summary: string; primary?: boolean; backgroundColor?: string; } /** * Initialize Google Calendar OAuth client */ export const initializeOAuth = (clientId: string, clientSecret: string, redirectUri: string) => { const oauth2Client = new google.auth.OAuth2( clientId, clientSecret, redirectUri ); return oauth2Client; }; /** * Get user calendar list using OAuth token */ export const getUserCalendars = async ( oauth2Client: any, accessToken: string ): Promise => { // Set access token oauth2Client.setCredentials({ access_token: accessToken }); try { const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); const response = await calendar.calendarList.list(); return response.data.items?.map((item: any) => ({ id: item.id, summary: item.summary, primary: item.primary, backgroundColor: item.backgroundColor, })) || []; } catch (error) { console.error('Error fetching user calendars:', error); throw new Error('Failed to fetch user calendars'); } }; /** * Get upcoming events for a specified time period */ export const getUpcomingEvents = async ( oauth2Client: any, accessToken: string, calendarId: string, timeMin: string, timeMax: string ): Promise => { // Set access token oauth2Client.setCredentials({ access_token: accessToken }); try { const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); const response = await calendar.events.list({ calendarId, timeMin, timeMax, singleEvents: true, orderBy: 'startTime', }); return response.data.items?.map((item: any) => ({ id: item.id, summary: item.summary, description: item.description, start: item.start, end: item.end, attendees: item.attendees?.map((a: any) => ({ email: a.email, displayName: a.displayName, responseStatus: a.responseStatus, })), location: item.location, colorId: item.colorId, reminders: item.reminders, transparency: item.transparency, visibility: item.visibility, recurringEventId: item.recurringEventId, })) || []; } catch (error) { console.error('Error fetching upcoming events:', error); throw new Error('Failed to fetch upcoming events'); } }; /** * Create a new event */ export const createEvent = async ( oauth2Client: any, accessToken: string, calendarId: string, event: Partial ): Promise => { oauth2Client.setCredentials({ access_token: accessToken }); try { const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); const requestBody: any = { summary: event.summary, description: event.description, start: event.start, end: event.end, location: event.location, }; if ((event as any).recurrence) requestBody.recurrence = (event as any).recurrence; if ((event as any).source) requestBody.source = (event as any).source; if (event.reminders) requestBody.reminders = event.reminders; if (event.transparency) requestBody.transparency = event.transparency; if (event.visibility) requestBody.visibility = event.visibility; if (event.attendees) requestBody.attendees = event.attendees; const response = await calendar.events.insert({ calendarId, requestBody, }); return response.data as any; } catch (error) { console.error('Error creating event:', error); throw new Error('Failed to create event'); } }; /** * Update an existing event */ export const updateEvent = async ( oauth2Client: any, accessToken: string, calendarId: string, eventId: string, event: Partial ): Promise => { oauth2Client.setCredentials({ access_token: accessToken }); try { const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); const requestBody: any = { summary: event.summary, description: event.description, start: event.start, end: event.end, location: event.location, }; if ((event as any).recurrence) requestBody.recurrence = (event as any).recurrence; if ((event as any).source) requestBody.source = (event as any).source; if (event.reminders) requestBody.reminders = event.reminders; if (event.transparency) requestBody.transparency = event.transparency; if (event.visibility) requestBody.visibility = event.visibility; if (event.attendees) requestBody.attendees = event.attendees; const response = await calendar.events.patch({ calendarId, eventId, requestBody, }); return response.data as any; } catch (error) { console.error('Error updating event:', error); throw new Error('Failed to update event'); } }; /** * Get a single event by ID */ export const getEvent = async ( oauth2Client: any, accessToken: string, calendarId: string, eventId: string ): Promise => { oauth2Client.setCredentials({ access_token: accessToken }); try { const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); const response = await calendar.events.get({ calendarId, eventId, }); return response.data as any; } catch (error) { console.error('Error getting event:', error); return null; } }; /** * Delete an event */ export const deleteEvent = async ( oauth2Client: any, accessToken: string, calendarId: string, eventId: string ): Promise => { oauth2Client.setCredentials({ access_token: accessToken }); try { const calendar = google.calendar({ version: 'v3', auth: oauth2Client }); await calendar.events.delete({ calendarId, eventId, }); } catch (error: any) { // 410 Gone means already deleted — treat as success if (error?.code === 410 || error?.status === 410) { console.log('[GOOGLE] Event already deleted (410 Gone), treating as success'); return; } console.error('Error deleting event:', error); throw new Error('Failed to delete event'); } };