Add public/private calendars, full iCal support, and iCal URL import

- Public/private: toggle is_public via PUT /calendars/{id}; generate/clear
  public_token and return ical_url when public
- Public feed: GET /cal/{token}/feed.ics (no auth) for subscription in
  Google/Apple/Outlook calendars
- Full iCal export: use golang-ical; VALARM, ATTENDEE, all-day (VALUE=DATE),
  RRULE, DTSTAMP, CREATED, LAST-MODIFIED
- Full iCal import: parse TZID, VALUE=DATE, VALARM, ATTENDEE, RRULE
- Import from URL: POST /calendars/import-url with calendar_id + url
- Migration: unique index on public_token, calendar_subscriptions table
- Config: BASE_URL for ical_url; Calendar model + API: ical_url field
- Docs: OpenAPI, llms.txt, README, SKILL.md, about/overview

Made-with: Cursor
This commit is contained in:
Michilis
2026-02-28 04:48:53 +00:00
parent 41f6ae916f
commit 2cb9d72a7f
23 changed files with 721 additions and 92 deletions

View File

@@ -42,7 +42,7 @@ DELETE /api-keys/{id} - Revoke key. Returns {"ok": true}.
GET /calendars - List all calendars (owned + shared). Returns {"items": [Calendar], "page": {...}}.
POST /calendars - Create. Body: {"name" (1-80), "color"? (#RRGGBB)}. Returns {"calendar": Calendar}.
GET /calendars/{id} - Get by ID. Returns {"calendar": Calendar}.
PUT /calendars/{id} - Update. Body: {"name"?, "color"?, "is_public"? (owner only)}. Returns {"calendar": Calendar}.
PUT /calendars/{id} - Update. Body: {"name"?, "color"?, "is_public"? (owner only)}. When is_public is set to true, a public iCal feed URL (ical_url) is generated. Returns {"calendar": Calendar}.
DELETE /calendars/{id} - Soft-delete (owner only). Returns {"ok": true}.
### Calendar Sharing
@@ -90,13 +90,15 @@ POST /booking/{token}/reserve - Public, no auth. Body: {"name", "email", "slot_s
## ICS Import/Export
GET /calendars/{id}/export.ics - Export as ICS (scope: calendars:read). Returns text/calendar.
POST /calendars/import - Import ICS (scope: calendars:write). Multipart form: calendar_id (uuid) + file (.ics). Returns {"ok": true, "imported": {"events": N}}.
GET /calendars/{id}/export.ics - Export as ICS (scope: calendars:read). Full RFC 5545 support: VEVENT with VALARM (reminders), ATTENDEE, all-day events (VALUE=DATE), RRULE, DTSTART/DTEND, SUMMARY, DESCRIPTION, LOCATION, CREATED, LAST-MODIFIED. Returns text/calendar.
POST /calendars/import - Import ICS file (scope: calendars:write). Multipart form: calendar_id (uuid) + file (.ics). Parses VEVENT with VALARM, ATTENDEE, TZID, VALUE=DATE, RRULE. Returns {"ok": true, "imported": {"events": N}}.
POST /calendars/import-url - Import from iCal URL (scope: calendars:write). Body: {"calendar_id": uuid, "url": "https://..."}. Fetches the iCal feed and imports events. Supports http, https, webcal protocols. Returns {"ok": true, "imported": {"events": N}, "source": url}.
GET /cal/{token}/feed.ics - Public iCal feed. No auth required. Returns the calendar's events in iCal format. Subscribe to this URL in Google Calendar, Apple Calendar, Outlook, etc. The URL is available as ical_url in the Calendar object when is_public is true.
## Data Schemas
User: {id, email, timezone, created_at, updated_at}
Calendar: {id, name, color, is_public, role, created_at, updated_at}
Calendar: {id, name, color, is_public, ical_url?, role, created_at, updated_at}
Event: {id, calendar_id, title, description?, location?, start_time, end_time, timezone, all_day, recurrence_rule?, is_occurrence, occurrence_start_time?, occurrence_end_time?, created_by, updated_by, created_at, updated_at, reminders[], attendees[], tags[], attachments[]}
Contact: {id, first_name?, last_name?, email?, phone?, company?, notes?, created_at, updated_at}
Reminder: {id, minutes_before}
@@ -126,4 +128,5 @@ Cursor-based. Query params: limit (default 50, max 200), cursor (opaque string).
- Event list range: max 1 year
- Soft deletion throughout (data recoverable)
- Calendar roles: owner (full), editor (events CRUD), viewer (read-only)
- Only owners can share calendars, delete calendars, create booking links
- Only owners can share calendars, delete calendars, create booking links, change is_public
- Public calendars provide an ical_url for subscription by external calendar apps