Agenda refactor

This commit is contained in:
alma 2026-01-15 12:46:55 +01:00
parent fe6655f913
commit 946a110054
3 changed files with 153 additions and 22 deletions

View File

@ -418,11 +418,14 @@ export async function syncInfomaniakCalendar(
});
// Create a map of existing events by externalEventId (UID) for fast lookup
// Use type assertion to handle case where Prisma client doesn't recognize externalEventId yet
type EventType = typeof existingEvents[number];
const existingEventsByExternalId = new Map<string, EventType>();
for (const event of existingEvents) {
if (event.externalEventId) {
existingEventsByExternalId.set(event.externalEventId, event);
// Access externalEventId safely (may not be in Prisma type if client not regenerated)
const externalId = (event as any).externalEventId;
if (externalId) {
existingEventsByExternalId.set(externalId, event);
}
}
@ -441,7 +444,9 @@ export async function syncInfomaniakCalendar(
if (!existingEvent) {
existingEvent = existingEvents.find(
(e: EventType) => {
if (!e.externalEventId && // Only match events that don't have externalEventId yet
// Access externalEventId safely (may not be in Prisma type if client not regenerated)
const hasExternalId = !!(e as any).externalEventId;
if (!hasExternalId && // Only match events that don't have externalEventId yet
e.title === caldavEvent.summary) {
const timeDiff = Math.abs(new Date(e.start).getTime() - caldavEvent.start.getTime());
return timeDiff < 60000; // Within 1 minute
@ -478,10 +483,18 @@ export async function syncInfomaniakCalendar(
});
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', {
// If externalEventId field doesn't exist in Prisma client (even though it exists in DB),
// retry without it. This can happen if Prisma client wasn't regenerated after migration.
const errorMessage = updateError?.message || '';
const errorCode = updateError?.code || '';
if (errorMessage.includes('externalEventId') ||
errorMessage.includes('Unknown argument') ||
errorCode === 'P2009' ||
errorCode === 'P1012') {
logger.warn('externalEventId field not recognized by Prisma client, updating without it', {
eventId: existingEvent.id,
error: errorMessage.substring(0, 200),
});
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
await prisma.event.update({
@ -505,9 +518,18 @@ export async function syncInfomaniakCalendar(
});
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');
// If externalEventId field doesn't exist in Prisma client (even though it exists in DB),
// retry without it. This can happen if Prisma client wasn't regenerated after migration.
const errorMessage = createError?.message || '';
const errorCode = createError?.code || '';
if (errorMessage.includes('externalEventId') ||
errorMessage.includes('Unknown argument') ||
errorCode === 'P2009' ||
errorCode === 'P1012') {
logger.warn('externalEventId field not recognized by Prisma client, creating without it', {
error: errorMessage.substring(0, 200),
});
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
await prisma.event.create({
data: {

View File

@ -440,10 +440,13 @@ export async function syncMicrosoftCalendar(
});
// Create a map of existing events by externalEventId (Microsoft ID) for fast lookup
// Use type assertion to handle case where Prisma client doesn't recognize externalEventId yet
const existingEventsByExternalId = new Map<string, typeof existingEvents[0]>();
for (const event of existingEvents) {
if (event.externalEventId) {
existingEventsByExternalId.set(event.externalEventId, event);
// Access externalEventId safely (may not be in Prisma type if client not regenerated)
const externalId = (event as any).externalEventId;
if (externalId) {
existingEventsByExternalId.set(externalId, event);
}
}
@ -477,7 +480,9 @@ export async function syncMicrosoftCalendar(
// Priority 2: Fallback to checking description for [MS_ID:xxx] (backward compatibility)
if (!existingEvent && microsoftId) {
existingEvent = existingEvents.find((e) => {
if (!e.externalEventId && e.description && e.description.includes(`[MS_ID:${microsoftId}]`)) {
// Access externalEventId safely (may not be in Prisma type if client not regenerated)
const hasExternalId = !!(e as any).externalEventId;
if (!hasExternalId && e.description && e.description.includes(`[MS_ID:${microsoftId}]`)) {
return true;
}
return false;
@ -487,10 +492,16 @@ export async function syncMicrosoftCalendar(
// Priority 3: Fallback to title + date matching for events without externalEventId
if (!existingEvent) {
existingEvent = existingEvents.find(
(e) =>
!e.externalEventId && // Only match events that don't have externalEventId yet
e.title === caldavEvent.summary &&
Math.abs(new Date(e.start).getTime() - caldavEvent.start.getTime()) < 60000 // Within 1 minute
(e) => {
// Access externalEventId safely (may not be in Prisma type if client not regenerated)
const hasExternalId = !!(e as any).externalEventId;
if (!hasExternalId && // Only match events that don't have externalEventId yet
e.title === caldavEvent.summary) {
const timeDiff = Math.abs(new Date(e.start).getTime() - caldavEvent.start.getTime());
return timeDiff < 60000; // Within 1 minute
}
return false;
}
);
}
@ -529,10 +540,18 @@ export async function syncMicrosoftCalendar(
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', {
// If externalEventId field doesn't exist in Prisma client (even though it exists in DB),
// retry without it. This can happen if Prisma client wasn't regenerated after migration.
const errorMessage = updateError?.message || '';
const errorCode = updateError?.code || '';
if (errorMessage.includes('externalEventId') ||
errorMessage.includes('Unknown argument') ||
errorCode === 'P2009' ||
errorCode === 'P1012') {
logger.warn('externalEventId field not recognized by Prisma client, updating without it', {
eventId: existingEvent.id,
error: errorMessage.substring(0, 200),
});
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
await prisma.event.update({
@ -562,9 +581,18 @@ export async function syncMicrosoftCalendar(
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');
// If externalEventId field doesn't exist in Prisma client (even though it exists in DB),
// retry without it. This can happen if Prisma client wasn't regenerated after migration.
const errorMessage = createError?.message || '';
const errorCode = createError?.code || '';
if (errorMessage.includes('externalEventId') ||
errorMessage.includes('Unknown argument') ||
errorCode === 'P2009' ||
errorCode === 'P1012') {
logger.warn('externalEventId field not recognized by Prisma client, creating without it', {
error: errorMessage.substring(0, 200),
});
const { externalEventId, ...dataWithoutExternalId } = baseEventData;
const newEvent = await prisma.event.create({
data: {

View File

@ -0,0 +1,81 @@
#!/usr/bin/env ts-node
/**
* Script to verify Prisma client is up to date with schema
* Run this to check if externalEventId field is available in Prisma client
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function verifyPrismaClient() {
try {
console.log('Checking if externalEventId field is available in Prisma client...\n');
// Try to access the externalEventId field in the Event model
// If it doesn't exist, TypeScript/Prisma will throw an error at compile time
// But we can check at runtime by trying to query with it
// Check if we can select externalEventId
try {
const testEvent = await prisma.event.findFirst({
select: {
id: true,
title: true,
externalEventId: true,
externalEventUrl: true,
},
});
if (testEvent !== null) {
console.log('✅ Prisma client recognizes externalEventId and externalEventUrl fields');
console.log(' Sample event:', {
id: testEvent.id,
title: testEvent.title,
hasExternalEventId: 'externalEventId' in testEvent,
hasExternalEventUrl: 'externalEventUrl' in testEvent,
});
} else {
console.log('⚠️ No events found, but Prisma client seems to recognize the fields');
}
} catch (error: any) {
if (error.message?.includes('externalEventId') || error.message?.includes('Unknown argument')) {
console.error('❌ Prisma client does NOT recognize externalEventId field');
console.error(' Error:', error.message);
console.log('\n🔧 Solution:');
console.log(' 1. Run: npx prisma generate');
console.log(' 2. Restart your Next.js server');
} else {
throw error;
}
}
// Also check database directly
const dbColumns = await prisma.$queryRaw<Array<{ column_name: string; data_type: string }>>`
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'Event'
AND column_name IN ('externalEventId', 'externalEventUrl')
`;
console.log('\n📊 Database columns:');
if (dbColumns.length === 0) {
console.log(' ❌ externalEventId and externalEventUrl columns NOT found in database');
console.log(' 🔧 Run the migration: npx prisma migrate deploy');
} else {
console.log(' ✅ Database columns exist:');
dbColumns.forEach(col => {
console.log(` - ${col.column_name} (${col.data_type})`);
});
}
await prisma.$disconnect();
} catch (error) {
console.error('Error:', error);
await prisma.$disconnect();
process.exit(1);
}
}
verifyPrismaClient();