Add ICS/webcal HTTP subscription calendars
Users can now subscribe to a remote ICS/webcal feed from the dashboard (name + color), the same way they set up a real calendar or the virtual Birthdays calendar. Subscriptions are read-only, per-user, and share the "/cal/home/<name>/" namespace with real calendars and "birthdays" (name collisions are rejected in both directions). - internal/db: new ics_subscriptions table + CRUD (internal/db/ics.go); CreateCalendarWithColor checks for a colliding subscription name. - internal/icssub: shared HTTP-fetch + TTL cache (15 min) for remote ICS calendars, with webcal:// -> https:// rewriting and stale-on-error fallback, used by both the web UI and the CalDAV backend. - internal/web: dashboard "Subscribe to an ICS/webcal calendar" form, color picker, delete button (internal/web/ics.go, templates/dashboard.templ); month view renders subscription events in their chosen color, read-only (internal/web/calendar.go). - internal/caldav: subscriptions are exposed as read-only calendars (internal/caldav/ics.go) - listed in PROPFIND, events served via GET/REPORT, PUT/DELETE on individual events rejected with 403, but DELETE on the calendar itself unsubscribes; calendar-color is injected the same way as for real calendars and Birthdays. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -12,20 +12,22 @@ import (
|
||||
|
||||
"github.com/yourusername/caldav-server/internal/config"
|
||||
"github.com/yourusername/caldav-server/internal/db"
|
||||
"github.com/yourusername/caldav-server/internal/icssub"
|
||||
"github.com/yourusername/caldav-server/internal/store"
|
||||
)
|
||||
|
||||
// Server holds the dependencies needed by the web UI handlers.
|
||||
type Server struct {
|
||||
cfg *config.Config
|
||||
store *store.Store
|
||||
dbase *db.DB
|
||||
logger *slog.Logger
|
||||
cfg *config.Config
|
||||
store *store.Store
|
||||
dbase *db.DB
|
||||
logger *slog.Logger
|
||||
icsCache *icssub.Cache
|
||||
}
|
||||
|
||||
// NewServer constructs a web UI Server.
|
||||
func NewServer(cfg *config.Config, st *store.Store, dbase *db.DB, logger *slog.Logger) *Server {
|
||||
return &Server{cfg: cfg, store: st, dbase: dbase, logger: logger}
|
||||
return &Server{cfg: cfg, store: st, dbase: dbase, logger: logger, icsCache: icssub.NewCache(icssub.DefaultTTL)}
|
||||
}
|
||||
|
||||
// Handler returns the http.Handler serving the web UI, mounted at "/web/"
|
||||
@@ -46,6 +48,8 @@ func (s *Server) Handler(staticFS http.FileSystem) http.Handler {
|
||||
mux.HandleFunc("/resources/calendar/color", s.requireLogin(s.handleCalendarColor))
|
||||
mux.HandleFunc("/resources/birthdays/color", s.requireLogin(s.handleBirthdayColor))
|
||||
mux.HandleFunc("/resources/addressbook", s.requireLogin(s.handleAddressBookResource))
|
||||
mux.HandleFunc("/resources/ics", s.requireLogin(s.handleICSResource))
|
||||
mux.HandleFunc("/resources/ics/color", s.requireLogin(s.handleICSColor))
|
||||
mux.HandleFunc("/files/{path...}", s.requireLogin(s.handleFiles))
|
||||
mux.HandleFunc("/contacts", s.requireLogin(s.handleContactsHome))
|
||||
mux.HandleFunc("/contacts/{book}", s.requireLogin(s.handleContactsList))
|
||||
|
||||
Reference in New Issue
Block a user