feat: refactor Radicale contact sync logic to include retry mechanism and improve error handling
This commit is contained in:
@@ -138,6 +138,58 @@ export function isRadicaleEnabled() {
|
||||
return getRadicaleConfig() !== null;
|
||||
}
|
||||
|
||||
// Sync contact to Radicale (internal function with retry logic)
|
||||
async function syncContactToRadicaleInternal(contact, forceUpdate = false) {
|
||||
const config = getRadicaleConfig();
|
||||
|
||||
// Skip if not configured
|
||||
if (!config) {
|
||||
return { success: false, reason: 'not_configured' };
|
||||
}
|
||||
|
||||
try {
|
||||
const vcard = generateVCard(contact);
|
||||
const auth = Buffer.from(`${config.username}:${config.password}`).toString('base64');
|
||||
|
||||
// Ensure URL ends with /
|
||||
const baseUrl = config.url.endsWith('/') ? config.url : config.url + '/';
|
||||
|
||||
// Construct the URL for this specific contact
|
||||
const vcardUrl = `${baseUrl}${config.username}/contacts/contact-${contact.contact_id}.vcf`;
|
||||
|
||||
const headers = {
|
||||
'Authorization': `Basic ${auth}`,
|
||||
'Content-Type': 'text/vcard; charset=utf-8'
|
||||
};
|
||||
|
||||
// If not forcing update, only create if doesn't exist
|
||||
if (!forceUpdate) {
|
||||
headers['If-None-Match'] = '*';
|
||||
}
|
||||
|
||||
const response = await fetch(vcardUrl, {
|
||||
method: 'PUT',
|
||||
headers: headers,
|
||||
body: vcard
|
||||
});
|
||||
|
||||
// Handle conflict - try again with force update
|
||||
if (response.status === 412 || response.status === 409) {
|
||||
// Conflict - contact already exists, update it instead
|
||||
return await syncContactToRadicaleInternal(contact, true);
|
||||
}
|
||||
|
||||
if (response.ok || response.status === 201 || response.status === 204) {
|
||||
return { success: true, status: response.status, updated: forceUpdate };
|
||||
} else {
|
||||
const text = await response.text();
|
||||
return { success: false, status: response.status, error: text };
|
||||
}
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// Sync contact to Radicale
|
||||
export async function syncContactToRadicale(contact) {
|
||||
const config = getRadicaleConfig();
|
||||
@@ -154,37 +206,16 @@ export async function syncContactToRadicale(contact) {
|
||||
return { success: true, reason: 'inactive_skipped' };
|
||||
}
|
||||
|
||||
try {
|
||||
const vcard = generateVCard(contact);
|
||||
const auth = Buffer.from(`${config.username}:${config.password}`).toString('base64');
|
||||
|
||||
// Ensure URL ends with /
|
||||
const baseUrl = config.url.endsWith('/') ? config.url : config.url + '/';
|
||||
|
||||
// Construct the URL for this specific contact
|
||||
const vcardUrl = `${baseUrl}${config.username}/contacts/contact-${contact.contact_id}.vcf`;
|
||||
|
||||
const response = await fetch(vcardUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Basic ${auth}`,
|
||||
'Content-Type': 'text/vcard; charset=utf-8'
|
||||
},
|
||||
body: vcard
|
||||
});
|
||||
|
||||
if (response.ok || response.status === 201 || response.status === 204) {
|
||||
console.log(`✅ Synced contact ${contact.contact_id} to Radicale`);
|
||||
return { success: true, status: response.status };
|
||||
} else {
|
||||
const text = await response.text();
|
||||
console.error(`❌ Failed to sync contact ${contact.contact_id} to Radicale: ${response.status} - ${text}`);
|
||||
return { success: false, status: response.status, error: text };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ Error syncing contact ${contact.contact_id} to Radicale:`, error);
|
||||
return { success: false, error: error.message };
|
||||
const result = await syncContactToRadicaleInternal(contact);
|
||||
|
||||
if (result.success) {
|
||||
const action = result.updated ? 'updated' : 'created';
|
||||
console.log(`✅ Synced contact ${contact.contact_id} to Radicale (${action})`);
|
||||
} else if (result.reason !== 'not_configured') {
|
||||
console.error(`❌ Failed to sync contact ${contact.contact_id} to Radicale: ${result.status} - ${result.error || result.reason}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Delete contact from Radicale
|
||||
@@ -229,6 +260,7 @@ export async function deleteContactFromRadicale(contactId) {
|
||||
|
||||
// Sync contact asynchronously (fire and forget)
|
||||
export function syncContactAsync(contact) {
|
||||
console.log(contact, isRadicaleEnabled());
|
||||
if (!isRadicaleEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user