- Added new `contacts` and `project_contacts` database tables for managing contacts. - Created API endpoints for CRUD operations on contacts and linking them to projects. - Developed UI components including `ContactForm` and `ProjectContactSelector`. - Integrated navigation and translations for Polish language support. - Documented usage, features, and future enhancements for the contacts system. feat: Introduce DOCX Template System for generating documents from templates - Enabled creation and management of DOCX templates with placeholders for project data. - Documented the process for creating templates, uploading, and generating documents. - Included detailed information on available variables and custom data fields. - Implemented troubleshooting guidelines for common issues related to template generation. feat: Add Radicale CardDAV Sync Integration for automatic contact synchronization - Implemented automatic syncing of contacts to a Radicale server on create/update/delete actions. - Documented setup instructions, including environment variable configuration and initial sync script. - Provided troubleshooting steps for common sync issues and error codes. feat: Develop Route Planning Feature with Optimization using OpenRouteService API - Integrated multi-point routing and automatic optimization for project locations. - Documented setup, usage, and technical implementation details for route planning. - Included performance considerations and troubleshooting for common routing issues. chore: Remove unnecessary files and scripts from the codebase - Deleted temporary, debug-related, and test-specific files that are not needed in production. - Reviewed and ensured core application code and essential documentation remain intact.
352 lines
8.7 KiB
Markdown
352 lines
8.7 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```javascript
|
|
// POST /api/contacts
|
|
const contact = createContact(data);
|
|
|
|
// Sync to Radicale asynchronously (non-blocking)
|
|
syncContactAsync(contact);
|
|
|
|
return NextResponse.json(contact);
|
|
```
|
|
|
|
### When Updating a Contact
|
|
|
|
```javascript
|
|
// PUT /api/contacts/[id]
|
|
const contact = updateContact(contactId, data);
|
|
|
|
// Sync updated contact to Radicale
|
|
syncContactAsync(contact);
|
|
|
|
return NextResponse.json(contact);
|
|
```
|
|
|
|
### When Deleting a Contact
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
### Common Issues
|
|
|
|
**Problem: Sync not working / contacts not appearing in Radicale**
|
|
|
|
**Check 1: Environment Variables**
|
|
```bash
|
|
# Verify variables are set
|
|
echo $RADICALE_URL
|
|
echo $RADICALE_USERNAME
|
|
# Don't echo password for security
|
|
```
|
|
|
|
**Check 2: Radicale Server Connectivity**
|
|
```bash
|
|
# Test server is reachable
|
|
curl -I http://your-radicale-server:5232
|
|
|
|
# Test authentication
|
|
curl -u username:password http://your-radicale-server:5232/username/contacts/
|
|
```
|
|
|
|
**Check 3: Application Logs**
|
|
Look for sync messages in your application console:
|
|
```
|
|
✅ Synced contact 123 to Radicale
|
|
❌ Failed to sync contact 456 to Radicale: 401 - Unauthorized
|
|
```
|
|
|
|
---
|
|
|
|
**Problem: 401 Unauthorized errors**
|
|
- **Cause**: Invalid credentials or user doesn't exist
|
|
- **Solution**:
|
|
- Verify `RADICALE_USERNAME` and `RADICALE_PASSWORD`
|
|
- Ensure user exists in Radicale
|
|
- Check Radicale authentication method (basic auth vs htpasswd)
|
|
|
|
---
|
|
|
|
**Problem: 404 Not Found errors**
|
|
- **Cause**: Contacts collection doesn't exist
|
|
- **Solution**:
|
|
- Create collection in Radicale: `/{username}/contacts/`
|
|
- Verify collection URL matches `RADICALE_URL`
|
|
- Check Radicale collection rights and permissions
|
|
|
|
---
|
|
|
|
**Problem: Network timeout or connection refused**
|
|
- **Cause**: Radicale server not accessible from app server
|
|
- **Solution**:
|
|
- Check firewall rules
|
|
- Verify Radicale is running: `systemctl status radicale`
|
|
- Test with curl from app server
|
|
- If using Docker, ensure network connectivity
|
|
|
|
---
|
|
|
|
**Problem: Contacts created but not syncing**
|
|
- **Cause**: Environment variables not loaded or sync disabled
|
|
- **Solution**:
|
|
- Restart application after setting env vars
|
|
- Check `.env` or `.env.local` file exists
|
|
- Verify Next.js loaded environment: check server startup logs
|
|
- Test with manual export script: `node export-contacts-to-radicale.mjs`
|
|
|
|
---
|
|
|
|
**Problem: Duplicate contacts in Radicale**
|
|
- **Cause**: Re-running export script or UID conflicts
|
|
- **Solution**:
|
|
- UIDs are unique: `contact-{id}@panel-app`
|
|
- Existing contacts are overwritten on update
|
|
- Delete duplicates manually in Radicale if needed
|
|
|
|
---
|
|
|
|
**Problem: VCARD format errors in Radicale**
|
|
- **Cause**: Invalid characters or incomplete data
|
|
- **Solution**:
|
|
- Check contact has at least name field
|
|
- Special characters in names are escaped
|
|
- Phone/email fields are optional
|
|
- Review contact data for completeness
|
|
|
|
---
|
|
|
|
### Monitoring Sync Status
|
|
|
|
**Enable Detailed Logging**
|
|
Edit `src/lib/radicale-sync.js` to increase logging verbosity:
|
|
```javascript
|
|
// Add more console.log statements
|
|
console.log('Syncing contact:', contact);
|
|
console.log('VCARD:', vcard);
|
|
console.log('Response:', await response.text());
|
|
```
|
|
|
|
**Check Radicale Server Logs**
|
|
```bash
|
|
# Typical log location
|
|
tail -f /var/log/radicale/radicale.log
|
|
|
|
# Or check systemd journal
|
|
journalctl -u radicale -f
|
|
```
|
|
|
|
**Manual Sync Test**
|
|
Test individual contact sync:
|
|
```bash
|
|
# Use the export script for a single contact
|
|
node export-contacts-to-radicale.mjs
|
|
# Select specific contact when prompted
|
|
```
|
|
|
|
---
|
|
|
|
### Disable Sync Temporarily
|
|
|
|
Comment out environment variables to disable sync without removing configuration:
|
|
```bash
|
|
# .env.local
|
|
# RADICALE_URL=http://localhost:5232
|
|
# RADICALE_USERNAME=admin
|
|
# RADICALE_PASSWORD=secret
|
|
```
|
|
|
|
Application will function normally without sync enabled.
|
|
|
|
---
|
|
|
|
### Manual Sync Endpoint
|
|
|
|
For manual sync control, you can trigger sync via API:
|
|
|
|
```bash
|
|
# Sync specific contact
|
|
POST /api/contacts/{id}/sync
|
|
|
|
# Response
|
|
{
|
|
"success": true,
|
|
"message": "Contact synced to Radicale"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Error Codes Reference
|
|
|
|
| Code | Meaning | Solution |
|
|
|------|---------|----------|
|
|
| 401 | Unauthorized | Check credentials |
|
|
| 403 | Forbidden | Verify user has write permissions |
|
|
| 404 | Not Found | Create contacts collection |
|
|
| 409 | Conflict | UID collision (rare) |
|
|
| 500 | Server Error | Check Radicale server logs |
|
|
| ECONNREFUSED | Connection Refused | Server not reachable |
|
|
| ETIMEDOUT | Timeout | Network/firewall issue |
|
|
|
|
---
|
|
|
|
## 📋 Configuration Reference
|
|
|
|
### Required Environment Variables
|
|
```bash
|
|
RADICALE_URL=http://localhost:5232
|
|
RADICALE_USERNAME=your_username
|
|
RADICALE_PASSWORD=your_password
|
|
```
|
|
|
|
### Default Settings
|
|
- **Collection Path**: `/{username}/contacts/`
|
|
- **VCARD Version**: 3.0
|
|
- **UID Format**: `contact-{id}@panel-app`
|
|
- **Sync Mode**: Asynchronous (non-blocking)
|
|
- **Retry Logic**: None (fire-and-forget)
|
|
|
|
---
|
|
|
|
## 📂 Files Reference
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `src/lib/radicale-sync.js` | Core sync logic, VCARD generation |
|
|
| `src/app/api/contacts/route.js` | Create sync trigger |
|
|
| `src/app/api/contacts/[id]/route.js` | Update/delete sync triggers |
|
|
| `export-contacts-to-radicale.mjs` | Bulk export utility |
|
|
|
|
---
|
|
|
|
## 🔒 Security Best Practices
|
|
|
|
✅ **Do's:**
|
|
- Use HTTPS for production Radicale servers
|
|
- Store credentials in environment variables (never in code)
|
|
- Use strong, unique passwords
|
|
- Limit Radicale user permissions to contacts collection only
|
|
- Regularly rotate credentials
|
|
- Use separate credentials per environment (dev/staging/prod)
|
|
|
|
❌ **Don'ts:**
|
|
- Don't commit credentials to git
|
|
- Don't use HTTP in production
|
|
- Don't share credentials between environments
|
|
- Don't log passwords or sensitive data
|
|
- Don't grant unnecessary permissions
|
|
|
|
---
|
|
|
|
## 🚀 Advanced Configuration
|
|
|
|
### Custom Collection Path
|
|
Modify `src/lib/radicale-sync.js`:
|
|
```javascript
|
|
const baseUrl = `${process.env.RADICALE_URL}/${process.env.RADICALE_USERNAME}/my-custom-collection/`;
|
|
```
|
|
|
|
### Batch Sync Operations
|
|
For large-scale sync (future enhancement):
|
|
```javascript
|
|
// Collect contacts, then sync in batches
|
|
const batchSize = 50;
|
|
// Implement batch logic
|
|
```
|
|
|
|
### Webhook Integration
|
|
Future: Trigger webhooks on sync events:
|
|
```javascript
|
|
// POST to webhook URL on sync success/failure
|
|
fetch(WEBHOOK_URL, {
|
|
method: 'POST',
|
|
body: JSON.stringify({ event: 'contact_synced', contact_id: id })
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
**See Also**: [Contacts System](CONTACTS_SYSTEM.md) | [Main README](../../README.md#-cardav-integration-radicale) | [API Documentation](../../README.md#contacts)
|