package website import ( "encoding/json" "fmt" "net/http" "regexp" "slices" "strings" 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" "github.com/kevinpollet/nego" ) func (website *Website) webfinger(w http.ResponseWriter, r *http.Request) *ihttp.Error { if r.URL.Query().Get("resource") == website.acctResource { w.Header().Add("Content-Type", "application/jrd+json") w.Header().Add("Access-Control-Allow-Origin", "*") if err := json.NewEncoder(w).Encode(website.me); err != nil { return ihttp.InternalServerError("Failed to encode webfinger response", err) } return nil } return ihttp.NotFound("Resource not found") } 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.Code) err := templates.Error(*website.siteSettings, err).Render(w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } else { http.Error(w, err.Message, err.Code) } } 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 { website.counter.Count(r, "302") 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 ihttp.InternalServerError("Error reading file", err) } if file == nil { website.counter.Count(r, "404") return ihttp.NotFound("File not found") } website.counter.Count(r, file.Title) w.Header().Add("ETag", file.Etag) w.Header().Add("Vary", "Accept-Encoding") if file.StyleHash != "" { CSPHeader.StyleSrc = append(CSPHeader.StyleSrc, fmt.Sprintf("'%s'", file.StyleHash)) } w.Header().Add("Content-Security-Policy", CSPHeader.String()) 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.Headers != nil { for k, v := range file.Headers { 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 { website.counter.Count(r, "Calendar") err := calendar.CalendarPage(*website.siteSettings, templates.PageSettings{ Title: "Calendar", TitleAttrs: templates.Attrs{}, BodyAttrs: templates.Attrs{}, }, *website.calendar).Render(w) if err != nil { return &ihttp.Error{ Code: http.StatusInternalServerError, Message: "", Cause: err, } } return nil } func (website *Website) MakeRedirectorApp() *server.App { mux := ihttp.NewServeMux() mux.HandleFunc("/.well-known/webfinger", website.webfinger) 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, } }