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 { deleteMicrosoftEvent } from "@/lib/services/microsoft-calendar-sync"; import { logger } from "@/lib/logger"; // Helper function to check if user can manage events in a mission calendar async function canManageMissionCalendar(userId: string, calendarId: string): Promise { const calendar = await prisma.calendar.findUnique({ where: { id: calendarId }, include: { mission: { include: { missionUsers: true } } } }); // If calendar is not linked to a mission, allow (it's a regular calendar) if (!calendar?.missionId || !calendar.mission) { return true; } const mission = calendar.mission; // Check if user is the creator of the mission if (mission.creatorId === userId) { return true; } // Check if user is a gardien-temps (time guardian) for this mission const isGardienTemps = mission.missionUsers.some( (mu) => mu.userId === userId && mu.role === 'gardien-temps' ); return isGardienTemps; } export async function DELETE(req: NextRequest, props: { params: Promise<{ id: string }> }) { const params = await props.params; const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: "Non authentifié" }, { status: 401 }); } try { // First, find the event and its associated calendar with sync config const event = await prisma.event.findUnique({ where: { id: params.id }, include: { calendar: { include: { syncConfig: { include: { mailCredential: true } } } } }, }); if (!event) { return NextResponse.json( { error: "Événement non trouvé" }, { status: 404 } ); } // Verify that the user owns the calendar if (event.calendar.userId !== session.user.id) { return NextResponse.json( { error: "Non autorisé" }, { status: 403 } ); } // If calendar is linked to a mission, check permissions if (event.calendar.missionId) { const canManage = await canManageMissionCalendar(session.user.id, event.calendarId); if (!canManage) { return NextResponse.json( { error: "Seul le créateur de la mission ou le gardien-temps peut supprimer des événements" }, { status: 403 } ); } } // If event has externalEventId and calendar has Microsoft sync, delete from Microsoft too if (event.externalEventId && event.calendar.syncConfig && event.calendar.syncConfig.provider === 'microsoft' && event.calendar.syncConfig.syncEnabled) { const syncConfig = event.calendar.syncConfig; const mailCredential = syncConfig.mailCredential; if (mailCredential && mailCredential.use_oauth && mailCredential.refresh_token) { try { await deleteMicrosoftEvent( session.user.id, mailCredential.email, syncConfig.externalCalendarId || '', event.externalEventId ); logger.info('Successfully synced event deletion to Microsoft', { eventId: params.id, externalEventId: event.externalEventId, email: mailCredential.email, }); } catch (syncError: any) { // Log error but don't fail the request - local deletion will proceed // Don't disable syncConfig for permission errors (403) - user just needs to re-authenticate const isPermissionError = syncError.response?.status === 403; logger.error('Failed to sync event deletion to Microsoft', { eventId: params.id, externalEventId: event.externalEventId, error: syncError instanceof Error ? syncError.message : String(syncError), isPermissionError, suggestion: isPermissionError ? 'User needs to re-authenticate with Calendars.ReadWrite scope' : undefined, }); } } } // Delete the event from local database await prisma.event.delete({ where: { id: params.id }, }); // Invalidate calendar cache so the deletion appears immediately try { const { invalidateCalendarCache } = await import('@/lib/redis'); await invalidateCalendarCache(session.user.id); logger.debug('[EVENTS] Invalidated calendar cache after event deletion', { eventId: params.id, userId: session.user.id, }); } catch (cacheError) { // Log but don't fail the request if cache invalidation fails logger.error('[EVENTS] Error invalidating calendar cache', { error: cacheError instanceof Error ? cacheError.message : String(cacheError), }); } return new NextResponse(null, { status: 204 }); } catch (error) { console.error("Erreur lors de la suppression de l'événement:", error); return NextResponse.json( { error: "Erreur serveur" }, { status: 500 } ); } }