feat: Implement comprehensive Contacts Management System with API and UI integration
- 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.
This commit is contained in:
351
docs/features/RADICALE_SYNC.md
Normal file
351
docs/features/RADICALE_SYNC.md
Normal file
@@ -0,0 +1,351 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user