package website
import (
"net/http"
"regexp"
"slices"
"strings"
"github.com/kevinpollet/nego"
"alin.ovh/homestead/domain/analytics"
calendar "alin.ovh/homestead/domain/calendar/templates"
"alin.ovh/homestead/domain/web/server"
"alin.ovh/homestead/domain/web/templates"
ihttp "alin.ovh/homestead/shared/http"
)
var (
ErrReadingFile = ihttp.NewError("Error reading file", http.StatusInternalServerError)
ErrNotFound = ihttp.NewError("File not found", http.StatusNotFound)
ErrRenderFailure = ihttp.NewError("Error rendering template", http.StatusInternalServerError)
feedHeaders = map[string]string{
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Max-Age": "3600",
}
)
func (website *Website) ErrorHandler(err ihttp.Error, w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.Header.Get("Accept"), "text/html") {
w.WriteHeader(err.StatusCode())
err := templates.Error(*website.siteSettings, err).Render(w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
} else {
http.Error(w, err.Error(), err.StatusCode())
}
}
func (website *Website) ServeHTTP(w http.ResponseWriter, r *http.Request) ihttp.Error {
urlPath := r.URL.Path
if r.URL.Query().Has("go-get") && r.URL.Query().Get("go-get") == "1" {
urlPath = "/go" + r.URL.Path
}
urlPath, shouldRedirect := website.reader.CanonicalisePath(urlPath)
if shouldRedirect {
http.Redirect(w, r, urlPath, http.StatusFound)
return nil
}
file, err := website.reader.GetFile(urlPath)
if err != nil {
website.log.Warn("Error reading file", "error", err)
return ErrReadingFile.WithCause(err)
}
if file == nil {
return ErrNotFound
}
analytics.WithTitle(r, file.Title)
w.Header().Add("ETag", file.Etag)
w.Header().Add("Vary", "Accept-Encoding")
for k, v := range ExtraHeaders {
w.Header().Add(k, v)
}
enc := nego.NegotiateContentEncoding(r, file.AvailableEncodings()...)
if enc != "" {
w.Header().Add("Content-Encoding", enc)
}
w.Header().Add("Content-Type", file.ContentType)
if file.ContentType == "application/xml" {
for k, v := range feedHeaders {
w.Header().Add(k, v)
}
}
http.ServeContent(w, r, file.Path, file.LastModified, file.Encodings[enc])
return nil
}
func (website *Website) Calendar(w http.ResponseWriter, r *http.Request) ihttp.Error {
analytics.WithTitle(r, "Calendar")
err := calendar.CalendarPage(*website.siteSettings, website.calendar).Render(w)
if err != nil {
return ErrRenderFailure.WithCause(err)
}
return nil
}
func (website *Website) MakeRedirectorApp() *server.App {
mux := ihttp.NewServeMux(website.log.Named("http"))
website.identity.RegisterHandlers(mux)
re := regexp.MustCompile(
"^(.*)\\." + strings.ReplaceAll(website.config.WildcardDomain, ".", `\.`) + "$",
)
replace := "${1}." + website.config.Domains[0]
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) ihttp.Error {
switch {
case r.URL.Query().Has("go-get") && r.URL.Query().Get("go-get") == "1":
return website.ServeHTTP(w, r)
case slices.Contains(website.config.Domains, r.Host):
path, _ := website.reader.CanonicalisePath(r.URL.Path)
ihttp.PermanentRedirect(w, r, website.config.BaseURL.JoinPath(path))
case re.MatchString(r.Host):
url := website.config.BaseURL.JoinPath()
url.Host = re.ReplaceAllString(r.Host, replace)
ihttp.TemporaryRedirect(w, r, url)
case true:
http.NotFound(w, r)
}
return nil
})
return &server.App{
WildcardDomain: website.config.WildcardDomain,
Domains: website.config.Domains,
Handler: mux,
}
}
domain/web/mux.go (view raw)