united-tattoo/docs/CALDAV-SETUP.md
Nicholai a77f62f949 feat: implement CalDAV Nextcloud bidirectional calendar integration
Adds complete CalDAV integration for syncing appointments between the web app
and Nextcloud calendars with real-time availability checking and conflict resolution.

Core Features:
- Bidirectional sync: Web ↔ Nextcloud calendars
- Real-time availability checking with instant user feedback
- Conflict detection (Nextcloud is source of truth)
- Pending request workflow with 'REQUEST:' prefix for unconfirmed appointments
- Hard time blocking - any calendar event blocks booking slots
- Graceful degradation when CalDAV unavailable

New Dependencies:
- tsdav@^2.0.4 - TypeScript CalDAV client
- ical.js@^1.5.0 - iCalendar format parser/generator

Database Changes:
- New table: artist_calendars (stores calendar configuration per artist)
- New table: calendar_sync_logs (tracks all sync operations)
- Added caldav_uid and caldav_etag columns to appointments table
- Migration: sql/migrations/20250109_add_caldav_support.sql

New Services:
- lib/caldav-client.ts - Core CalDAV operations and iCalendar conversion
- lib/calendar-sync.ts - Bidirectional sync logic with error handling

New API Endpoints:
- GET /api/caldav/availability - Real-time availability checking
- POST /api/caldav/sync - Manual sync trigger (admin only)
- GET/POST/PUT/DELETE /api/admin/calendars - Calendar configuration CRUD

Updated Components:
- app/api/appointments/route.ts - Integrated CalDAV sync on CRUD operations
- components/booking-form.tsx - Added real-time availability indicator
- hooks/use-availability.ts - Custom hook for debounced availability checking

Documentation:
- docs/CALDAV-SETUP.md - Complete setup guide with troubleshooting
- docs/CALDAV-IMPLEMENTATION-SUMMARY.md - Technical implementation overview

Pending Tasks (for future PRs):
- Admin dashboard UI for calendar management
- Background sync worker (Cloudflare Workers cron)
- Unit and integration tests

Tested with local database migration and linting checks passed.
2025-10-08 20:44:17 -06:00

9.4 KiB

CalDAV Nextcloud Integration Setup Guide

This document provides instructions for setting up and configuring the bidirectional CalDAV integration with Nextcloud.

Overview

The CalDAV integration allows your tattoo booking system to:

  • Sync appointments FROM the web app TO Nextcloud calendars in real-time
  • Check availability FROM Nextcloud calendars to prevent double-bookings
  • Pull events FROM Nextcloud TO the database (for manual calendar entries)
  • Handle conflicts automatically (Nextcloud is the source of truth)

Prerequisites

  1. A Nextcloud instance with CalDAV enabled
  2. Admin access to Nextcloud to create app-specific passwords
  3. Individual calendars set up for each artist in Nextcloud

Environment Variables

Add these variables to your .env.local file:

# CalDAV / Nextcloud Integration
NEXTCLOUD_BASE_URL=https://your-nextcloud-instance.com
NEXTCLOUD_USERNAME=admin_or_service_account
NEXTCLOUD_PASSWORD=app_specific_password
NEXTCLOUD_CALENDAR_BASE_PATH=/remote.php/dav/calendars

Getting Nextcloud Credentials

  1. Log in to your Nextcloud instance
  2. Go to SettingsSecurity
  3. Scroll to Devices & Sessions
  4. Under App passwords, create a new app password named "Tattoo Booking System"
  5. Copy the generated password (it will look like: xxxxx-xxxxx-xxxxx-xxxxx-xxxxx)
  6. Use this as your NEXTCLOUD_PASSWORD value

Database Migration

The CalDAV integration requires new database tables. Run the migration:

# For local development
npm run db:migrate:local -- --file=./sql/migrations/20250109_add_caldav_support.sql

# For production
wrangler d1 execute united-tattoo --remote --file=./sql/migrations/20250109_add_caldav_support.sql

This creates the following tables:

  • artist_calendars - Stores calendar configuration for each artist
  • calendar_sync_logs - Tracks sync operations for monitoring
  • Adds caldav_uid and caldav_etag columns to appointments table

Configuring Artist Calendars

After setting up the environment variables, you need to configure which Nextcloud calendar belongs to each artist.

Step 1: Get Calendar URLs from Nextcloud

  1. Log in to Nextcloud
  2. Go to the Calendar app
  3. For each artist calendar:
    • Click the (three dots) menu next to the calendar name
    • Select Settings
    • Copy the Calendar Link (WebDAV URL)
    • It should look like: https://your-nextcloud.com/remote.php/dav/calendars/username/calendar-name/

Step 2: Configure in Admin Dashboard

  1. Log in to your tattoo booking admin dashboard
  2. Navigate to AdminCalendars
  3. Click Add Calendar Configuration
  4. Fill in the form:
    • Artist: Select the artist from dropdown
    • Calendar URL: Paste the WebDAV URL from Nextcloud
    • Calendar ID: Enter the calendar name (last part of URL)
  5. Click Test Connection to verify
  6. Save the configuration

API Method (Alternative)

You can also configure calendars via API:

curl -X POST https://your-domain.com/api/admin/calendars \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -d '{
    "artistId": "artist-uuid-here",
    "calendarUrl": "https://nextcloud.com/remote.php/dav/calendars/user/artist-name/",
    "calendarId": "artist-name"
  }'

How It Works

Booking Flow

  1. User submits booking → Creates PENDING appointment in database
  2. Real-time sync → Event created in Nextcloud with title "REQUEST: [Client Name] - [Description]"
  3. Artist/admin reviews → Sees pending request in their calendar app
  4. Admin approves → Status changes to CONFIRMED, event updated in Nextcloud
  5. Any conflicts → Detected automatically before booking is created

Conflict Resolution

  • Before booking creation: System checks Nextcloud calendar for conflicts
  • Nextcloud is source of truth: If an event exists in Nextcloud, that time slot is blocked
  • User feedback: Clear messaging if selected time is unavailable
  • Alternative times: Users can provide backup date/time preferences

Event Syncing

Web → Nextcloud (Real-time)

  • Appointment created → Event created in CalDAV
  • Appointment updated → Event updated in CalDAV
  • Appointment cancelled → Event deleted from CalDAV

Nextcloud → Web (Manual/Scheduled)

  • Use the admin sync button for manual sync
  • Background worker (future implementation) will sync periodically
  • Any calendar event blocks that time slot for web bookings

API Endpoints

Check Availability

GET /api/caldav/availability?artistId=UUID&startTime=ISO_DATE&endTime=ISO_DATE

Returns:

{
  "artistId": "uuid",
  "startTime": "2025-01-15T14:00:00Z",
  "endTime": "2025-01-15T16:00:00Z",
  "available": true,
  "reason": null
}

Manual Sync

POST /api/caldav/sync

Body:

{
  "artistId": "uuid-or-omit-for-all",
  "startDate": "2025-01-01T00:00:00Z",
  "endDate": "2025-03-31T23:59:59Z"
}

Manage Calendar Configurations

GET /api/admin/calendars
POST /api/admin/calendars
PUT /api/admin/calendars
DELETE /api/admin/calendars?id=UUID

Testing

1. Test Calendar Connection

# Using the admin UI
1. Go to Admin → Calendars
2. Click "Test Connection" on any calendar
3. Verify green checkmark appears

# Or via curl
curl -X GET https://your-nextcloud.com/remote.php/dav/calendars/username/ \
  -u "username:app-password"

2. Test Booking Flow

  1. Create a test appointment via the booking form
  2. Check Nextcloud calendar - event should appear with "REQUEST:" prefix
  3. Update appointment status to CONFIRMED in admin dashboard
  4. Check Nextcloud - event title should update (no "REQUEST:" prefix)
  5. Delete appointment - event should disappear from Nextcloud

3. Test Conflict Detection

  1. Manually create an event in Nextcloud for a specific time
  2. Try to book the same time slot via the web form
  3. Verify error message appears: "Time slot not available"

4. Test Availability Checking

  1. Open booking form
  2. Select an artist, date, and time
  3. Wait for availability indicator (green checkmark or red X)
  4. Verify real-time feedback as you change selections

Troubleshooting

"CalDAV not configured" warnings

Problem: Environment variables not set or incorrect

Solution:

  1. Verify all NEXTCLOUD_* variables are in .env.local
  2. Restart your development server
  3. Check credentials are correct (test with curl)

"Calendar configuration not found"

Problem: Artist doesn't have a calendar configured

Solution:

  1. Go to Admin → Calendars
  2. Add calendar configuration for the artist
  3. Test the connection

Sync fails with 401/403 errors

Problem: Authentication issue with Nextcloud

Solution:

  1. Verify app password is correct (regenerate if needed)
  2. Check username matches Nextcloud username
  3. Ensure calendar permissions allow API access

Events not appearing in Nextcloud

Problem: Sync is failing silently

Solution:

  1. Check Admin → Calendars → Sync Logs
  2. Look for error messages in logs
  3. Verify calendar URL is correct (trailing slash matters!)
  4. Test connection manually with curl

Availability always shows "not available"

Problem: CalDAV client returning errors

Solution:

  1. Check browser console for errors
  2. Verify API endpoint works: /api/caldav/availability
  3. Check network tab for failed requests
  4. Ensure artist has calendar configured

Monitoring

View Sync Logs

-- In Wrangler D1 console
SELECT * FROM calendar_sync_logs 
ORDER BY created_at DESC 
LIMIT 20;

Or via the admin dashboard:

  • Go to AdminCalendars
  • Click on any artist
  • View Recent Sync History

Key Metrics to Monitor

  • Sync success rate: Should be >95%
  • Events processed: Track volume over time
  • Error patterns: Look for repeating errors
  • Sync duration: Should be <2 seconds per artist

Best Practices

  1. Use app-specific passwords: Never use main Nextcloud password
  2. Test before production: Verify with test appointments first
  3. Monitor sync logs: Check regularly for failures
  4. Calendar naming: Use clear, consistent artist names
  5. Backup strategy: Export calendars regularly from Nextcloud
  6. User communication: Inform users that Nextcloud is authoritative

Future Enhancements

  • Background worker for automatic periodic sync (every 5 minutes)
  • Webhook support for instant sync when Nextcloud calendar changes
  • Bulk calendar configuration import
  • Sync status dashboard with real-time updates
  • Email notifications for sync failures
  • Two-way sync for appointment details (not just create/delete)

Security Considerations

  • Credentials stored in environment variables (never in code)
  • App-specific passwords (not main password)
  • Admin-only calendar configuration endpoints
  • CalDAV responses validated before database updates
  • Rate limiting on API endpoints
  • Sanitized event data before storing

Support

For issues or questions:

  1. Check the troubleshooting section above
  2. Review sync logs in admin dashboard
  3. Test with curl commands to isolate issues
  4. Check Nextcloud server logs if needed

References