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.
master
Luther Wen Xu 2020-12-01 22:16:56 +07:00
parent ccc23cead0
commit a50504f747
Signed by: chanbakjsd
GPG Key ID: B7D77E3E9D102B70
5 changed files with 82 additions and 3 deletions

1
.gitignore vendored

@ -1,2 +1,3 @@
config.json config.json
content/ content/
static/

@ -1,6 +1,12 @@
package handler 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. // Index is the handler that displays the index page.
func Index(w http.ResponseWriter, r *http.Request) { func Index(w http.ResponseWriter, r *http.Request) {
@ -13,5 +19,45 @@ func Handler(w http.ResponseWriter, r *http.Request) {
Index(w, r) Index(w, r)
return return
} }
if r.URL.Path == "/style.css" {
TemplateStyle(w, r)
return
}
if Static(w, r) {
return
}
Post(w, r) 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
}

@ -1,6 +1,7 @@
package handler package handler
import ( import (
"fmt"
"net/http" "net/http"
"os" "os"
) )
@ -18,7 +19,7 @@ func Post(w http.ResponseWriter, r *http.Request) {
} }
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
CurrentTheme.ExecuteTemplate(w, "500.tmpl", err) CurrentTheme.ExecuteTemplate(w, "500.tmpl", fmt.Errorf("error reading content folder: %w", err))
return return
} }

@ -1,10 +1,19 @@
package handler package handler
import "html/template" import (
"html/template"
"io/ioutil"
"os"
"github.com/sirupsen/logrus"
)
// CurrentTheme is the set of template being used. // CurrentTheme is the set of template being used.
var CurrentTheme *template.Template 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. // SetTheme parses the provided theme name and sets it as the current theme.
func SetTheme(themeName string) error { func SetTheme(themeName string) error {
var err error var err error
@ -17,5 +26,25 @@ func SetTheme(themeName string) error {
folder+"post.tmpl", // Page for posts. 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 return err
} }

@ -0,0 +1,2 @@
body { margin: 0 }
/* This is style.css of the debug template. */