import { NextRequest, NextResponse } from "next/server"; import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/options"; import { prisma } from "@/lib/prisma"; import { syncInfomaniakCalendar } from "@/lib/services/caldav-sync"; import { syncMicrosoftCalendar } from "@/lib/services/microsoft-calendar-sync"; import { logger } from "@/lib/logger"; /** * Create a calendar sync configuration * POST /api/calendars/sync */ export async function POST(req: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: "Non authentifié" }, { status: 401 }); } const { calendarId, mailCredentialId, externalCalendarUrl, externalCalendarId, syncFrequency, provider } = await req.json(); if (!calendarId || !mailCredentialId || !externalCalendarUrl) { return NextResponse.json( { error: "calendarId, mailCredentialId et externalCalendarUrl sont requis" }, { status: 400 } ); } // Determine provider if not provided let detectedProvider = provider; if (!detectedProvider) { const mailCreds = await prisma.mailCredentials.findUnique({ where: { id: mailCredentialId }, }); if (mailCreds?.host.includes('infomaniak')) { detectedProvider = 'infomaniak'; } else if (mailCreds?.host.includes('outlook.office365.com')) { detectedProvider = 'microsoft'; } else { return NextResponse.json( { error: "Provider non supporté ou non détecté" }, { status: 400 } ); } } // Verify calendar belongs to user const calendar = await prisma.calendar.findFirst({ where: { id: calendarId, userId: session.user.id, }, }); if (!calendar) { return NextResponse.json( { error: "Calendrier non trouvé" }, { status: 404 } ); } // Verify mail credentials belong to user const mailCreds = await prisma.mailCredentials.findFirst({ where: { id: mailCredentialId, userId: session.user.id, }, }); if (!mailCreds) { return NextResponse.json( { error: "Credentials non trouvés" }, { status: 404 } ); } // Create or update sync configuration // Use different default frequencies: 5 min for Microsoft (more reactive), 15 min for others const defaultSyncFrequency = detectedProvider === 'microsoft' ? 5 : (syncFrequency || 15); const syncConfig = await prisma.calendarSync.upsert({ where: { calendarId }, create: { calendarId, mailCredentialId, provider: detectedProvider, externalCalendarId: externalCalendarId || null, externalCalendarUrl, syncEnabled: true, syncFrequency: syncFrequency || defaultSyncFrequency, }, update: { mailCredentialId, provider: detectedProvider, externalCalendarId: externalCalendarId || null, externalCalendarUrl, syncFrequency: syncFrequency || defaultSyncFrequency, syncEnabled: true, }, }); // Trigger initial sync based on provider try { if (detectedProvider === 'infomaniak') { const { syncInfomaniakCalendar } = await import('@/lib/services/caldav-sync'); await syncInfomaniakCalendar(syncConfig.id, true); } else if (detectedProvider === 'microsoft') { const { syncMicrosoftCalendar } = await import('@/lib/services/microsoft-calendar-sync'); await syncMicrosoftCalendar(syncConfig.id, true); } } catch (syncError) { logger.error('Error during initial sync', { syncConfigId: syncConfig.id, provider: detectedProvider, error: syncError instanceof Error ? syncError.message : String(syncError), }); // Don't fail the request if sync fails, just log it } return NextResponse.json({ syncConfig }); } catch (error) { logger.error('Error creating calendar sync', { error: error instanceof Error ? error.message : String(error), }); return NextResponse.json( { error: "Erreur lors de la création de la synchronisation", details: error instanceof Error ? error.message : String(error), }, { status: 500 } ); } } /** * Trigger manual sync for a calendar * PUT /api/calendars/sync */ export async function PUT(req: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: "Non authentifié" }, { status: 401 }); } const { calendarSyncId } = await req.json(); if (!calendarSyncId) { return NextResponse.json( { error: "calendarSyncId est requis" }, { status: 400 } ); } // Verify sync config belongs to user const syncConfig = await prisma.calendarSync.findUnique({ where: { id: calendarSyncId }, include: { calendar: true, }, }); if (!syncConfig || syncConfig.calendar.userId !== session.user.id) { return NextResponse.json( { error: "Synchronisation non trouvée" }, { status: 404 } ); } // Trigger sync based on provider let result; if (syncConfig.provider === 'infomaniak') { result = await syncInfomaniakCalendar(calendarSyncId, true); } else if (syncConfig.provider === 'microsoft') { result = await syncMicrosoftCalendar(calendarSyncId, true); } else { return NextResponse.json( { error: "Provider non supporté" }, { status: 400 } ); } return NextResponse.json({ success: true, result }); } catch (error) { logger.error('Error triggering calendar sync', { error: error instanceof Error ? error.message : String(error), }); return NextResponse.json( { error: "Erreur lors de la synchronisation", details: error instanceof Error ? error.message : String(error), }, { status: 500 } ); } } /** * Get sync status for user calendars * GET /api/calendars/sync */ export async function GET(req: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: "Non authentifié" }, { status: 401 }); } const syncConfigs = await prisma.calendarSync.findMany({ where: { calendar: { userId: session.user.id, }, }, include: { calendar: true, mailCredential: { select: { id: true, email: true, display_name: true, }, }, }, }); return NextResponse.json({ syncConfigs }); } catch (error) { logger.error('Error fetching sync configs', { error: error instanceof Error ? error.message : String(error), }); return NextResponse.json( { error: "Erreur serveur" }, { status: 500 } ); } } /** * Delete sync configuration * DELETE /api/calendars/sync */ export async function DELETE(req: NextRequest) { try { const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: "Non authentifié" }, { status: 401 }); } const { calendarSyncId } = await req.json(); if (!calendarSyncId) { return NextResponse.json( { error: "calendarSyncId est requis" }, { status: 400 } ); } // Verify sync config belongs to user const syncConfig = await prisma.calendarSync.findUnique({ where: { id: calendarSyncId }, include: { calendar: true, }, }); if (!syncConfig || syncConfig.calendar.userId !== session.user.id) { return NextResponse.json( { error: "Synchronisation non trouvée" }, { status: 404 } ); } await prisma.calendarSync.delete({ where: { id: calendarSyncId }, }); return NextResponse.json({ success: true }); } catch (error) { logger.error('Error deleting sync config', { error: error instanceof Error ? error.message : String(error), }); return NextResponse.json( { error: "Erreur serveur" }, { status: 500 } ); } }