From a50504f747ba6ad4b3be63e68ca5c24901d6bfde Mon Sep 17 00:00:00 2001 From: Luther Wen Xu Date: Tue, 1 Dec 2020 22:16:56 +0800 Subject: [PATCH] feat: Add static path and per-template style.css The handler now searches in the static path first and fallback to the content directory otherwise. If that fails, the 404 template is rendered as expected. The style.css path is now reserved for templates' style.css file and does not follow the regular search path. This means that /style.css will serve a 404 page even if static/style.css or content/style.css.md exists. --- .gitignore | 1 + handler/handler.go | 48 ++++++++++++++++++++++++++++++++++++++- handler/post.go | 3 ++- handler/template.go | 31 ++++++++++++++++++++++++- templates/debug/style.css | 2 ++ 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 templates/debug/style.css diff --git a/.gitignore b/.gitignore index 686addc..064e02a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ config.json content/ +static/ diff --git a/handler/handler.go b/handler/handler.go index a39c290..72637ac 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -1,6 +1,12 @@ package handler -import "net/http" +import ( + "fmt" + "net/http" + "os" +) + +var fs = http.FileServer(http.Dir("static/")) // Index is the handler that displays the index page. func Index(w http.ResponseWriter, r *http.Request) { @@ -13,5 +19,45 @@ func Handler(w http.ResponseWriter, r *http.Request) { Index(w, r) return } + if r.URL.Path == "/style.css" { + TemplateStyle(w, r) + return + } + if Static(w, r) { + return + } Post(w, r) } + +// TemplateStyle is a handler that servers the style of the current template. +func TemplateStyle(w http.ResponseWriter, r *http.Request) { + if themeCSS == nil { + // 404 if the theme CSS doesn't exist as it's optional. + CurrentTheme.ExecuteTemplate(w, "404.tmpl", nil) + return + } + w.Header().Add("Content-Type", "text/css") + _, _ = w.Write(themeCSS) +} + +// Static is a handler that serves static content if available. +// If it has served something (the static file or an error), it returns true. +func Static(w http.ResponseWriter, r *http.Request) bool { + fileName := "static" + r.URL.Path + + // Check if the file exists and is not a directory. + info, err := os.Stat(fileName) + if os.IsNotExist(err) || (info != nil && info.IsDir()) { + return false + } + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + CurrentTheme.ExecuteTemplate(w, "500.tmpl", fmt.Errorf("error reading static folder: %w", err)) + return true + } + + // The file probably exist now. Even if there's a mismatch, the malicious attacker can only get a + // 404 or a directory listing. + fs.ServeHTTP(w, r) + return true +} diff --git a/handler/post.go b/handler/post.go index d18377f..4ef0610 100644 --- a/handler/post.go +++ b/handler/post.go @@ -1,6 +1,7 @@ package handler import ( + "fmt" "net/http" "os" ) @@ -18,7 +19,7 @@ func Post(w http.ResponseWriter, r *http.Request) { } if err != nil { w.WriteHeader(http.StatusInternalServerError) - CurrentTheme.ExecuteTemplate(w, "500.tmpl", err) + CurrentTheme.ExecuteTemplate(w, "500.tmpl", fmt.Errorf("error reading content folder: %w", err)) return } diff --git a/handler/template.go b/handler/template.go index dfb5ff8..36f18c8 100644 --- a/handler/template.go +++ b/handler/template.go @@ -1,10 +1,19 @@ package handler -import "html/template" +import ( + "html/template" + "io/ioutil" + "os" + + "github.com/sirupsen/logrus" +) // CurrentTheme is the set of template being used. var CurrentTheme *template.Template +// themeCSS is the CSS of the current theme. +var themeCSS []byte + // SetTheme parses the provided theme name and sets it as the current theme. func SetTheme(themeName string) error { var err error @@ -17,5 +26,25 @@ func SetTheme(themeName string) error { folder+"post.tmpl", // Page for posts. ) + if err != nil { + return err + } + + // Check if the style CSS exists and is not a directory. + info, err := os.Stat(folder + "style.css") + if os.IsNotExist(err) || (info != nil && info.IsDir()) { + return nil + } + f, err := os.Open(folder + "style.css") + if err != nil { + return err + } + defer func() { + err := f.Close() + if err != nil { + logrus.Fatalf("Error while closing theme CSS file: %v", err) + } + }() + themeCSS, err = ioutil.ReadAll(f) return err } diff --git a/templates/debug/style.css b/templates/debug/style.css new file mode 100644 index 0000000..41f6463 --- /dev/null +++ b/templates/debug/style.css @@ -0,0 +1,2 @@ +body { margin: 0 } +/* This is style.css of the debug template. */