package caddyhugo import ( "fmt" "html/template" "net/http" "path" "path/filepath" "strings" "sync" "git.stephensearles.com/stephen/acedoc" "git.stephensearles.com/stephen/caddy-hugo2/comments" "git.stephensearles.com/stephen/caddy-hugo2/media" "go.uber.org/zap" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" "github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/hugolib" "github.com/spf13/afero" ) func init() { caddy.RegisterModule(&CaddyHugo{}) httpcaddyfile.RegisterHandlerDirective("hugo", parseCaddyfile) } type SiteConfig struct { Hosts []string Root string CommentsEnabled bool CommentsPassword string } func (m *CaddyHugo) CaddyModule() caddy.ModuleInfo { return caddy.ModuleInfo{ ID: "http.handlers.hugo", New: func() caddy.Module { return new(CaddyHugo) }, } } func (m *CaddyHugo) Provision(ctx caddy.Context) error { m.logger = ctx.Logger(m) if m.Site.CommentsEnabled { m.Comments = comments.WithStorage(comments.NewDiskv(path.Join(m.Site.Root, "comments"))) } // TODO: not sure where m.Site is getting populated. // m.Site.Root looks to be the working directory of caddy but it needs to be the directory of the site root, err := filepath.Abs(m.Site.Root) if err != nil { return err } err = m.Setup(root) if err != nil { return err } ctx.OnCancel(func() { m.persistAllEdits() }) m.commentsSetting() return nil } // UnmarshalCaddyfile implements caddyfile.Unmarshaler. func (s *SiteConfig) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { for d.NextBlock(0) { if d.Val() == "comments" { if d.NextArg() { s.CommentsPassword = d.Val() } } } return nil } // CaddyHugo implements the plugin for a single site type CaddyHugo struct { logger *zap.Logger ServerType string Site SiteConfig HugoSites *hugolib.HugoSites HugoCfg *deps.DepsCfg Dir string Media *media.MediaSource Comments *comments.Service docs map[string]*editSession mtx sync.Mutex authorTmpl, adminTmpl, editTmpl *template.Template ltime uint64 confirmingToClient map[uint64]struct{} } func (ch *CaddyHugo) log(msg string, args ...interface{}) { all := make([]any, len(args)+1) all[0] = msg copy(all[1:], args) ch.logger.Info(fmt.Sprint(all...)) } func (ch *CaddyHugo) logf(msg string, args ...interface{}) { ch.logger.Info(fmt.Sprintf(msg, args...)) } // Build rebuilds the cached state of the site. TODO: determine if this republishes func (ch *CaddyHugo) Build() error { return buildSite(ch.HugoSites) } // BasePath returns the directory that the CaddyHugo internal/author pages are under func (ch *CaddyHugo) BasePath() string { return "/hugo" } func docname(orig string) string { orig = strings.Replace(orig, " ", "-", -1) return strings.ToLower(orig) } func (ch *CaddyHugo) docFilename(orig string) string { return filepath.Join(ch.Dir, docname(orig)) } // TmplData collects data for template execution func (ch *CaddyHugo) TmplData(r *http.Request, docref *editSession) interface{} { var doc *acedoc.Document if docref != nil { doc = docref.doc } if ch.HugoSites != nil && ch.HugoSites.Fs != nil { ch.HugoSites.Cfg.Set("buildDrafts", true) ch.HugoSites.Fs.Destination = afero.NewMemMapFs() ch.Build() } return &tmplData{ch.Site, r, ch, doc, docref} } // parseCaddyfile unmarshals tokens from h into a new Middleware. func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) { var m CaddyHugo err := m.Site.UnmarshalCaddyfile(h.Dispenser) return &m, err } var ( _ caddy.Provisioner = (*CaddyHugo)(nil) )