all repos — homestead @ 5ce60eee31c403537efd6ddb20308a3652a928b2

Code for my website

re-organise everything

Alan Pearce
commit

5ce60eee31c403537efd6ddb20308a3652a928b2

parent

4f48df92fa20e1c9cb4833400aabbeaf5c67718b

1 file changed, 130 insertions(+), 8 deletions(-)

changed files
M internal/website/mux.gointernal/website/mux.go
@@ -2,19 +2,43 @@ package website
import ( "encoding/json" + "fmt" "net/http" + "regexp" + "slices" "strings" + "gitlab.com/tozd/go/errors" + "go.alanpearce.eu/website/internal/builder" "go.alanpearce.eu/website/internal/config" ihttp "go.alanpearce.eu/website/internal/http" + "go.alanpearce.eu/website/internal/server" + "go.alanpearce.eu/website/internal/storage" "go.alanpearce.eu/website/internal/storage/files" + "go.alanpearce.eu/website/internal/watcher" "go.alanpearce.eu/website/templates" "go.alanpearce.eu/x/log" "github.com/benpate/digit" "github.com/kevinpollet/nego" + "github.com/osdevisnot/sorvor/pkg/livereload" ) +type Options struct { + Source string + Destination string + Redirect bool + Development bool + Config *config.Config +} + +type Website struct { + config *config.Config + log *log.Logger + reader storage.Reader + *server.App +} + type webHandler func(http.ResponseWriter, *http.Request) *ihttp.Error type WrappedWebHandler struct {
@@ -51,22 +75,68 @@ }
} } -func NewMux( - cfg *config.Config, - reader *files.Reader, +func New( + opts *Options, log *log.Logger, -) (mux *http.ServeMux, err error) { - mux = &http.ServeMux{} +) (*Website, error) { + website := &Website{ + config: opts.Config, + log: log, + } + builderOptions := &builder.Options{ + Source: opts.Source, + Development: opts.Development, + Destination: opts.Destination, + } + + mux := &http.ServeMux{} templates.Setup() + cfg := opts.Config + + err := rebuild(builderOptions, cfg, log) + if err != nil { + return nil, errors.WithMessage(err, "could not build site") + } + + if opts.Development { + liveReload := livereload.New() + mux.Handle("/_/reload", liveReload) + liveReload.Start() + fw, err := watcher.New(log.Named("watcher")) + if err != nil { + return nil, errors.WithMessage(err, "could not create file watcher") + } + err = fw.AddRecursive(opts.Source) + if err != nil { + return nil, errors.WithMessage( + err, + "could not add directory to file watcher", + ) + } + + go fw.Start(func(filename string) { + log.Info("rebuilding site", "changed_file", filename) + err := rebuild(builderOptions, cfg, log) + if err != nil { + log.Error("error rebuilding site", "error", err) + } + }) + } + + website.reader, err = files.NewReader(builderOptions.Destination, log.Named("reader")) + if err != nil { + return nil, errors.WithMessage(err, "error creating sqlite reader") + } + mux.Handle("/", wrapHandler(cfg, func(w http.ResponseWriter, r *http.Request) *ihttp.Error { - urlPath, shouldRedirect := reader.CanonicalisePath(r.URL.Path) + urlPath, shouldRedirect := website.reader.CanonicalisePath(r.URL.Path) if shouldRedirect { http.Redirect(w, r, urlPath, 302) return nil } - file, err := reader.GetFile(urlPath) + file, err := website.reader.GetFile(urlPath) if err != nil { return &ihttp.Error{ Message: "Error reading file",
@@ -129,5 +199,57 @@ u := cfg.OIDCHost.JoinPath(oidcPath)
http.Redirect(w, r, u.String(), 302) }) - return mux, nil + website.App = &server.App{ + Domain: cfg.Domains[0], + Handler: mux, + } + + return website, nil +} + +func (website *Website) MakeRedirectorApp() *server.App { + mux := http.NewServeMux() + + re := regexp.MustCompile( + "^(.*)\\." + strings.ReplaceAll(website.config.WildcardDomain, ".", `\.`) + "$", + ) + replace := "${1}." + website.config.Domains[0] + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + switch { + case slices.Contains(website.config.Domains, r.Host): + path, _ := website.reader.CanonicalisePath(r.URL.Path) + http.Redirect( + w, + r, + website.config.BaseURL.JoinPath(path).String(), + http.StatusMovedPermanently, + ) + case re.MatchString(r.Host): + url := website.config.BaseURL.JoinPath() + url.Host = re.ReplaceAllString(r.Host, replace) + http.Redirect(w, r, url.String(), http.StatusTemporaryRedirect) + case true: + http.NotFound(w, r) + } + }) + + return &server.App{ + Handler: mux, + } +} + +func updateCSPHashes(config *config.Config, r *builder.Result) { + for i, h := range r.Hashes { + config.CSP.StyleSrc[i] = fmt.Sprintf("'%s'", h) + } +} + +func rebuild(builderConfig *builder.Options, config *config.Config, log *log.Logger) error { + r, err := builder.BuildSite(builderConfig, config, log.Named("builder")) + if err != nil { + return errors.WithMessage(err, "could not build site") + } + updateCSPHashes(config, r) + + return nil }