Agenda refactor
This commit is contained in:
parent
5d90bc6989
commit
5a4e746bf4
@ -131,6 +131,50 @@ export default async function CalendarPage() {
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up orphaned syncs FIRST (before creating new ones)
|
||||
// This handles the case where a user deleted and re-added an email account
|
||||
const allMailCredentialIds = new Set([
|
||||
...infomaniakAccounts.map(acc => acc.id),
|
||||
...microsoftAccounts.map(acc => acc.id)
|
||||
]);
|
||||
|
||||
const orphanedSyncs = await prisma.calendarSync.findMany({
|
||||
where: {
|
||||
calendar: {
|
||||
userId: session?.user?.id || ''
|
||||
},
|
||||
mailCredentialId: {
|
||||
not: null
|
||||
}
|
||||
},
|
||||
include: {
|
||||
calendar: true,
|
||||
mailCredential: true
|
||||
}
|
||||
});
|
||||
|
||||
// Delete syncs where mailCredential no longer exists
|
||||
for (const sync of orphanedSyncs) {
|
||||
if (sync.mailCredentialId && !allMailCredentialIds.has(sync.mailCredentialId)) {
|
||||
console.log(`[AGENDA] Deleting orphaned sync for non-existent mailCredentialId: ${sync.mailCredentialId}`);
|
||||
// Delete the calendar if it has no events
|
||||
const eventCount = await prisma.event.count({
|
||||
where: { calendarId: sync.calendarId }
|
||||
});
|
||||
if (eventCount === 0) {
|
||||
await prisma.calendar.delete({
|
||||
where: { id: sync.calendarId }
|
||||
});
|
||||
} else {
|
||||
// Just disable the sync, keep the calendar
|
||||
await prisma.calendarSync.update({
|
||||
where: { id: sync.id },
|
||||
data: { syncEnabled: false }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each Infomaniak account, ensure there's a synced calendar
|
||||
// Skip if no Infomaniak accounts exist (user may only have Microsoft accounts)
|
||||
if (infomaniakAccounts.length > 0) {
|
||||
@ -148,18 +192,46 @@ export default async function CalendarPage() {
|
||||
|
||||
// If sync exists but is disabled, check if it's due to invalid credentials
|
||||
// Don't re-enable if the last error was 401 (invalid credentials)
|
||||
if (existingSync && !existingSync.syncEnabled) {
|
||||
const isAuthError = existingSync.lastSyncError?.includes('401') ||
|
||||
existingSync.lastSyncError?.includes('Unauthorized') ||
|
||||
existingSync.lastSyncError?.includes('invalid');
|
||||
if (existingSync) {
|
||||
console.log(`[AGENDA] Found existing sync for Infomaniak account ${account.email}: syncId=${existingSync.id}, calendarId=${existingSync.calendarId}, syncEnabled=${existingSync.syncEnabled}, hasCalendar=${!!existingSync.calendar}`);
|
||||
|
||||
if (!isAuthError) {
|
||||
// Only re-enable if it's not an authentication error
|
||||
// Check if calendar still exists
|
||||
if (!existingSync.calendar) {
|
||||
console.log(`[AGENDA] Calendar for sync ${existingSync.id} does not exist, creating new calendar`);
|
||||
// Calendar was deleted, create a new one
|
||||
const calendar = await prisma.calendar.create({
|
||||
data: {
|
||||
name: "Privée",
|
||||
color: "#4F46E5",
|
||||
description: `Calendrier synchronisé avec ${account.display_name || account.email}`,
|
||||
userId: session?.user?.id || '',
|
||||
}
|
||||
});
|
||||
|
||||
// Update sync to point to new calendar
|
||||
await prisma.calendarSync.update({
|
||||
where: { id: existingSync.id },
|
||||
data: { syncEnabled: true }
|
||||
data: {
|
||||
calendarId: calendar.id,
|
||||
syncEnabled: true
|
||||
}
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!existingSync.syncEnabled) {
|
||||
const isAuthError = existingSync.lastSyncError?.includes('401') ||
|
||||
existingSync.lastSyncError?.includes('Unauthorized') ||
|
||||
existingSync.lastSyncError?.includes('invalid');
|
||||
|
||||
if (!isAuthError) {
|
||||
// Only re-enable if it's not an authentication error
|
||||
console.log(`[AGENDA] Re-enabling sync ${existingSync.id} for Infomaniak account ${account.email}`);
|
||||
await prisma.calendarSync.update({
|
||||
where: { id: existingSync.id },
|
||||
data: { syncEnabled: true }
|
||||
});
|
||||
} else {
|
||||
// Try to discover calendars to verify if credentials are now valid
|
||||
// But if discovery fails and we have an existing URL, re-enable sync anyway
|
||||
// The existing URL might still work even if discovery fails
|
||||
@ -221,18 +293,23 @@ export default async function CalendarPage() {
|
||||
}
|
||||
|
||||
if (!existingSync) {
|
||||
// Try to discover calendars for this account
|
||||
// No sync exists for this account - try to discover and create calendar
|
||||
// Only create calendar if discovery succeeds
|
||||
try {
|
||||
const { discoverInfomaniakCalendars } = await import('@/lib/services/caldav-sync');
|
||||
const externalCalendars = await discoverInfomaniakCalendars(
|
||||
account.email,
|
||||
account.password!
|
||||
);
|
||||
|
||||
console.log(`[AGENDA] Discovered ${externalCalendars.length} calendars for Infomaniak account ${account.email}`);
|
||||
|
||||
if (externalCalendars.length > 0) {
|
||||
// Use the first calendar (usually the main calendar)
|
||||
const mainCalendar = externalCalendars[0];
|
||||
|
||||
console.log(`[AGENDA] Creating Infomaniak calendar for ${account.email} with URL: ${mainCalendar.url}`);
|
||||
|
||||
// Create a private calendar for this account
|
||||
const calendar = await prisma.calendar.create({
|
||||
data: {
|
||||
@ -244,7 +321,7 @@ export default async function CalendarPage() {
|
||||
});
|
||||
|
||||
// Create sync configuration
|
||||
await prisma.calendarSync.create({
|
||||
const syncConfig = await prisma.calendarSync.create({
|
||||
data: {
|
||||
calendarId: calendar.id,
|
||||
mailCredentialId: account.id,
|
||||
@ -256,50 +333,28 @@ export default async function CalendarPage() {
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[AGENDA] Created Infomaniak calendar sync: ${syncConfig.id} for calendar: ${calendar.id}`);
|
||||
|
||||
// Trigger initial sync
|
||||
try {
|
||||
const { syncInfomaniakCalendar } = await import('@/lib/services/caldav-sync');
|
||||
const syncConfig = await prisma.calendarSync.findUnique({
|
||||
where: { calendarId: calendar.id },
|
||||
include: {
|
||||
calendar: true,
|
||||
mailCredential: true
|
||||
await syncInfomaniakCalendar(syncConfig.id, true);
|
||||
console.log(`[AGENDA] Initial sync completed for Infomaniak calendar: ${calendar.id}`);
|
||||
} catch (syncError) {
|
||||
const syncErrorMessage = syncError instanceof Error ? syncError.message : 'Unknown error';
|
||||
console.log(`[AGENDA] Initial sync failed for Infomaniak calendar: ${calendar.id} - ${syncErrorMessage}`);
|
||||
await prisma.calendarSync.update({
|
||||
where: { id: syncConfig.id },
|
||||
data: {
|
||||
lastSyncError: `Erreur de synchronisation: ${syncErrorMessage}`
|
||||
}
|
||||
});
|
||||
if (syncConfig) {
|
||||
await syncInfomaniakCalendar(syncConfig.id, true);
|
||||
}
|
||||
} catch (syncError) {
|
||||
console.error('Error during initial sync:', syncError);
|
||||
// Don't fail if sync fails, calendar is still created
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Log error but don't fail the page - account may not have calendar access or credentials may be invalid
|
||||
// Discovery failed - don't create calendar
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
console.log(`Infomaniak calendar sync not available for ${account.email} - ${errorMessage}`);
|
||||
|
||||
// If it's a 401 error, the credentials are likely invalid - update lastSyncError in existing sync if any
|
||||
if (errorMessage.includes('401') || errorMessage.includes('Unauthorized')) {
|
||||
// Check if there's a disabled sync for this account
|
||||
const disabledSync = await prisma.calendarSync.findFirst({
|
||||
where: {
|
||||
mailCredentialId: account.id,
|
||||
provider: 'infomaniak',
|
||||
syncEnabled: false
|
||||
}
|
||||
});
|
||||
|
||||
if (disabledSync) {
|
||||
// Update the error message
|
||||
await prisma.calendarSync.update({
|
||||
where: { id: disabledSync.id },
|
||||
data: {
|
||||
lastSyncError: `Identifiants invalides ou expirés (401 Unauthorized). Veuillez vérifier vos identifiants Infomaniak dans la page courrier.`
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(`[AGENDA] Infomaniak calendar discovery failed for ${account.email} - ${errorMessage}. Calendar will not be created.`);
|
||||
// Continue with other accounts even if one fails
|
||||
}
|
||||
}
|
||||
@ -392,12 +447,59 @@ export default async function CalendarPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up orphaned syncs (syncs with mailCredentialId that no longer exists)
|
||||
// This can happen when a user deletes and re-adds an email account
|
||||
const allMailCredentialIds = new Set([
|
||||
...infomaniakAccounts.map(acc => acc.id),
|
||||
...microsoftAccounts.map(acc => acc.id)
|
||||
]);
|
||||
|
||||
const orphanedSyncs = await prisma.calendarSync.findMany({
|
||||
where: {
|
||||
calendar: {
|
||||
userId: session?.user?.id || ''
|
||||
},
|
||||
mailCredentialId: {
|
||||
not: null
|
||||
}
|
||||
},
|
||||
include: {
|
||||
calendar: true,
|
||||
mailCredential: true
|
||||
}
|
||||
});
|
||||
|
||||
// Delete syncs where mailCredential no longer exists
|
||||
for (const sync of orphanedSyncs) {
|
||||
if (sync.mailCredentialId && !allMailCredentialIds.has(sync.mailCredentialId)) {
|
||||
console.log(`[AGENDA] Deleting orphaned sync for non-existent mailCredentialId: ${sync.mailCredentialId}`);
|
||||
// Delete the calendar if it has no events
|
||||
const eventCount = await prisma.event.count({
|
||||
where: { calendarId: sync.calendarId }
|
||||
});
|
||||
if (eventCount === 0) {
|
||||
await prisma.calendar.delete({
|
||||
where: { id: sync.calendarId }
|
||||
});
|
||||
} else {
|
||||
// Just disable the sync, keep the calendar
|
||||
await prisma.calendarSync.update({
|
||||
where: { id: sync.id },
|
||||
data: { syncEnabled: false }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up duplicate calendars for the same mailCredentialId
|
||||
// Keep only the most recent one with syncEnabled=true, delete others
|
||||
const allSyncs = await prisma.calendarSync.findMany({
|
||||
where: {
|
||||
calendar: {
|
||||
userId: session?.user?.id || ''
|
||||
},
|
||||
mailCredentialId: {
|
||||
in: Array.from(allMailCredentialIds)
|
||||
}
|
||||
},
|
||||
include: {
|
||||
@ -507,6 +609,24 @@ export default async function CalendarPage() {
|
||||
|
||||
// No default calendar creation - only synced calendars from courrier
|
||||
|
||||
// Debug: Verify Infomaniak calendars exist in database
|
||||
const allInfomaniakSyncs = await prisma.calendarSync.findMany({
|
||||
where: {
|
||||
provider: 'infomaniak',
|
||||
calendar: {
|
||||
userId: session?.user?.id || ''
|
||||
}
|
||||
},
|
||||
include: {
|
||||
calendar: true,
|
||||
mailCredential: true
|
||||
}
|
||||
});
|
||||
console.log(`[AGENDA] Found ${allInfomaniakSyncs.length} Infomaniak syncs in database`);
|
||||
allInfomaniakSyncs.forEach(sync => {
|
||||
console.log(`[AGENDA] Infomaniak sync: id=${sync.id}, calendarId=${sync.calendarId}, calendarName=${sync.calendar?.name}, syncEnabled=${sync.syncEnabled}, mailCredentialId=${sync.mailCredentialId}, hasMailCredential=${!!sync.mailCredential}`);
|
||||
});
|
||||
|
||||
// Debug: Log calendars before filtering
|
||||
console.log(`[AGENDA] Calendars before filtering: ${calendars.length}`);
|
||||
const infomaniakBeforeFilter = calendars.filter(cal => cal.syncConfig?.provider === 'infomaniak');
|
||||
|
||||
@ -48,8 +48,40 @@ export async function discoverInfomaniakCalendars(
|
||||
try {
|
||||
const client = await getInfomaniakCalDAVClient(email, password);
|
||||
|
||||
// List all calendars using PROPFIND on /caldav path
|
||||
const items = await client.getDirectoryContents('/caldav');
|
||||
// List all calendars using PROPFIND
|
||||
// Try different paths: root, /caldav, /calendars/{username}
|
||||
let items;
|
||||
let triedPaths: string[] = [];
|
||||
|
||||
// Try root path first
|
||||
try {
|
||||
logger.debug('Trying CalDAV discovery on root path /');
|
||||
items = await client.getDirectoryContents('/');
|
||||
logger.debug(`CalDAV discovery succeeded on root path, found ${items.length} items`);
|
||||
} catch (rootError) {
|
||||
triedPaths.push('/');
|
||||
logger.debug('Root path failed, trying /caldav path');
|
||||
|
||||
// Try /caldav path
|
||||
try {
|
||||
items = await client.getDirectoryContents('/caldav');
|
||||
logger.debug(`CalDAV discovery succeeded on /caldav path, found ${items.length} items`);
|
||||
} catch (caldavError) {
|
||||
triedPaths.push('/caldav');
|
||||
|
||||
// Try /calendars/{username} path
|
||||
const username = email.split('@')[0];
|
||||
const calendarsPath = `/calendars/${username}`;
|
||||
logger.debug(`Trying CalDAV discovery on ${calendarsPath} path`);
|
||||
try {
|
||||
items = await client.getDirectoryContents(calendarsPath);
|
||||
logger.debug(`CalDAV discovery succeeded on ${calendarsPath} path, found ${items.length} items`);
|
||||
} catch (calendarsError) {
|
||||
triedPaths.push(calendarsPath);
|
||||
throw new Error(`CalDAV discovery failed on all paths (${triedPaths.join(', ')}). Last error: ${calendarsError instanceof Error ? calendarsError.message : String(calendarsError)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const calendars: CalDAVCalendar[] = [];
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user