Agenda Sync refactor
This commit is contained in:
parent
50cdca1ac2
commit
1bdafdf408
@ -273,10 +273,11 @@ export default async function CalendarPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error auto-setting up sync for Microsoft account ${account.email}:`, error);
|
// Microsoft sync setup failed - likely because account doesn't have calendar scope yet
|
||||||
// Don't fail the page if Microsoft sync setup fails
|
// This is expected for accounts authenticated before calendar scope was added
|
||||||
// The account might not have the calendar scope yet, or there might be a token issue
|
// User will need to re-authenticate their Microsoft account to get calendar access
|
||||||
// User can manually set up sync later if needed
|
console.log(`Microsoft calendar sync not available for ${account.email} - account may need re-authentication with calendar permissions`);
|
||||||
|
// Don't fail the page - continue with other accounts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,92 +47,52 @@ export async function discoverInfomaniakCalendars(
|
|||||||
try {
|
try {
|
||||||
const client = await getInfomaniakCalDAVClient(email, password);
|
const client = await getInfomaniakCalDAVClient(email, password);
|
||||||
|
|
||||||
// Try to discover calendar home set using CalDAV discovery
|
// List all calendars using PROPFIND on root
|
||||||
// First, try to find the calendar home set using current-user-principal
|
const items = await client.getDirectoryContents('/');
|
||||||
let calendarHomePath = '/calendars/';
|
|
||||||
|
|
||||||
// Extract username from email (before @) or use email as username
|
const calendars: CalDAVCalendar[] = [];
|
||||||
// Infomaniak might use the email as username or require the Infomaniak username
|
|
||||||
const username = email.split('@')[0];
|
|
||||||
|
|
||||||
// Try different paths
|
for (const item of items) {
|
||||||
const possiblePaths = [
|
if (item.type === 'directory' && item.filename !== '/') {
|
||||||
`/calendars/${username}/`,
|
// Get calendar properties
|
||||||
`/calendars/${email}/`,
|
try {
|
||||||
'/calendars/',
|
const props = await client.customRequest(item.filename, {
|
||||||
'/',
|
method: 'PROPFIND',
|
||||||
];
|
headers: {
|
||||||
|
Depth: '0',
|
||||||
let calendars: CalDAVCalendar[] = [];
|
'Content-Type': 'application/xml',
|
||||||
|
},
|
||||||
for (const path of possiblePaths) {
|
data: `<?xml version="1.0" encoding="utf-8" ?>
|
||||||
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>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if it's a calendar (has calendar resource type)
|
// Parse XML response to extract calendar name and color
|
||||||
const isCalendar = typeof props.data === 'string' &&
|
const displayName = extractDisplayName(props.data);
|
||||||
(props.data.includes('<c:calendar') || props.data.includes('calendar'));
|
const color = extractCalendarColor(props.data);
|
||||||
|
|
||||||
if (isCalendar || item.type === 'directory') {
|
calendars.push({
|
||||||
// Parse XML response to extract calendar name and color
|
id: item.filename.replace(/^\//, '').replace(/\/$/, ''),
|
||||||
const displayName = extractDisplayName(props.data);
|
name: displayName || item.basename || 'Calendrier',
|
||||||
const color = extractCalendarColor(props.data);
|
url: item.filename,
|
||||||
|
color: color,
|
||||||
calendars.push({
|
});
|
||||||
id: item.filename.replace(/^\//, '').replace(/\/$/, ''),
|
} catch (error) {
|
||||||
name: displayName || item.basename || 'Calendrier',
|
logger.error('Error fetching calendar properties', {
|
||||||
url: item.filename,
|
calendar: item.filename,
|
||||||
color: color,
|
error: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
}
|
// Still add the calendar with default name
|
||||||
} catch (error) {
|
calendars.push({
|
||||||
logger.debug('Error fetching calendar properties', {
|
id: item.filename.replace(/^\//, '').replace(/\/$/, ''),
|
||||||
calendar: item.filename,
|
name: item.basename || 'Calendrier',
|
||||||
error: error instanceof Error ? error.message : String(error),
|
url: item.filename,
|
||||||
});
|
});
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,10 +39,12 @@ async function getMicrosoftGraphClient(
|
|||||||
email: string
|
email: string
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// Ensure we have a fresh access token
|
// Ensure we have a fresh access token
|
||||||
|
// Note: The token might not have calendar scope if the account was authenticated before calendar scope was added
|
||||||
|
// In that case, the user will need to re-authenticate
|
||||||
const { accessToken, success } = await ensureFreshToken(userId, email);
|
const { accessToken, success } = await ensureFreshToken(userId, email);
|
||||||
|
|
||||||
if (!success || !accessToken) {
|
if (!success || !accessToken) {
|
||||||
throw new Error('Failed to obtain valid Microsoft access token');
|
throw new Error('Failed to obtain valid Microsoft access token. The account may need to be re-authenticated with calendar permissions.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessToken;
|
return accessToken;
|
||||||
@ -84,21 +86,31 @@ export async function discoverMicrosoftCalendars(
|
|||||||
|
|
||||||
return calendars;
|
return calendars;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// Check if error is due to missing calendar scope
|
// Check if error is due to missing calendar scope or invalid audience
|
||||||
if (error.response?.status === 403 || error.response?.status === 401) {
|
if (error.response?.status === 403 || error.response?.status === 401) {
|
||||||
logger.warn('Microsoft calendar access denied - may need to re-authenticate with calendar scope', {
|
const errorMessage = error.response?.data?.error?.message || error.message || '';
|
||||||
userId,
|
const needsReauth = errorMessage.includes('Invalid audience') ||
|
||||||
email,
|
errorMessage.includes('insufficient_privileges') ||
|
||||||
error: error.response?.data?.error?.message || error.message,
|
errorMessage.includes('invalid_token');
|
||||||
});
|
|
||||||
// Return empty array instead of throwing - user can re-authenticate later
|
if (needsReauth) {
|
||||||
return [];
|
logger.warn('Microsoft calendar access denied - account needs re-authentication with calendar scope', {
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
error: errorMessage,
|
||||||
|
});
|
||||||
|
// Return empty array - user needs to re-authenticate their Microsoft account
|
||||||
|
// The account was authenticated before calendar scope was added
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.error('Error discovering Microsoft calendars', {
|
logger.error('Error discovering Microsoft calendars', {
|
||||||
userId,
|
userId,
|
||||||
email,
|
email,
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
responseStatus: error.response?.status,
|
||||||
|
responseData: error.response?.data,
|
||||||
});
|
});
|
||||||
// Return empty array instead of throwing to avoid breaking the page
|
// Return empty array instead of throwing to avoid breaking the page
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user