Agenda refactor
This commit is contained in:
parent
95a56d8828
commit
a4c4baa491
@ -2,6 +2,8 @@ import { NextRequest, NextResponse } from "next/server";
|
|||||||
import { getServerSession } from "next-auth/next";
|
import { getServerSession } from "next-auth/next";
|
||||||
import { authOptions } from "@/app/api/auth/options";
|
import { authOptions } from "@/app/api/auth/options";
|
||||||
import { prisma } from "@/lib/prisma";
|
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
|
// Helper function to check if user can manage events in a mission calendar
|
||||||
async function canManageMissionCalendar(userId: string, calendarId: string): Promise<boolean> {
|
async function canManageMissionCalendar(userId: string, calendarId: string): Promise<boolean> {
|
||||||
@ -45,10 +47,20 @@ export async function DELETE(req: NextRequest, props: { params: Promise<{ id: st
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// First, find the event and its associated calendar
|
// First, find the event and its associated calendar with sync config
|
||||||
const event = await prisma.event.findUnique({
|
const event = await prisma.event.findUnique({
|
||||||
where: { id: params.id },
|
where: { id: params.id },
|
||||||
include: { calendar: true },
|
include: {
|
||||||
|
calendar: {
|
||||||
|
include: {
|
||||||
|
syncConfig: {
|
||||||
|
include: {
|
||||||
|
mailCredential: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
@ -77,7 +89,41 @@ export async function DELETE(req: NextRequest, props: { params: Promise<{ id: st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the event
|
// 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({
|
await prisma.event.delete({
|
||||||
where: { id: params.id },
|
where: { id: params.id },
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
|
|||||||
import { getServerSession } from "next-auth/next";
|
import { getServerSession } from "next-auth/next";
|
||||||
import { authOptions } from "@/app/api/auth/options";
|
import { authOptions } from "@/app/api/auth/options";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
import { updateMicrosoftEvent } from "@/lib/services/microsoft-calendar-sync";
|
import { updateMicrosoftEvent, deleteMicrosoftEvent } from "@/lib/services/microsoft-calendar-sync";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
|
|
||||||
// Helper function to check if user can manage events in a mission calendar
|
// Helper function to check if user can manage events in a mission calendar
|
||||||
@ -263,10 +263,14 @@ export async function PUT(req: NextRequest) {
|
|||||||
});
|
});
|
||||||
} catch (syncError: any) {
|
} catch (syncError: any) {
|
||||||
// Log error but don't fail the request - local update succeeded
|
// Log error but don't fail the request - local update succeeded
|
||||||
|
// 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 update to Microsoft', {
|
logger.error('Failed to sync event update to Microsoft', {
|
||||||
eventId: id,
|
eventId: id,
|
||||||
externalEventId: existingEvent.externalEventId,
|
externalEventId: existingEvent.externalEventId,
|
||||||
error: syncError instanceof Error ? syncError.message : String(syncError),
|
error: syncError instanceof Error ? syncError.message : String(syncError),
|
||||||
|
isPermissionError,
|
||||||
|
suggestion: isPermissionError ? 'User needs to re-authenticate with Calendars.ReadWrite scope' : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -448,6 +448,54 @@ export async function updateMicrosoftEvent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a Microsoft calendar event via Graph API
|
||||||
|
*/
|
||||||
|
export async function deleteMicrosoftEvent(
|
||||||
|
userId: string,
|
||||||
|
email: string,
|
||||||
|
calendarId: string,
|
||||||
|
eventId: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
const accessToken = await getMicrosoftGraphClient(userId, email);
|
||||||
|
|
||||||
|
const url = `https://graph.microsoft.com/v1.0/me/calendars/${calendarId}/events/${eventId}`;
|
||||||
|
|
||||||
|
logger.info('Deleting Microsoft event', {
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
calendarId,
|
||||||
|
eventId,
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
|
||||||
|
await axios.delete(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('Successfully deleted Microsoft event', {
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
eventId,
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.error('Error deleting Microsoft event', {
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
calendarId,
|
||||||
|
eventId,
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
responseStatus: error.response?.status,
|
||||||
|
responseData: error.response?.data,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync events from Microsoft calendar to local Prisma calendar
|
* Sync events from Microsoft calendar to local Prisma calendar
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -21,6 +21,7 @@ const REQUIRED_SCOPES = [
|
|||||||
'https://graph.microsoft.com/Mail.Read', // Read mail via Graph API
|
'https://graph.microsoft.com/Mail.Read', // Read mail via Graph API
|
||||||
'https://graph.microsoft.com/Mail.Send', // Send mail via Graph API
|
'https://graph.microsoft.com/Mail.Send', // Send mail via Graph API
|
||||||
'https://graph.microsoft.com/Calendars.Read', // Read calendars via Graph API
|
'https://graph.microsoft.com/Calendars.Read', // Read calendars via Graph API
|
||||||
|
'https://graph.microsoft.com/Calendars.ReadWrite', // Read and write calendars via Graph API (for updates/deletes)
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user