feat(phase 2 caldav): list + link + create CalDAV calendars
m's CalDAV server (dav.msbls.de, SabreDAV) now feeds projax via a thin
read-only-plus-create-on-demand integration. No background sync; tasks
fetched live on detail-page render.
New caldav/ package
- ListCalendars (PROPFIND Depth: 1, filters non-calendar collections)
- ListTodos (REPORT calendar-query for VTODO; hand-rolled iCalendar
parser for UID/SUMMARY/STATUS/DUE/PRIORITY/LAST-MODIFIED — RFC 5545
line-folding aware)
- CreateCalendar (MKCALENDAR, 405 → ErrCalendarExists for the "link
instead" branch)
- httptest-stubbed tests cover all four paths.
Store
- ItemLink shape + LinksByType / LinksByRefType / AddLink / DeleteLink.
AddLink upserts on (item_id, ref_type, ref_id, rel) so re-linking the
same calendar is idempotent.
Web
- GET /admin/caldav — discovery + auto-suggested matches + manual
linker. Suggestion = lowercased displayname == projax slug or title.
- POST /admin/caldav/link — insert item_links row.
- POST /admin/caldav/unlink — delete by link id.
- POST /i/{path}/caldav/create — MKCALENDAR at <base>/<slug>/, then
AddLink. On 405 (already exists), fall back to link-only.
- Detail page Tasks section: per-calendar block with open VTODOs +
collapsed completed (30d window). Errors per calendar logged and
skipped, so one bad calendar does not blank the page.
- nav adds /admin/caldav link.
main.go
- DAV_URL + DAV_USER + DAV_PASSWORD optional. Missing DAV_URL → CalDAV
off (admin page renders "not configured" notice). DAV_URL set but
user/pass missing → fail fast at boot.
docs/design.md gains §5 documenting the integration shape.
deploy/dokploy.yaml lists the two new secrets + the env var.
Phase 2.b (writeback / two-way / background sync) is parked.
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"github.com/m/projax/caldav"
|
||||
"github.com/m/projax/db"
|
||||
"github.com/m/projax/store"
|
||||
"github.com/m/projax/web"
|
||||
@@ -76,6 +77,19 @@ func main() {
|
||||
logger.Warn("auth: disabled — SUPABASE_URL not set, every request is anonymous")
|
||||
}
|
||||
|
||||
if davURL := os.Getenv("DAV_URL"); davURL != "" {
|
||||
davUser := os.Getenv("DAV_USER")
|
||||
davPass := os.Getenv("DAV_PASSWORD")
|
||||
if davUser == "" || davPass == "" {
|
||||
logger.Error("DAV_URL set but DAV_USER / DAV_PASSWORD missing — refusing to start")
|
||||
os.Exit(1)
|
||||
}
|
||||
srv.CalDAV = &web.CalDAVDeps{Client: caldav.New(davURL, davUser, davPass)}
|
||||
logger.Info("caldav: enabled", "base_url", davURL)
|
||||
} else {
|
||||
logger.Info("caldav: disabled — DAV_URL not set")
|
||||
}
|
||||
|
||||
httpServer := &http.Server{
|
||||
Addr: listen,
|
||||
Handler: srv.Routes(),
|
||||
|
||||
Reference in New Issue
Block a user