Agenda refactor
This commit is contained in:
parent
1d2a72ce8c
commit
fe6655f913
@ -595,6 +595,7 @@ export default async function CalendarPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auto-sync Infomaniak calendars if needed (background, don't block page load)
|
// Auto-sync Infomaniak calendars if needed (background, don't block page load)
|
||||||
|
// Reload sync configs after URL corrections to get updated URLs
|
||||||
const infomaniakSyncConfigs = await prisma.calendarSync.findMany({
|
const infomaniakSyncConfigs = await prisma.calendarSync.findMany({
|
||||||
where: {
|
where: {
|
||||||
provider: 'infomaniak',
|
provider: 'infomaniak',
|
||||||
@ -609,6 +610,12 @@ export default async function CalendarPage() {
|
|||||||
|
|
||||||
// Trigger sync for Infomaniak calendars that need it (async, don't wait)
|
// Trigger sync for Infomaniak calendars that need it (async, don't wait)
|
||||||
for (const syncConfig of infomaniakSyncConfigs) {
|
for (const syncConfig of infomaniakSyncConfigs) {
|
||||||
|
// Skip sync if URL is still invalid (should have been fixed above, but double-check)
|
||||||
|
if (syncConfig.externalCalendarUrl === '/principals' || !syncConfig.externalCalendarUrl || syncConfig.externalCalendarUrl === '/') {
|
||||||
|
console.log(`[AGENDA] Skipping Infomaniak sync ${syncConfig.id} - invalid calendar URL: ${syncConfig.externalCalendarUrl}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const minutesSinceLastSync = syncConfig.lastSyncAt
|
const minutesSinceLastSync = syncConfig.lastSyncAt
|
||||||
? (Date.now() - syncConfig.lastSyncAt.getTime()) / (1000 * 60)
|
? (Date.now() - syncConfig.lastSyncAt.getTime()) / (1000 * 60)
|
||||||
: Infinity;
|
: Infinity;
|
||||||
@ -617,7 +624,7 @@ export default async function CalendarPage() {
|
|||||||
const needsSync = !syncConfig.lastSyncAt ||
|
const needsSync = !syncConfig.lastSyncAt ||
|
||||||
minutesSinceLastSync >= syncConfig.syncFrequency;
|
minutesSinceLastSync >= syncConfig.syncFrequency;
|
||||||
|
|
||||||
console.log(`[AGENDA] Infomaniak sync config ${syncConfig.id}: lastSyncAt=${syncConfig.lastSyncAt}, minutesSinceLastSync=${minutesSinceLastSync.toFixed(1)}, needsSync=${needsSync}, syncFrequency=${syncConfig.syncFrequency}`);
|
console.log(`[AGENDA] Infomaniak sync config ${syncConfig.id}: lastSyncAt=${syncConfig.lastSyncAt}, minutesSinceLastSync=${minutesSinceLastSync.toFixed(1)}, needsSync=${needsSync}, syncFrequency=${syncConfig.syncFrequency}, calendarUrl=${syncConfig.externalCalendarUrl}`);
|
||||||
|
|
||||||
if (needsSync) {
|
if (needsSync) {
|
||||||
console.log(`[AGENDA] Triggering background sync for Infomaniak calendar ${syncConfig.id}`);
|
console.log(`[AGENDA] Triggering background sync for Infomaniak calendar ${syncConfig.id}`);
|
||||||
|
|||||||
@ -453,33 +453,74 @@ export async function syncInfomaniakCalendar(
|
|||||||
|
|
||||||
// For updates, we cannot modify calendarId and userId (they are relations)
|
// For updates, we cannot modify calendarId and userId (they are relations)
|
||||||
// For creates, we need them
|
// For creates, we need them
|
||||||
const baseEventData = {
|
// Build event data dynamically to handle case where externalEventId field doesn't exist yet
|
||||||
|
const baseEventData: any = {
|
||||||
title: caldavEvent.summary,
|
title: caldavEvent.summary,
|
||||||
description: caldavEvent.description || null,
|
description: caldavEvent.description || null,
|
||||||
start: caldavEvent.start,
|
start: caldavEvent.start,
|
||||||
end: caldavEvent.end,
|
end: caldavEvent.end,
|
||||||
location: caldavEvent.location || null,
|
location: caldavEvent.location || null,
|
||||||
isAllDay: caldavEvent.allDay,
|
isAllDay: caldavEvent.allDay,
|
||||||
externalEventId: caldavEvent.uid, // Store UID for reliable matching
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only add externalEventId if migration has been applied
|
||||||
|
// We'll try to add it, and if it fails, we'll retry without it
|
||||||
|
if (caldavEvent.uid) {
|
||||||
|
baseEventData.externalEventId = caldavEvent.uid;
|
||||||
|
}
|
||||||
|
|
||||||
if (existingEvent) {
|
if (existingEvent) {
|
||||||
// Update existing event (without calendarId and userId - they are relations)
|
// Update existing event (without calendarId and userId - they are relations)
|
||||||
await prisma.event.update({
|
try {
|
||||||
where: { id: existingEvent.id },
|
await prisma.event.update({
|
||||||
data: baseEventData,
|
where: { id: existingEvent.id },
|
||||||
});
|
data: baseEventData,
|
||||||
updated++;
|
});
|
||||||
|
updated++;
|
||||||
|
} catch (updateError: any) {
|
||||||
|
// If externalEventId field doesn't exist, retry without it
|
||||||
|
if (updateError?.message?.includes('externalEventId') || updateError?.code === 'P2009') {
|
||||||
|
logger.warn('externalEventId field not available, updating without it', {
|
||||||
|
eventId: existingEvent.id,
|
||||||
|
});
|
||||||
|
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
|
||||||
|
await prisma.event.update({
|
||||||
|
where: { id: existingEvent.id },
|
||||||
|
data: dataWithoutExternalId,
|
||||||
|
});
|
||||||
|
updated++;
|
||||||
|
} else {
|
||||||
|
throw updateError;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create new event (with calendarId and userId)
|
// Create new event (with calendarId and userId)
|
||||||
await prisma.event.create({
|
try {
|
||||||
data: {
|
await prisma.event.create({
|
||||||
...baseEventData,
|
data: {
|
||||||
calendarId: syncConfig.calendarId,
|
...baseEventData,
|
||||||
userId: syncConfig.calendar.userId,
|
calendarId: syncConfig.calendarId,
|
||||||
},
|
userId: syncConfig.calendar.userId,
|
||||||
});
|
},
|
||||||
created++;
|
});
|
||||||
|
created++;
|
||||||
|
} catch (createError: any) {
|
||||||
|
// If externalEventId field doesn't exist, retry without it
|
||||||
|
if (createError?.message?.includes('externalEventId') || createError?.code === 'P2009') {
|
||||||
|
logger.warn('externalEventId field not available, creating without it');
|
||||||
|
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
|
||||||
|
await prisma.event.create({
|
||||||
|
data: {
|
||||||
|
...dataWithoutExternalId,
|
||||||
|
calendarId: syncConfig.calendarId,
|
||||||
|
userId: syncConfig.calendar.userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
created++;
|
||||||
|
} else {
|
||||||
|
throw createError;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -499,44 +499,85 @@ export async function syncMicrosoftCalendar(
|
|||||||
|
|
||||||
// For updates, we cannot modify calendarId and userId (they are relations)
|
// For updates, we cannot modify calendarId and userId (they are relations)
|
||||||
// For creates, we need them
|
// For creates, we need them
|
||||||
const baseEventData = {
|
// Build event data dynamically to handle case where externalEventId field doesn't exist yet
|
||||||
|
const baseEventData: any = {
|
||||||
title: caldavEvent.summary,
|
title: caldavEvent.summary,
|
||||||
description: cleanedDescription, // Clean description without [MS_ID:xxx] prefix
|
description: cleanedDescription, // Clean description without [MS_ID:xxx] prefix
|
||||||
start: caldavEvent.start,
|
start: caldavEvent.start,
|
||||||
end: caldavEvent.end,
|
end: caldavEvent.end,
|
||||||
location: caldavEvent.location || null,
|
location: caldavEvent.location || null,
|
||||||
isAllDay: caldavEvent.allDay,
|
isAllDay: caldavEvent.allDay,
|
||||||
externalEventId: microsoftId, // Store Microsoft ID for reliable matching
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only add externalEventId if migration has been applied
|
||||||
|
// We'll try to add it, and if it fails, we'll retry without it
|
||||||
|
if (microsoftId) {
|
||||||
|
baseEventData.externalEventId = microsoftId;
|
||||||
|
}
|
||||||
|
|
||||||
if (existingEvent) {
|
if (existingEvent) {
|
||||||
// Update existing event (without calendarId and userId - they are relations)
|
// Update existing event (without calendarId and userId - they are relations)
|
||||||
await prisma.event.update({
|
try {
|
||||||
where: { id: existingEvent.id },
|
await prisma.event.update({
|
||||||
data: baseEventData,
|
where: { id: existingEvent.id },
|
||||||
});
|
data: baseEventData,
|
||||||
updated++;
|
});
|
||||||
logger.debug('Updated event', {
|
updated++;
|
||||||
eventId: existingEvent.id,
|
logger.debug('Updated event', {
|
||||||
title: caldavEvent.summary,
|
eventId: existingEvent.id,
|
||||||
microsoftId,
|
title: caldavEvent.summary,
|
||||||
});
|
microsoftId,
|
||||||
|
});
|
||||||
|
} catch (updateError: any) {
|
||||||
|
// If externalEventId field doesn't exist, retry without it
|
||||||
|
if (updateError?.message?.includes('externalEventId') || updateError?.code === 'P2009') {
|
||||||
|
logger.warn('externalEventId field not available, updating without it', {
|
||||||
|
eventId: existingEvent.id,
|
||||||
|
});
|
||||||
|
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
|
||||||
|
await prisma.event.update({
|
||||||
|
where: { id: existingEvent.id },
|
||||||
|
data: dataWithoutExternalId,
|
||||||
|
});
|
||||||
|
updated++;
|
||||||
|
} else {
|
||||||
|
throw updateError;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create new event (with calendarId and userId)
|
// Create new event (with calendarId and userId)
|
||||||
const newEvent = await prisma.event.create({
|
try {
|
||||||
data: {
|
const newEvent = await prisma.event.create({
|
||||||
...baseEventData,
|
data: {
|
||||||
calendarId: syncConfig.calendarId,
|
...baseEventData,
|
||||||
userId: syncConfig.calendar.userId,
|
calendarId: syncConfig.calendarId,
|
||||||
},
|
userId: syncConfig.calendar.userId,
|
||||||
});
|
},
|
||||||
created++;
|
});
|
||||||
logger.debug('Created new event', {
|
created++;
|
||||||
eventId: newEvent.id,
|
logger.debug('Created new event', {
|
||||||
title: caldavEvent.summary,
|
eventId: newEvent.id,
|
||||||
microsoftId,
|
title: caldavEvent.summary,
|
||||||
start: caldavEvent.start.toISOString(),
|
microsoftId,
|
||||||
});
|
start: caldavEvent.start.toISOString(),
|
||||||
|
});
|
||||||
|
} catch (createError: any) {
|
||||||
|
// If externalEventId field doesn't exist, retry without it
|
||||||
|
if (createError?.message?.includes('externalEventId') || createError?.code === 'P2009') {
|
||||||
|
logger.warn('externalEventId field not available, creating without it');
|
||||||
|
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
|
||||||
|
const newEvent = await prisma.event.create({
|
||||||
|
data: {
|
||||||
|
...dataWithoutExternalId,
|
||||||
|
calendarId: syncConfig.calendarId,
|
||||||
|
userId: syncConfig.calendar.userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
created++;
|
||||||
|
} else {
|
||||||
|
throw createError;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
49
scripts/apply-external-event-id-migration.sql
Normal file
49
scripts/apply-external-event-id-migration.sql
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
-- Script to manually apply externalEventId migration
|
||||||
|
-- Run this directly on your PostgreSQL database
|
||||||
|
|
||||||
|
-- Check if columns exist
|
||||||
|
SELECT
|
||||||
|
column_name,
|
||||||
|
data_type
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_name = 'Event'
|
||||||
|
AND column_name IN ('externalEventId', 'externalEventUrl');
|
||||||
|
|
||||||
|
-- Add externalEventId column if it doesn't exist
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_name = 'Event' AND column_name = 'externalEventId'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE "Event" ADD COLUMN "externalEventId" TEXT;
|
||||||
|
RAISE NOTICE 'Added externalEventId column';
|
||||||
|
ELSE
|
||||||
|
RAISE NOTICE 'externalEventId column already exists';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Add externalEventUrl column if it doesn't exist
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.columns
|
||||||
|
WHERE table_name = 'Event' AND column_name = 'externalEventUrl'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE "Event" ADD COLUMN "externalEventUrl" TEXT;
|
||||||
|
RAISE NOTICE 'Added externalEventUrl column';
|
||||||
|
ELSE
|
||||||
|
RAISE NOTICE 'externalEventUrl column already exists';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Create index on externalEventId if it doesn't exist
|
||||||
|
CREATE INDEX IF NOT EXISTS "Event_externalEventId_idx" ON "Event"("externalEventId");
|
||||||
|
|
||||||
|
-- Verify columns were added
|
||||||
|
SELECT
|
||||||
|
column_name,
|
||||||
|
data_type
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_name = 'Event'
|
||||||
|
AND column_name IN ('externalEventId', 'externalEventUrl');
|
||||||
Loading…
Reference in New Issue
Block a user