Agenda refactor

This commit is contained in:
alma 2026-01-15 12:52:50 +01:00
parent 946a110054
commit fd24dfbd21
2 changed files with 96 additions and 34 deletions

View File

@ -242,35 +242,65 @@ export default async function CalendarPage() {
console.log(`[AGENDA] Invalid calendar URL detected (${existingSync.externalCalendarUrl}), attempting to rediscover...`);
try {
const { discoverInfomaniakCalendars } = await import('@/lib/services/caldav-sync');
const externalCalendars = await discoverInfomaniakCalendars(
account.email,
account.password!
);
if (externalCalendars.length > 0) {
const mainCalendar = externalCalendars[0];
console.log(`[AGENDA] Updating sync with correct calendar URL: ${mainCalendar.url}`);
await prisma.calendarSync.update({
where: { id: existingSync.id },
data: {
externalCalendarId: mainCalendar.id,
externalCalendarUrl: mainCalendar.url,
lastSyncError: null, // Clear error since we're fixing the URL
}
});
// Reload the sync config
const updatedSync = await prisma.calendarSync.findUnique({
where: { id: existingSync.id },
include: { calendar: true }
});
if (updatedSync) {
existingSync = updatedSync;
}
if (!account.password) {
console.error(`[AGENDA] Cannot rediscover calendars: missing password for ${account.email}`);
} else {
console.log(`[AGENDA] No calendars found during rediscovery for ${account.email}`);
const externalCalendars = await discoverInfomaniakCalendars(
account.email,
account.password
);
console.log(`[AGENDA] Rediscovery result: found ${externalCalendars.length} calendars for ${account.email}`);
if (externalCalendars.length > 0) {
const mainCalendar = externalCalendars[0];
console.log(`[AGENDA] Updating sync with correct calendar URL: ${mainCalendar.url} (name: ${mainCalendar.name})`);
await prisma.calendarSync.update({
where: { id: existingSync.id },
data: {
externalCalendarId: mainCalendar.id,
externalCalendarUrl: mainCalendar.url,
lastSyncError: null, // Clear error since we're fixing the URL
}
});
// Reload the sync config
const updatedSync = await prisma.calendarSync.findUnique({
where: { id: existingSync.id },
include: { calendar: true }
});
if (updatedSync) {
existingSync = updatedSync;
console.log(`[AGENDA] Sync config updated successfully, new URL: ${updatedSync.externalCalendarUrl}`);
}
} else {
console.warn(`[AGENDA] No calendars found during rediscovery for ${account.email}. This may indicate:`);
console.warn(` - Authentication issue (wrong password)`);
console.warn(` - No calendars exist for this account`);
console.warn(` - CalDAV server issue`);
// Mark sync as having an error so user knows something is wrong
await prisma.calendarSync.update({
where: { id: existingSync.id },
data: {
lastSyncError: `No calendars found during rediscovery. Please check your Infomaniak account credentials and calendar access.`,
}
});
}
}
} catch (rediscoverError) {
console.error(`[AGENDA] Error rediscovering calendars:`, rediscoverError);
} catch (rediscoverError: any) {
console.error(`[AGENDA] Error rediscovering calendars for ${account.email}:`, {
error: rediscoverError?.message || String(rediscoverError),
stack: rediscoverError?.stack?.substring(0, 300),
});
// Mark sync as having an error
await prisma.calendarSync.update({
where: { id: existingSync.id },
data: {
lastSyncError: `Rediscovery failed: ${rediscoverError?.message || 'Unknown error'}`,
}
}).catch(() => {
// Ignore update errors
});
}
}

View File

@ -48,18 +48,31 @@ export async function discoverInfomaniakCalendars(
const client = await getInfomaniakCalDAVClient(email, password);
// List all calendars using PROPFIND on root
logger.debug('Discovering Infomaniak calendars', { email });
const items = await client.getDirectoryContents('/');
logger.debug('Found items in root directory', {
email,
itemsCount: items.length,
items: items.map(item => ({ filename: item.filename, type: item.type })),
});
const calendars: CalDAVCalendar[] = [];
for (const item of items) {
// Skip non-directories, root, and special directories like /principals
if (item.type !== 'directory' || item.filename === '/' || item.filename === '/principals') {
logger.debug('Skipping item', {
filename: item.filename,
type: item.type,
reason: item.type !== 'directory' ? 'not a directory' : 'special directory',
});
continue;
}
// Get calendar properties to verify it's actually a calendar
try {
logger.debug('Checking if item is a calendar', { filename: item.filename });
const props = await client.customRequest(item.filename, {
method: 'PROPFIND',
headers: {
@ -77,11 +90,23 @@ export async function discoverInfomaniakCalendars(
});
// Check if this is actually a calendar (has <c:calendar> in resourcetype)
const isCalendar = props.data && props.data.includes('<c:calendar');
// Try multiple patterns to be more flexible with XML namespaces
const dataStr = props.data || '';
const isCalendar = dataStr.includes('<c:calendar') ||
dataStr.includes('calendar') ||
dataStr.includes('urn:ietf:params:xml:ns:caldav');
logger.debug('Calendar check result', {
filename: item.filename,
isCalendar,
hasData: !!props.data,
dataPreview: props.data ? props.data.substring(0, 500) : 'no data',
});
if (!isCalendar) {
logger.debug('Skipping non-calendar directory', {
filename: item.filename,
reason: 'resourcetype does not indicate calendar',
});
continue;
}
@ -90,12 +115,15 @@ export async function discoverInfomaniakCalendars(
const displayName = extractDisplayName(props.data);
const color = extractCalendarColor(props.data);
calendars.push({
const calendar = {
id: item.filename.replace(/^\//, '').replace(/\/$/, ''),
name: displayName || item.basename || 'Calendrier',
url: item.filename,
color: color,
});
};
logger.debug('Found valid calendar', calendar);
calendars.push(calendar);
} catch (error) {
logger.error('Error fetching calendar properties', {
calendar: item.filename,
@ -105,19 +133,23 @@ export async function discoverInfomaniakCalendars(
}
}
logger.info('Infomaniak calendar discovery completed', {
email,
calendarsFound: calendars.length,
calendars: calendars.map(cal => ({ id: cal.id, name: cal.name, url: cal.url })),
});
return calendars;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorDetails = error instanceof Error ? {
name: error.name,
message: error.message,
stack: error.stack?.substring(0, 200), // First 200 chars of stack
stack: error.stack?.substring(0, 500), // More stack for debugging
} : { raw: String(error) };
// Use logger.log instead of logger.error for non-critical errors
// This prevents console.error from showing up in the browser console
// The error is still logged server-side but won't appear as a red error in the browser
logger.log('info', 'Infomaniak calendar discovery failed (non-critical)', {
// Log as error for debugging, but don't throw to avoid breaking the page
logger.error('Infomaniak calendar discovery failed', {
email,
error: errorMessage,
errorDetails,