NeahStable/app/api/calendars/sync/route.ts
2026-01-14 21:16:08 +01:00

289 lines
8.1 KiB
TypeScript

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 }
);
}
}