Files
panel/RADICALE_SYNC_README.md

4.0 KiB

Radicale CardDAV Sync Integration

This application now automatically syncs contacts to a Radicale CardDAV server whenever contacts are created, updated, or deleted.

Features

  • Automatic Sync - Contacts are automatically synced when created or updated
  • Automatic Deletion - Contacts are removed from Radicale when soft/hard deleted
  • Non-Blocking - Sync happens asynchronously without slowing down the API
  • Optional - Sync is disabled by default, enable by configuring environment variables
  • VCARD 3.0 - Generates standard VCARD format with full contact details

Setup

1. Configure Environment Variables

Add these to your .env.local or production environment:

RADICALE_URL=http://localhost:5232
RADICALE_USERNAME=your_username
RADICALE_PASSWORD=your_password

Note: If these variables are not set, sync will be disabled and the app will work normally.

2. Radicale Server Setup

Make sure your Radicale server:

  • Is accessible from your application server
  • Has a user created with the credentials you configured
  • Has a contacts collection at: {username}/contacts/

3. One-Time Initial Sync

To sync all existing contacts to Radicale:

node export-contacts-to-radicale.mjs

This script will:

  • Prompt for Radicale URL, username, and password
  • Export all active contacts as VCARDs
  • Upload them to your Radicale server

How It Works

When Creating a Contact

// POST /api/contacts
const contact = createContact(data);

// Sync to Radicale asynchronously (non-blocking)
syncContactAsync(contact);

return NextResponse.json(contact);

When Updating a Contact

// PUT /api/contacts/[id]
const contact = updateContact(contactId, data);

// Sync updated contact to Radicale
syncContactAsync(contact);

return NextResponse.json(contact);

When Deleting a Contact

// DELETE /api/contacts/[id]
deleteContact(contactId);

// Delete from Radicale asynchronously
deleteContactAsync(contactId);

return NextResponse.json({ message: "Contact deleted" });

VCARD Format

Each contact is exported with the following fields:

  • UID: contact-{id}@panel-app
  • FN/N: Full name and structured name
  • ORG: Company
  • TITLE: Position/Title
  • TEL: Phone numbers (multiple supported - first as WORK, others as CELL)
  • EMAIL: Email address
  • NOTE: Contact type + notes
  • CATEGORIES: Based on contact type (Projekty, Wykonawcy, Urzędy, etc.)
  • REV: Last modified timestamp

VCARD Storage Path

VCARDs are stored at:

{RADICALE_URL}/{RADICALE_USERNAME}/contacts/contact-{id}.vcf

Example:

http://localhost:5232/admin/contacts/contact-123.vcf

Troubleshooting

Sync Not Working

  1. Check environment variables are set correctly
  2. Verify Radicale server is accessible
  3. Check application logs for sync errors
  4. Test manually with the export script

Check Sync Status

Sync operations are logged to console:

✅ Synced contact 123 to Radicale
❌ Failed to sync contact 456 to Radicale: 401 - Unauthorized

Disable Sync

Simply remove or comment out the Radicale environment variables:

# RADICALE_URL=
# RADICALE_USERNAME=
# RADICALE_PASSWORD=

Files

  • src/lib/radicale-sync.js - Main sync utility with VCARD generation
  • src/app/api/contacts/route.js - Integrated sync on create
  • src/app/api/contacts/[id]/route.js - Integrated sync on update/delete
  • export-contacts-to-radicale.mjs - One-time bulk export script

Security Notes

  • ⚠️ Store credentials securely in environment variables
  • ⚠️ Use HTTPS for production Radicale servers
  • ⚠️ Consider using environment-specific credentials
  • ⚠️ Sync happens in background - errors won't block API responses

Future Enhancements

  • Bi-directional sync (import changes from Radicale)
  • Batch sync operations
  • Sync queue with retry logic
  • Webhook notifications for sync status
  • Admin UI to trigger manual sync