feat(calendar): Phase 2 – mehrere Kalender, Export/Import als .ics

Neuer APIHandler unter /api/v1/calendars (Session-Auth, wie der Rest
der API) neben dem bestehenden CalDAV-Handler (App-Passwörter,
/caldav/) – zwei Handler, zwei Auth-Modelle, wie schon bei /dav.

- GET/POST /api/v1/calendars – Liste, Anlegen
- PATCH/DELETE /api/v1/calendars/{id} – Umbenennen, Farbe, Zeitzone,
  Löschen (go-webdav kennt PROPPATCH und Kalender-Löschen nicht – dafür
  gibt's im CalDAV-Protokoll selbst keinen Weg)
- GET .../export – alle Termine eines Kalenders als eine .ics-Datei,
  VTIMEZONE-Blöcke dedupliziert
- POST .../import – .ics mit mehreren VEVENT/VTODO einlesen, jede
  Komponente wird ein eigenes Objekt wie bei einem einzelnen CalDAV-PUT

Gegen den laufenden Container getestet (Session-Login, CSRF-Token):
Anlegen, Umbenennen, zwei Termine importieren, sauberer Re-Export,
Löschen, und dass ohne Login 401 statt Daten kommt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Sonnet 5 committed 2026-09-03 23:03:26 +02:00
1 parent 3f1b2b8a14
commit 4d635fff20
3 files changed
+284

No files matched your search

+11
View File
@@ -250,6 +250,7 @@ func main() {
// CalDAV
calStore := calendar.NewStore(pool)
calHandler := calendar.NewHandler(authStore, calStore)
calAPIHandler := calendar.NewAPIHandler(calStore)
// OIDC (optional – disabled if OIDC_ISSUER not set)
oidcHandler, err := appoidc.New(ctx, appoidc.Config{
@@ -524,6 +525,16 @@ func main() {
// es steht ohnehin an Freigaben und in Nutzerlisten.
r.Get("/users/{id}/avatar", avatarHandler.Serve)
// Kalender – die Termine selbst laufen über CalDAV (/caldav/), das
// hier ist nur Verwaltung: mehrere Kalender, umbenennen, Farbe,
// löschen, sowie Export/Import als .ics.
r.Get("/calendars", calAPIHandler.List)
r.Post("/calendars", calAPIHandler.Create)
r.Patch("/calendars/{id}", calAPIHandler.Update)
r.Delete("/calendars/{id}", calAPIHandler.Delete)
r.Get("/calendars/{id}/export", calAPIHandler.Export)
r.Post("/calendars/{id}/import", calAPIHandler.Import)
r.Get("/app-passwords", authHandler.ListAppPasswords)
r.Post("/app-passwords", authHandler.CreateAppPassword)
r.Delete("/app-passwords/{id}", authHandler.DeleteAppPassword)