106 lines
2.9 KiB
Go
106 lines
2.9 KiB
Go
|
|
package routes
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bakonpancakz/homepage/env"
|
||
|
|
"bakonpancakz/homepage/include"
|
||
|
|
"bytes"
|
||
|
|
"compress/gzip"
|
||
|
|
"fmt"
|
||
|
|
"log"
|
||
|
|
"net/http"
|
||
|
|
"strings"
|
||
|
|
"sync"
|
||
|
|
"text/template"
|
||
|
|
)
|
||
|
|
|
||
|
|
var articleCache sync.Map
|
||
|
|
|
||
|
|
func PrepareBlogArticles(notFoundHandler HttpHandler) HttpHandler {
|
||
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
||
|
|
for _, art := range env.Database.Articles {
|
||
|
|
if strings.Compare(art.Slug, r.PathValue("slug")) == 0 {
|
||
|
|
|
||
|
|
compress := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
|
||
|
|
cacheKeyStandard := fmt.Sprintf("art_%s_%t", art.Slug, true)
|
||
|
|
cacheKeyCompress := fmt.Sprintf("art_%s_%t", art.Slug, false)
|
||
|
|
|
||
|
|
// Check Browser Cache
|
||
|
|
if r.Header.Get("If-None-Match") == env.VERSION {
|
||
|
|
w.WriteHeader(http.StatusNotModified)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check Template Cache
|
||
|
|
w.Header().Set("ETag", env.VERSION)
|
||
|
|
w.Header().Set("Content-Type", "text/html")
|
||
|
|
w.Header().Set("Cache-Control", "public, max-age=3600, must-revalidate")
|
||
|
|
if compress {
|
||
|
|
if v, ok := articleCache.Load(cacheKeyCompress); ok {
|
||
|
|
w.Header().Set("Content-Encoding", "gzip")
|
||
|
|
w.Write(v.([]byte))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if v, ok := articleCache.Load(cacheKeyStandard); ok {
|
||
|
|
w.Write(v.([]byte))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Render Document
|
||
|
|
tmpl, err := template.ParseFS(
|
||
|
|
include.Templates,
|
||
|
|
include.PreparePath(include.Templates, "base.html"),
|
||
|
|
include.PreparePath(include.Templates, "blog_article.html"),
|
||
|
|
include.PreparePath(include.Templates, fmt.Sprint("articles/", art.BaseTemplate)),
|
||
|
|
include.PreparePath(include.Templates, "icons/logo_facebook.svg"),
|
||
|
|
include.PreparePath(include.Templates, "icons/logo_linkedin.svg"),
|
||
|
|
include.PreparePath(include.Templates, "icons/logo_x.svg"),
|
||
|
|
include.PreparePath(include.Templates, "icons/icon_top.svg"),
|
||
|
|
include.PreparePath(include.Templates, "icons/icon_home.svg"),
|
||
|
|
include.PreparePath(include.Templates, "icons/icon_share.svg"),
|
||
|
|
include.PreparePath(include.Templates, "icons/icon_stack.svg"),
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
log.Println("[routes/blog] Template Error:", err)
|
||
|
|
w.WriteHeader(http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var b bytes.Buffer
|
||
|
|
if err = tmpl.ExecuteTemplate(&b, "base.html", map[string]any{
|
||
|
|
"Title": art.Title,
|
||
|
|
"Version": env.VERSION,
|
||
|
|
"Site": env.Database.Site,
|
||
|
|
"Article": art,
|
||
|
|
}); err != nil {
|
||
|
|
log.Println("[routes/blog] Template Error:", err)
|
||
|
|
w.WriteHeader(http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Compress Document
|
||
|
|
var c bytes.Buffer
|
||
|
|
g := gzip.NewWriter(&c)
|
||
|
|
g.Write(b.Bytes())
|
||
|
|
g.Close()
|
||
|
|
|
||
|
|
if include.Embedded {
|
||
|
|
articleCache.Store(cacheKeyStandard, b.Bytes())
|
||
|
|
articleCache.Store(cacheKeyCompress, c.Bytes())
|
||
|
|
}
|
||
|
|
|
||
|
|
// Send Document
|
||
|
|
if compress {
|
||
|
|
w.Header().Set("Content-Encoding", "gzip")
|
||
|
|
w.Write(c.Bytes())
|
||
|
|
} else {
|
||
|
|
w.Write(b.Bytes())
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
notFoundHandler(w, r)
|
||
|
|
}
|
||
|
|
}
|