11 KiB
Calendar Application Architecture
Overview
The Calendar application is a modern, self-hosted calendar solution that combines a Django REST API backend with a separate CalDAV server for standards-compliant calendar data storage and synchronization. This architecture provides both a modern web interface and full CalDAV protocol support for compatibility with standard calendar clients.
System Architecture
┌─────────────────┐
│ Frontend │
│ (Next.js) │
└────────┬────────┘
│
│ HTTP/REST API + CalDAV Protocol
│
┌────────▼─────────────────────────────────────┐
│ Django Backend │
│ ┌──────────────────────────────────────┐ │
│ │ REST API Endpoints │ │
│ │ - /api/v1.0/calendars │ │
│ │ - /api/v1.0/users │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ CalDAV Proxy │ │
│ │ - /api/v1.0/caldav/* │ │
│ │ - /.well-known/caldav │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ Authentication (OIDC/Keycloak) │ │
│ └──────────────────────────────────────┘ │
└────────┬───────────────────────────────────┘
│
│ HTTP/CalDAV Protocol
│
┌────────▼─────────────────────────────────────┐
│ CalDAV Server │
│ (CalDAV Protocol Implementation) │
│ - Calendar storage │
│ - Event storage (iCalendar format) │
│ - CalDAV protocol handling │
└────────┬─────────────────────────────────────┘
│
│ PostgreSQL
│
┌────────▼─────────────────────────────────────┐
│ PostgreSQL Database │
│ - Django models (users, calendars metadata) │
│ - CalDAV server schema (calendar data) │
└──────────────────────────────────────────────┘
Component Responsibilities
Django Backend
The Django backend serves as the orchestration layer and business logic engine for the application.
Primary Responsibilities:
- User Management & Authentication: OIDC authentication via Keycloak, user profiles, sessions, authorization
- Calendar Metadata Management: Calendar creation/deletion, sharing, visibility settings, display preferences
- REST API Layer: Modern RESTful API for the web frontend (JSON, standard HTTP methods, versioned at
/api/v1.0/) - CalDAV Proxy: Proxies CalDAV requests to CalDAV server, handles authentication translation, URL routing, discovery endpoint
- Business Logic: Calendar sharing logic, permission checks, data validation, integration coordination
Data Storage:
- User accounts
- Calendar metadata (name, color, visibility, owner)
- Sharing relationships
- Application configuration
Important: Django does NOT store actual calendar events. Events are stored in the CalDAV server.
CalDAV Server
The CalDAV server is a standards-compliant CalDAV server that handles all calendar data storage and protocol operations.
Primary Responsibilities:
- Calendar Data Storage: Stores actual calendar events in iCalendar format, manages calendar collections
- CalDAV Protocol Implementation: Full RFC 4791 implementation (PROPFIND, REPORT, MKCALENDAR, PUT, DELETE)
- iCalendar Format Management: Parses and generates iCalendar files, validates syntax, handles VEVENT/VTODO components
- Database Schema: Uses PostgreSQL with its own schema for calendar data
Authentication Integration:
- Uses Apache authentication backend which reads
REMOTE_USERenvironment variable - Django proxy sets
X-Forwarded-Userheader, which the CalDAV server converts toREMOTE_USER - All communication is via HTTP - no direct database access from Django
Frontend (Next.js)
The frontend provides the user interface and interacts with both REST API and CalDAV protocol:
- Modern React-based UI
- Uses REST API for calendar metadata operations
- Uses CalDAV protocol directly for event operations
- Supports multiple languages and themes
Why This Architecture?
Design Decision: CalDAV Server Separation
The decision to use a separate CalDAV server rather than implementing CalDAV directly in Django was made for several reasons:
-
Standards Compliance: Using a mature, well-tested CalDAV server that fully implements RFC 4791. Implementing CalDAV from scratch would be error-prone and time-consuming.
-
Protocol Complexity: CalDAV is built on WebDAV, involving complex XML handling, property management, and collection hierarchies. A dedicated CalDAV server handles all of this complexity.
-
Maintenance: Using a proven, maintained CalDAV server reduces maintenance burden and ensures compatibility with various CalDAV clients.
-
Focus: Django backend can focus on business logic, user management, and REST API, while the CalDAV server handles calendar protocol operations.
-
Shared database: The CalDAV server stores its data into Postgres, which we use in all LaSuite projects.
-
Clean separation: All communication between Django and the CalDAV server is via HTTP, ensuring clean separation of concerns and no direct database access.
Benefits
-
Standards Compliance
- Full CalDAV protocol support enables compatibility with any CalDAV client (Apple Calendar, Thunderbird, etc.)
- Users can sync calendars with external applications
- Follows industry standards (RFC 4791)
-
Separation of Concerns
- Django handles business logic and user management
- CalDAV server handles calendar protocol and data storage
- Each component focuses on its core competency
-
Flexibility
- Can expose both REST API (for web app) and CalDAV (for external clients)
- Different clients can use different protocols
- Future-proof architecture
-
Maintainability
- Clear boundaries between components
- Easier to test and debug
- Can update components independently
-
Performance
- CalDAV server is optimized for CalDAV operations
- Django can focus on application logic
- Database can be optimized separately for each use case
Data Flow
Creating a Calendar
TODO: should this only be via caldav too?
- Frontend → POST
/api/v1.0/calendars(REST API) - Django Backend: Validates request, creates
Calendarmodel, calls CalDAV server to create calendar collection - CalDAV Server: Receives MKCALENDAR request, creates calendar collection, returns calendar path
- Django Backend: Stores CalDAV server path in
Calendar.caldav_path, returns calendar data to frontend
Creating an Event
Events are created directly via CalDAV protocol:
- Frontend → PUT
/api/v1.0/caldav/{user}/{calendar}/{event_uid}.ics(CalDAV) - Django Backend:
CalDAVProxyViewauthenticates user, forwards request to CalDAV server with authentication headers - CalDAV Server: Receives PUT request with iCalendar data, stores event in calendar collection
- Django Backend: Forwards CalDAV response to frontend
CalDAV Client Access
- CalDAV Client → PROPFIND
/api/v1.0/caldav/(CalDAV protocol) - Django Backend: Authenticates user via Django session, forwards request to CalDAV server with
X-Forwarded-Userheader - CalDAV Server: Processes CalDAV request, returns CalDAV response
- Django Backend: Forwards response to client
Integration Points
User Synchronization
Users are automatically created in the CalDAV server when they first access it. The CalDAV server's Apache authentication backend reads the REMOTE_USER environment variable, which is set from the X-Forwarded-User header sent by Django. No explicit user creation is needed - the CalDAV server will create principals on-demand.
Calendar Creation
When creating a calendar via REST API:
- Django creates
Calendarmodel with metadata - Django calls CalDAV server via HTTP to create calendar collection
- Django stores CalDAV server path in
Calendar.caldav_path
Authentication Translation
Django sessions are translated to CalDAV server authentication:
- Django adds
X-Forwarded-Userheader with user email - CalDAV server converts
X-Forwarded-UsertoREMOTE_USERenvironment variable - CalDAV server's Apache authentication backend reads
REMOTE_USERfor authentication - All communication is via HTTP - no direct database access
URL Routing
CalDAV clients expect specific URL patterns. The CalDAV proxy handles path translation:
- Discovery endpoint at
.well-known/caldavredirects to/api/v1.0/caldav/ - Proxy forwards requests to CalDAV server with correct paths
Database Schema
Both Django and the CalDAV server use the same PostgreSQL database in a local Docker install, but maintain separate schemas:
Django Schema (public schema):
calendars_user- User accountscaldav_calendar- Calendar metadatacaldav_calendarshare- Sharing relationships- Other Django app tables
CalDAV Server Schema (public schema, same database):
users- CalDAV server user records (for digest auth, not used with Apache auth)principals- CalDAV server principalscalendars- Calendar collectionscalendarinstances- Calendar instance metadatacalendarobjects- Calendar resources (events)calendarchanges- Change tracking- Other CalDAV server-specific tables
This allows them to share the database locally while keeping data organized. Note that Django never directly accesses CalDAV server tables - all communication is via HTTP.