Improved page rendering and caching and disabled tracking for not found handler.

This commit is contained in:
Marvin Blum
2020-06-21 23:18:52 +02:00
committed by Marvin Blum
parent 38bfac3a8a
commit 619924b4ec
3 changed files with 78 additions and 65 deletions

View File

@@ -2,6 +2,7 @@ package blog
import ( import (
"fmt" "fmt"
"github.com/Kugelschieber/marvinblum.de/tpl"
emvi "github.com/emvi/api-go" emvi "github.com/emvi/api-go"
"github.com/emvi/logbuch" "github.com/emvi/logbuch"
"io/ioutil" "io/ioutil"
@@ -9,6 +10,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sync"
"time" "time"
) )
@@ -29,11 +31,13 @@ type Blog struct {
articles map[string]emvi.Article // id -> article articles map[string]emvi.Article // id -> article
articlesYear map[int][]emvi.Article // year -> articles articlesYear map[int][]emvi.Article // year -> articles
nextUpdate time.Time nextUpdate time.Time
cache *tpl.Cache
m sync.Mutex
} }
func NewBlog() *Blog { func NewBlog(cache *tpl.Cache) *Blog {
logbuch.Info("Initializing blog") logbuch.Info("Initializing blog")
b := new(Blog) b := &Blog{cache: cache}
b.client = emvi.NewClient(os.Getenv("MB_EMVI_CLIENT_ID"), b.client = emvi.NewClient(os.Getenv("MB_EMVI_CLIENT_ID"),
os.Getenv("MB_EMVI_CLIENT_SECRET"), os.Getenv("MB_EMVI_CLIENT_SECRET"),
os.Getenv("MB_EMVI_ORGA"), os.Getenv("MB_EMVI_ORGA"),
@@ -76,6 +80,8 @@ func (blog *Blog) GetLatestArticles() []emvi.Article {
} }
func (blog *Blog) loadArticles() { func (blog *Blog) loadArticles() {
blog.m.Lock()
defer blog.m.Unlock()
logbuch.Info("Refreshing blog articles...") logbuch.Info("Refreshing blog articles...")
articles, offset, count := make(map[string]emvi.Article), 0, 1 articles, offset, count := make(map[string]emvi.Article), 0, 1
var err error var err error
@@ -188,6 +194,7 @@ func (blog *Blog) setArticles(articles map[string]emvi.Article) {
func (blog *Blog) refreshIfRequired() { func (blog *Blog) refreshIfRequired() {
if blog.nextUpdate.Before(time.Now()) { if blog.nextUpdate.Before(time.Now()) {
blog.cache.Clear()
blog.loadArticles() blog.loadArticles()
} }
} }

41
main.go
View File

@@ -31,6 +31,7 @@ const (
var ( var (
tracker *pirsch.Tracker tracker *pirsch.Tracker
tplCache *tpl.Cache
blogInstance *blog.Blog blogInstance *blog.Blog
) )
@@ -88,32 +89,29 @@ func setupTracker() {
func serveAbout() http.HandlerFunc { func serveAbout() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
tracker.Hit(r) tracker.Hit(r)
data := struct { tplCache.Render(w, "about.html", struct {
Articles []emvi.Article Articles []emvi.Article
}{ }{
blogInstance.GetLatestArticles(), blogInstance.GetLatestArticles(),
})
}
} }
if err := tpl.Get().ExecuteTemplate(w, "about.html", data); err != nil { func serveLegal() http.HandlerFunc {
logbuch.Error("Error executing blog template", logbuch.Fields{"err": err}) return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError) tracker.Hit(r)
} tplCache.Render(w, "legal.html", nil)
} }
} }
func serveBlogPage() http.HandlerFunc { func serveBlogPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
tracker.Hit(r) tracker.Hit(r)
data := struct { tplCache.Render(w, "blog.html", struct {
Articles map[int][]emvi.Article Articles map[int][]emvi.Article
}{ }{
blogInstance.GetArticles(), blogInstance.GetArticles(),
} })
if err := tpl.Get().ExecuteTemplate(w, "blog.html", data); err != nil {
logbuch.Error("Error executing blog template", logbuch.Fields{"err": err})
w.WriteHeader(http.StatusInternalServerError)
}
} }
} }
@@ -135,7 +133,7 @@ func serveBlogArticle() http.HandlerFunc {
return return
} }
data := struct { tplCache.Render(w, "article.html", struct {
Title string Title string
Content template.HTML Content template.HTML
Published time.Time Published time.Time
@@ -143,12 +141,13 @@ func serveBlogArticle() http.HandlerFunc {
article.LatestArticleContent.Title, article.LatestArticleContent.Title,
template.HTML(article.LatestArticleContent.Content), template.HTML(article.LatestArticleContent.Content),
article.Published, article.Published,
})
}
} }
if err := tpl.Get().ExecuteTemplate(w, "article.html", data); err != nil { func serveNotFound() http.HandlerFunc {
logbuch.Error("Error executing blog article template", logbuch.Fields{"err": err}) return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError) tplCache.Render(w, "notfound.html", nil)
}
} }
} }
@@ -157,9 +156,9 @@ func setupRouter() *mux.Router {
router.PathPrefix(staticDirPrefix).Handler(http.StripPrefix(staticDirPrefix, gziphandler.GzipHandler(http.FileServer(http.Dir(staticDir))))) router.PathPrefix(staticDirPrefix).Handler(http.StripPrefix(staticDirPrefix, gziphandler.GzipHandler(http.FileServer(http.Dir(staticDir)))))
router.Handle("/blog/{slug}", serveBlogArticle()) router.Handle("/blog/{slug}", serveBlogArticle())
router.Handle("/blog", serveBlogPage()) router.Handle("/blog", serveBlogPage())
router.Handle("/legal", tpl.ServeTemplate("legal.html", tracker)) router.Handle("/legal", serveLegal())
router.Handle("/", serveAbout()) router.Handle("/", serveAbout())
router.NotFoundHandler = tpl.ServeTemplate("notfound.html", tracker) router.NotFoundHandler = serveNotFound()
return router return router
} }
@@ -199,9 +198,9 @@ func start(handler http.Handler) {
func main() { func main() {
configureLog() configureLog()
logEnvConfig() logEnvConfig()
tpl.LoadTemplate()
setupTracker() setupTracker()
blogInstance = blog.NewBlog() tplCache = tpl.NewCache()
blogInstance = blog.NewBlog(tplCache)
router := setupRouter() router := setupRouter()
corsConfig := configureCors(router) corsConfig := configureCors(router)
start(corsConfig) start(corsConfig)

View File

@@ -3,11 +3,11 @@ package tpl
import ( import (
"bytes" "bytes"
"github.com/emvi/logbuch" "github.com/emvi/logbuch"
"github.com/emvi/pirsch"
"github.com/gosimple/slug" "github.com/gosimple/slug"
"html/template" "html/template"
"net/http" "net/http"
"os" "os"
"sync"
"time" "time"
) )
@@ -15,61 +15,68 @@ const (
templateDir = "template/*" templateDir = "template/*"
) )
var ( type Cache struct {
tpl *template.Template tpl *template.Template
tplCache = make(map[string][]byte) cache map[string][]byte
hotReload bool hotReload bool
) m sync.RWMutex
}
var funcMap = template.FuncMap{ func NewCache() *Cache {
cache := &Cache{
cache: make(map[string][]byte),
hotReload: os.Getenv("MB_HOT_RELOAD") == "true",
}
cache.load()
return cache
}
func (cache *Cache) load() {
logbuch.Debug("Loading templates")
funcMap := template.FuncMap{
"slug": slug.Make, "slug": slug.Make,
"format": func(t time.Time, layout string) string { "format": func(t time.Time, layout string) string {
return t.Format(layout) return t.Format(layout)
}, },
} }
func LoadTemplate() {
logbuch.Debug("Loading templates")
var err error var err error
tpl, err = template.New("").Funcs(funcMap).ParseGlob(templateDir) cache.tpl, err = template.New("").Funcs(funcMap).ParseGlob(templateDir)
if err != nil { if err != nil {
logbuch.Fatal("Error loading template", logbuch.Fields{"err": err}) logbuch.Fatal("Error loading template", logbuch.Fields{"err": err})
} }
hotReload = os.Getenv("MB_HOT_RELOAD") == "true" logbuch.Debug("Templates loaded", logbuch.Fields{"hot_reload": cache.hotReload})
logbuch.Debug("Templates loaded", logbuch.Fields{"hot_reload": hotReload})
} }
func renderTemplate(name string) { func (cache *Cache) Render(w http.ResponseWriter, name string, data interface{}) {
cache.m.RLock()
if cache.cache[name] == nil || cache.hotReload {
cache.m.RUnlock()
cache.m.Lock()
defer cache.m.Unlock()
logbuch.Debug("Rendering template", logbuch.Fields{"name": name}) logbuch.Debug("Rendering template", logbuch.Fields{"name": name})
var buffer bytes.Buffer var buffer bytes.Buffer
if err := tpl.ExecuteTemplate(&buffer, name, nil); err != nil { if err := cache.tpl.ExecuteTemplate(&buffer, name, data); err != nil {
logbuch.Fatal("Error executing template", logbuch.Fields{"err": err, "name": name}) logbuch.Error("Error executing template", logbuch.Fields{"err": err, "name": name})
w.WriteHeader(http.StatusInternalServerError)
} }
tplCache[name] = buffer.Bytes() cache.cache[name] = buffer.Bytes()
} else {
cache.m.RUnlock()
} }
func Get() *template.Template { if _, err := w.Write(cache.cache[name]); err != nil {
return tpl logbuch.Error("Error sending response to client", logbuch.Fields{"err": err, "template": name})
w.WriteHeader(http.StatusInternalServerError)
}
} }
func ServeTemplate(name string, tracker *pirsch.Tracker) http.HandlerFunc { func (cache *Cache) Clear() {
// render once so we have it in cache cache.m.Lock()
renderTemplate(name) defer cache.m.Unlock()
cache.cache = make(map[string][]byte)
return func(w http.ResponseWriter, r *http.Request) {
tracker.Hit(r)
if hotReload {
LoadTemplate()
renderTemplate(name)
}
if _, err := w.Write(tplCache[name]); err != nil {
logbuch.Error("Error returning page to client", logbuch.Fields{"err": err, "name": name})
}
}
} }