Agenda Sync refactor

This commit is contained in:
alma 2026-01-14 14:17:53 +01:00
parent 760f27cb99
commit de8cbc5824

View File

@ -26,8 +26,8 @@ export async function getInfomaniakCalDAVClient(
email: string, email: string,
password: string password: string
): Promise<WebDAVClient> { ): Promise<WebDAVClient> {
// Infomaniak CalDAV base URL // Infomaniak CalDAV base URL (from Infomaniak sync assistant)
const baseUrl = 'https://sync.infomaniak.com/caldav'; const baseUrl = 'https://sync.infomaniak.com';
const client = createClient(baseUrl, { const client = createClient(baseUrl, {
username: email, username: email,
@ -47,52 +47,92 @@ export async function discoverInfomaniakCalendars(
try { try {
const client = await getInfomaniakCalDAVClient(email, password); const client = await getInfomaniakCalDAVClient(email, password);
// List all calendars using PROPFIND // Try to discover calendar home set using CalDAV discovery
const items = await client.getDirectoryContents('/'); // First, try to find the calendar home set using current-user-principal
let calendarHomePath = '/calendars/';
const calendars: CalDAVCalendar[] = []; // Extract username from email (before @) or use email as username
// Infomaniak might use the email as username or require the Infomaniak username
const username = email.split('@')[0];
for (const item of items) { // Try different paths
if (item.type === 'directory' && item.filename !== '/') { const possiblePaths = [
// Get calendar properties `/calendars/${username}/`,
try { `/calendars/${email}/`,
const props = await client.customRequest(item.filename, { '/calendars/',
method: 'PROPFIND', '/',
headers: { ];
Depth: '0',
'Content-Type': 'application/xml', let calendars: CalDAVCalendar[] = [];
},
data: `<?xml version="1.0" encoding="utf-8" ?> for (const path of possiblePaths) {
try {
// List all calendars using PROPFIND with Depth: 1
const items = await client.getDirectoryContents(path);
for (const item of items) {
if (item.type === 'directory' && item.filename !== '/' && item.filename !== path) {
// Get calendar properties
try {
const props = await client.customRequest(item.filename, {
method: 'PROPFIND',
headers: {
Depth: '0',
'Content-Type': 'application/xml',
},
data: `<?xml version="1.0" encoding="utf-8" ?>
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav"> <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
<d:prop> <d:prop>
<d:displayname /> <d:displayname />
<c:calendar-color /> <c:calendar-color />
<d:resourcetype />
</d:prop> </d:prop>
</d:propfind>`, </d:propfind>`,
}); });
// Parse XML response to extract calendar name and color // Check if it's a calendar (has calendar resource type)
const displayName = extractDisplayName(props.data); const isCalendar = typeof props.data === 'string' &&
const color = extractCalendarColor(props.data); (props.data.includes('<c:calendar') || props.data.includes('calendar'));
calendars.push({ if (isCalendar || item.type === 'directory') {
id: item.filename.replace(/^\//, '').replace(/\/$/, ''), // Parse XML response to extract calendar name and color
name: displayName || item.basename || 'Calendrier', const displayName = extractDisplayName(props.data);
url: item.filename, const color = extractCalendarColor(props.data);
color: color,
}); calendars.push({
} catch (error) { id: item.filename.replace(/^\//, '').replace(/\/$/, ''),
logger.error('Error fetching calendar properties', { name: displayName || item.basename || 'Calendrier',
calendar: item.filename, url: item.filename,
error: error instanceof Error ? error.message : String(error), color: color,
}); });
// Still add the calendar with default name }
calendars.push({ } catch (error) {
id: item.filename.replace(/^\//, '').replace(/\/$/, ''), logger.debug('Error fetching calendar properties', {
name: item.basename || 'Calendrier', calendar: item.filename,
url: item.filename, error: error instanceof Error ? error.message : String(error),
}); });
// Still add the calendar with default name if it's a directory
if (item.type === 'directory') {
calendars.push({
id: item.filename.replace(/^\//, '').replace(/\/$/, ''),
name: item.basename || 'Calendrier',
url: item.filename,
});
}
}
}
} }
// If we found calendars, break
if (calendars.length > 0) {
break;
}
} catch (pathError) {
// Try next path
logger.debug(`Path ${path} failed, trying next`, {
error: pathError instanceof Error ? pathError.message : String(pathError),
});
continue;
} }
} }
@ -102,7 +142,9 @@ export async function discoverInfomaniakCalendars(
email, email,
error: error instanceof Error ? error.message : String(error), error: error instanceof Error ? error.message : String(error),
}); });
throw error; // Ne pas faire échouer toute la page agenda si la découverte échoue
// On retourne simplement une liste vide -> pas de sync auto possible
return [];
} }
} }