From 2c68c00d9bd6d14a308efbb03d4762a7cba986ee Mon Sep 17 00:00:00 2001 From: alma Date: Wed, 14 Jan 2026 21:19:55 +0100 Subject: [PATCH] Agenda refactor --- lib/services/microsoft-calendar-sync.ts | 61 ++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/services/microsoft-calendar-sync.ts b/lib/services/microsoft-calendar-sync.ts index 4a73f85..a351d03 100644 --- a/lib/services/microsoft-calendar-sync.ts +++ b/lib/services/microsoft-calendar-sync.ts @@ -138,13 +138,29 @@ export async function fetchMicrosoftEvents( }; // Add date filter if provided + // Note: Microsoft Graph API filter for events needs to handle both timed and all-day events + // For all-day events, start/dateTime might not exist, so we use start/date instead if (startDate && endDate) { // Microsoft Graph API expects ISO format for date filters - // For all-day events, we need to handle date-only format - const startFilter = startDate.toISOString(); - const endFilter = endDate.toISOString(); - params.$filter = `start/dateTime ge '${startFilter}' and start/dateTime le '${endFilter}'`; + // For all-day events, use start/date (YYYY-MM-DD format) + // For timed events, use start/dateTime (ISO format) + const startDateStr = startDate.toISOString().split('T')[0]; // YYYY-MM-DD + const endDateStr = endDate.toISOString().split('T')[0]; // YYYY-MM-DD + const startDateTimeStr = startDate.toISOString(); + const endDateTimeStr = endDate.toISOString(); + + // Filter: events where start is within range + // Handle both timed events (start/dateTime) and all-day events (start/date) + params.$filter = `(start/dateTime ge '${startDateTimeStr}' or start/date ge '${startDateStr}') and (start/dateTime le '${endDateTimeStr}' or start/date le '${endDateStr}')`; } + + logger.debug('Fetching Microsoft events with params', { + email, + calendarId, + startDate: startDate?.toISOString(), + endDate: endDate?.toISOString(), + filter: params.$filter, + }); // Get events from Microsoft Graph API const response = await axios.get( @@ -283,7 +299,13 @@ export async function syncMicrosoftCalendar( logger.info('Fetched Microsoft events', { calendarSyncId, eventCount: microsoftEvents.length, - events: microsoftEvents.slice(0, 5).map(e => ({ id: e.id, subject: e.subject, start: e.start.dateTime })), + events: microsoftEvents.slice(0, 10).map(e => ({ + id: e.id, + subject: e.subject, + start: e.start.dateTime || e.start.date, + isAllDay: e.isAllDay, + end: e.end.dateTime || e.end.date + })), }); // Convert Microsoft events to CalDAV-like format @@ -349,12 +371,23 @@ export async function syncMicrosoftCalendar( data: eventData, }); updated++; + logger.debug('Updated event', { + eventId: existingEvent.id, + title: caldavEvent.summary, + microsoftId, + }); } else { // Create new event - await prisma.event.create({ + const newEvent = await prisma.event.create({ data: eventData, }); created++; + logger.debug('Created new event', { + eventId: newEvent.id, + title: caldavEvent.summary, + microsoftId, + start: caldavEvent.start.toISOString(), + }); } } @@ -367,6 +400,22 @@ export async function syncMicrosoftCalendar( }, }); + // Invalidate cache for this user's calendars so new events appear immediately + try { + const { invalidateCalendarCache } = await import('@/lib/redis'); + await invalidateCalendarCache(syncConfig.calendar.userId); + logger.info('Invalidated calendar cache after sync', { + userId: syncConfig.calendar.userId, + calendarId: syncConfig.calendarId, + }); + } catch (cacheError) { + // Don't fail sync if cache invalidation fails + logger.warn('Failed to invalidate calendar cache', { + userId: syncConfig.calendar.userId, + error: cacheError instanceof Error ? cacheError.message : String(cacheError), + }); + } + logger.info('Microsoft calendar sync completed', { calendarSyncId, calendarId: syncConfig.calendarId,