mirror of
https://github.com/Kugelschieber/marvinblum.git
synced 2026-01-18 14:50:27 +00:00
Improved page rendering and caching and disabled tracking for not found handler.
This commit is contained in:
11
blog/blog.go
11
blog/blog.go
@@ -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
41
main.go
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
|
||||||
return tpl
|
|
||||||
}
|
|
||||||
|
|
||||||
func ServeTemplate(name string, tracker *pirsch.Tracker) http.HandlerFunc {
|
|
||||||
// render once so we have it in cache
|
|
||||||
renderTemplate(name)
|
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
tracker.Hit(r)
|
|
||||||
|
|
||||||
if hotReload {
|
|
||||||
LoadTemplate()
|
|
||||||
renderTemplate(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := w.Write(tplCache[name]); err != nil {
|
if _, err := w.Write(cache.cache[name]); err != nil {
|
||||||
logbuch.Error("Error returning page to client", logbuch.Fields{"err": err, "name": name})
|
logbuch.Error("Error sending response to client", logbuch.Fields{"err": err, "template": name})
|
||||||
}
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cache *Cache) Clear() {
|
||||||
|
cache.m.Lock()
|
||||||
|
defer cache.m.Unlock()
|
||||||
|
cache.cache = make(map[string][]byte)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user