Add OpenSpec workflow for AI-assisted change proposals including proposal templates, archive commands, and project configuration. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4.9 KiB
4.9 KiB
Tasks: iCal Subscription Export
1. Backend - Data Model
- 1.1 Create
CalendarSubscriptionTokenmodel incore/models.py- ForeignKey to User (owner, on_delete=CASCADE)
caldav_pathfield (CharField, max_length=512) - stores CalDAV path directlycalendar_namefield (CharField, max_length=255, optional) - for displaytokenfield (UUID, unique, indexed, default=uuid4)is_activeboolean (default=True)last_accessed_atDateTimeField (nullable)- UniqueConstraint on (owner, caldav_path)
- 1.2 Create and run database migration
- 1.3 Add model to Django admin for debugging
2. SabreDAV - Enable ICSExportPlugin
- 2.1 Add
ICSExportPlugintoserver.php:$server->addPlugin(new CalDAV\ICSExportPlugin()); - 2.2 Test that
?exportworks via existing CalDAV proxy
3. Backend - Public iCal Endpoint
- 3.1 Create
ICalExportViewincore/api/viewsets_ical.py- No authentication required (public endpoint)
- Extract token from URL path
- Lookup
CalendarSubscriptionTokenby token - Return 404 if token invalid/inactive
- Update
last_accessed_aton access - Proxy request to SabreDAV using
token.caldav_pathandtoken.owner.email - Return ICS response with
Content-Type: text/calendar - Set security headers (Cache-Control, Referrer-Policy)
- 3.2 Add URL route:
path('ical/<uuid:token>.ics', ...) - 3.3 Write tests for public endpoint (valid token, invalid token, inactive)
4. Backend - Standalone Token Management API
- 4.1 Create serializers in
core/api/serializers.pyCalendarSubscriptionTokenSerializer- fields: token, url, caldav_path, calendar_name, etc.CalendarSubscriptionTokenCreateSerializer- for POST body validation
- 4.2 Create standalone
SubscriptionTokenViewSetincore/api/viewsets.py:POST /subscription-tokens/- create token with { caldav_path, calendar_name }GET /subscription-tokens/by-path/?caldav_path=...- get existing tokenDELETE /subscription-tokens/by-path/?caldav_path=...- revoke token- Permission verification: user's email must be in caldav_path
- 4.3 Register viewset in
core/urls.py - 4.4 Write API tests for token management (create, get, delete, permissions)
5. Frontend - API Integration
- 5.1 Add API functions in
features/calendar/api.ts:getSubscriptionToken(caldavPath)- GET by-pathcreateSubscriptionToken({ caldavPath, calendarName })- POSTdeleteSubscriptionToken(caldavPath)- DELETE by-path
- 5.2 Update React Query hooks in
hooks/useCalendars.ts- Use caldavPath instead of calendarId
6. Frontend - UI Components
- 6.1 Update
SubscriptionUrlModalcomponent- Accept
caldavPathprop instead ofcalendarId - Extract caldavPath from calendar URL in parent component
- Display the subscription URL in a copyable field
- "Copy to clipboard" button with success feedback
- Warning text about URL being private
- "Regenerate URL" button with confirmation dialog
- Only show error alert for real errors (not for expected 404)
- Accept
- 6.2 Update
CalendarList.tsx- Extract CalDAV path from calendar URL
- Pass caldavPath to SubscriptionUrlModal
- 6.3 Add translations (i18n) for new UI strings
7. Cleanup
- 7.1 Remove old
subscription_tokenaction from CalendarViewSet - 7.2 Remove
sync-from-caldavendpoint (no longer needed) - 7.3 Remove
syncFromCaldavfrom frontend API
8. Testing & Validation
- 8.1 Manual test: add URL to Apple Calendar
- 8.2 Manual test: add URL to Google Calendar
- 8.3 Verify token regeneration invalidates old URL
- 8.4 E2E test for subscription workflow (optional)
Dependencies
1 (Django model)
↓
2 (ICSExportPlugin) ──────┐
↓ │
3 (Public endpoint) ──────┤ can run in parallel after 1
↓ │
4 (Token API) ────────────┘
↓
5 (Frontend API)
↓
6 (Frontend UI)
↓
7 (Cleanup)
↓
8 (Testing)
Key Files Modified
Backend
src/backend/core/models.py- CalendarSubscriptionToken model (standalone)src/backend/core/migrations/0002_calendarsubscriptiontoken.pysrc/backend/core/api/serializers.py- Token serializerssrc/backend/core/api/viewsets.py- SubscriptionTokenViewSetsrc/backend/core/api/viewsets_ical.py- ICalExportViewsrc/backend/core/urls.py- Route registrationsrc/backend/core/admin.py- Admin configurationsrc/backend/core/factories.py- Test factory
Frontend
src/features/calendar/api.ts- API functions with caldavPathsrc/features/calendar/hooks/useCalendars.ts- React Query hookssrc/features/calendar/components/calendar-list/CalendarList.tsxsrc/features/calendar/components/calendar-list/SubscriptionUrlModal.tsx
SabreDAV
docker/sabredav/server.php- ICSExportPlugin enabled