ActivityFox Dev

CalDAV Integration

Davis CalDAV server integration, tsdav/ical.js libraries, event lifecycle, and recurrence rule handling.

CalDAV Integration

All calendar and scheduling data lives exclusively on the Davis CalDAV server. Sharetribe privateData stores only references (calendarUrl, davisUserId).

Event Creation Flow

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Browser
    participant API as Express API
    participant ST as Sharetribe
    participant Davis

    Browser->>API: POST /api/calendar/events
    API->>ST: Verify user session<br/>(get childIds, credentials)
    API->>API: Verify calendar ownership
    API->>API: Decrypt child credentials<br/>(AES-256-GCM)
    API->>Davis: PUT VEVENT<br/>(CalDAV with rrule)
    Davis-->>API: 201 Created
    API-->>Browser: Event data

Davis Server

Davis is a CalDAV/CardDAV server. Run locally with:

docker compose up -d

This starts Davis + Postgres. Configuration is in docker-compose.yml.

Environment Variables

VariableDescription
DAVIS_CALDAV_URLCalDAV endpoint (default: http://localhost:9000/dav)
DAVIS_API_URLREST API endpoint (default: http://localhost:9000/api)
DAVIS_API_TOKENAdmin API token

Libraries

  • tsdav — WebDAV/CalDAV client for Node.js
  • ical.js — Parse and build iCalendar (ICS) data

CalDAV URL Encoding

CalDAV URLs contain slashes, so they can't be used directly in REST route params. The calendarId used in API routes is the CalDAV URL encoded as base64url:

encodeCalendarId(calendarUrl)  // Buffer.from(url).toString('base64url')
decodeCalendarId(calendarId)   // Buffer.from(id, 'base64url').toString('utf-8')

Calendar Lifecycle

  1. On child creation (/api/children/create): if the parent has Davis credentials, a CalDAV calendar is automatically created for the child
  2. Manual creation (/api/calendar/create): creates a calendar for a child that doesn't have one yet
  3. On session publish (/api/sessions/publish): a CalDAV calendar is created on the vendor's Davis account with WEEKLY recurring events matching the session schedule

The calendar URL is stored in the child's Sharetribe privateData as calendarUrl.

Event CRUD

All endpoints verify calendar ownership before proceeding (the calendar must belong to one of the authenticated user's children).

EndpointMethodDescription
/api/calendar/events/:calendarIdGETFetch all events from a calendar
/api/calendar/eventsPOSTCreate event (supports rrule)
/api/calendar/events/updatePOSTUpdate existing event
/api/calendar/events/deletePOSTDelete event
/api/calendar/listGETList all calendars
/api/calendar/createPOSTCreate calendar for a child

Recurrence (rrule)

Events support iCalendar RRULE for recurrence:

{
  "summary": "Swimming",
  "dtstart": "2025-01-06T15:00:00.000Z",
  "dtend": "2025-01-06T16:00:00.000Z",
  "rrule": {
    "freq": "WEEKLY",
    "byday": ["MO"]
  }
}

Supported frequencies: DAILY, WEEKLY, WEEKLY with byday. The frontend expands rrules into individual occurrences for display (see calendarUtils.ts).

davisUserId vs davisUsername

  • davisUsername: the user's login credential (typically their email)
  • davisUserId: Davis's internal user ID used in CalDAV URL paths (/dav/<davisUserId>/calendars/...)

Both are stored in the parent's Sharetribe privateData. Calendar URL construction uses davisUserId, not davisUsername.

On this page