serve

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package serve is anvil's self-hostable scheduling-link server and agenda app. One JSON config wires calendars (iCal URLs, CalDAV, Google) to people, people to scheduling links, and links to the calendar the booked invite is written into.

Index

Constants

View Source
const FreeLinkLimit = 1

FreeLinkLimit is how many scheduling links the free tier serves.

Variables

This section is empty.

Functions

func ParseDays

func ParseDays(s string) ([]time.Weekday, error)

ParseDays parses "mon,tue,..." (empty means default weekdays → nil).

func ParseHours

func ParseHours(s string) (from, to time.Duration, err error)

ParseHours parses "HH:MM-HH:MM" into offsets from midnight.

Types

type BasicAuth

type BasicAuth struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

BasicAuth guards the agenda UI and API.

type CalDAVConfig

type CalDAVConfig struct {
	BaseURL     string `json:"base_url"`
	Username    string `json:"username"`
	Password    string `json:"password"`
	CalendarURL string `json:"calendar_url"` // collection URL; find with `anvil caldav-calendars`
}

CalDAVConfig points at one CalDAV collection.

type CalendarConfig

type CalendarConfig struct {
	Name    string        `json:"name"`
	ICSURL  string        `json:"ics_url,omitempty"`
	ICSFile string        `json:"ics_file,omitempty"`
	CalDAV  *CalDAVConfig `json:"caldav,omitempty"`
	Google  *GoogleConfig `json:"google,omitempty"`
}

CalendarConfig names one calendar source. Exactly one of ICSURL, ICSFile, CalDAV, or Google must be set.

type Config

type Config struct {
	Listen    string           `json:"listen"`           // e.g. ":8080"
	Timezone  string           `json:"timezone"`         // IANA zone for pages and masks
	Auth      *BasicAuth       `json:"auth,omitempty"`   // protects / and /api; links stay public
	Agenda    []string         `json:"agenda,omitempty"` // calendar names on the agenda; default all
	Calendars []CalendarConfig `json:"calendars"`
	People    []PersonConfig   `json:"people"`
	Links     []LinkConfig     `json:"links"`
}

Config is the anvil serve configuration file.

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig reads and validates a config file.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks cross-references and fills nothing in; defaults apply at use sites.

type GoogleConfig

type GoogleConfig struct {
	ClientID     string `json:"client_id"`
	ClientSecret string `json:"client_secret"`
	RefreshToken string `json:"refresh_token"` // obtain with `anvil gcal-login`
	CalendarID   string `json:"calendar_id"`   // usually "primary"
}

GoogleConfig points at one Google calendar.

type LinkConfig

type LinkConfig struct {
	Slug       string   `json:"slug"` // /l/{slug}
	Title      string   `json:"title"`
	DurationM  int      `json:"duration_m"`
	Required   []string `json:"required"`               // person names; all must be free
	Optional   []string `json:"optional,omitempty"`     // scored
	DaysAhead  int      `json:"days_ahead,omitempty"`   // search horizon; default 14
	MinNoticeH int      `json:"min_notice_h,omitempty"` // earliest bookable; default 24
	Hours      string   `json:"hours,omitempty"`        // "09:00-17:00"
	Days       string   `json:"days,omitempty"`         // "mon,tue,..."; default weekdays
	StepM      int      `json:"step_m,omitempty"`       // default 30

	InPerson bool   `json:"in_person,omitempty"`
	TravelM  int    `json:"travel_m,omitempty"` // drive-time padding when in person
	Address  string `json:"address,omitempty"`  // event LOCATION when in person

	GoogleMeet bool   `json:"google_meet,omitempty"` // attach a Meet link (Google target only)
	VideoURL   string `json:"video_url,omitempty"`   // static conferencing room otherwise

	BookInto  string `json:"book_into"`           // calendar name the invite is created in
	Organizer string `json:"organizer,omitempty"` // person name; defaults to first required
}

LinkConfig is one public scheduling link.

type PersonConfig

type PersonConfig struct {
	Name      string   `json:"name"`
	Email     string   `json:"email,omitempty"`
	Calendars []string `json:"calendars"`
}

PersonConfig binds a person to every calendar they live by.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server hosts scheduling links and the agenda app.

func Demo added in v0.3.2

func Demo(listen string) (*Server, error)

Demo builds a fully working server with synthetic calendars — no config file, no providers, no credentials. Booking writes into an in-memory calendar that immediately blocks the slot, so the whole loop is visible: busy merging, slot search, booking, agenda.

func New

func New(cfg *Config) (*Server, error)

New wires a validated config into a Server.

func (*Server) CheckEntitlement added in v0.3.0

func (s *Server) CheckEntitlement() error

CheckEntitlement enforces the free-tier limit at boot: failing fast beats surprising a paying guest with a dead link later.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler returns the HTTP handler for the whole app.

func (*Server) Licensed added in v0.3.0

func (s *Server) Licensed() bool

Licensed reports the current entitlement.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe() error

ListenAndServe runs the server at cfg.Listen and shuts down gracefully on SIGINT/SIGTERM, letting in-flight bookings finish.

func (*Server) SetLicensed added in v0.3.0

func (s *Server) SetLicensed(v bool)

SetLicensed flips the Pro entitlement (set at boot and by the background license revalidation loop).

type Source

type Source interface {
	Events(window interval.Span) (*ics.Calendar, error)
	Busy(window interval.Span) (interval.Set, error)
}

Source reads one calendar. Events powers the agenda; Busy powers scheduling.

type Writer

type Writer interface {
	CreateEvent(ev ics.Event, meet bool) (joinURL string, err error)
}

Writer creates the booked invite. The returned join URL is non-empty when the backend minted a conferencing link (Google Meet).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL