Added basic template rendering.

This commit is contained in:
Marvin Blum
2019-12-12 22:12:48 +01:00
parent 8676f7c9cb
commit 290aa1e0ea
17 changed files with 212 additions and 11 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.idea/

3
go.mod
View File

@@ -3,7 +3,8 @@ module github.com/Kugelschieber/schnittfest
go 1.13
require (
github.com/emvi/logbuch v0.0.0-20191002134629-fd76a46de20c // indirect
github.com/emvi/iso-639-1 v0.0.0-20190602002026-5ad2c26993cd
github.com/emvi/logbuch v0.0.0-20191002134629-fd76a46de20c
github.com/gorilla/mux v1.7.3 // indirect
github.com/rs/cors v1.7.0 // indirect
)

2
go.sum
View File

@@ -1,3 +1,5 @@
github.com/emvi/iso-639-1 v0.0.0-20190602002026-5ad2c26993cd h1:x5d+Ikfu2nrNXdC8WThAdCcdajxG4JKqCkLpqAFqmpc=
github.com/emvi/iso-639-1 v0.0.0-20190602002026-5ad2c26993cd/go.mod h1:mghC4MDFyszxzH98ujf/K5whvB6B0nV4qCa5u94dP84=
github.com/emvi/logbuch v0.0.0-20191002134629-fd76a46de20c h1:LsG8/aichRG7oGqTz77PQJuArY1id0kQttxNBuC/C3Q=
github.com/emvi/logbuch v0.0.0-20191002134629-fd76a46de20c/go.mod h1:J2Wgbr3BuSc1JO+D2MBVh6q3WPVSK5GzktwWz8pvkKw=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=

24
main.go
View File

@@ -1,6 +1,7 @@
package main
import (
"github.com/Kugelschieber/schnittfest/pages"
"github.com/emvi/logbuch"
"github.com/gorilla/mux"
"github.com/rs/cors"
@@ -23,6 +24,7 @@ const (
func configureLog() {
logbuch.Info("Configure logging...")
logbuch.SetFormatter(logbuch.NewFieldFormatter("", "\t"))
level := strings.ToLower(os.Getenv("SCHNITTFEST_LOGLEVEL"))
if level == "debug" {
@@ -45,20 +47,21 @@ func logEnvConfig() {
func setupRouter() *mux.Router {
router := mux.NewRouter()
router.PathPrefix(staticDirPrefix).Handler(http.StripPrefix(staticDirPrefix, http.FileServer(http.Dir(staticDir))))
router.PathPrefix(staticDirPrefix).Handler(http.StripPrefix(staticDirPrefix, http.FileServer(http.Dir(staticDir)))).Methods("GET")
router.HandleFunc("/", pages.LandingPageHandler).Methods("GET")
return router
}
func configureCors(router *mux.Router) http.Handler {
logbuch.Info("Configuring CORS...")
origins := strings.Split(os.Getenv("ACCWEB_ALLOWED_ORIGINS"), ",")
origins := strings.Split(os.Getenv("SCHNITTFEST_ALLOWED_ORIGINS"), ",")
c := cors.New(cors.Options{
AllowedOrigins: origins,
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowedHeaders: []string{"*"},
AllowCredentials: true,
Debug: strings.ToLower(os.Getenv("ACCWEB_CORS_LOGLEVEL")) == "debug",
Debug: strings.ToLower(os.Getenv("SCHNITTFEST_CORS_LOGLEVEL")) == "debug",
})
return c.Handler(router)
}
@@ -70,16 +73,16 @@ func start(handler http.Handler) {
readTimeout := defaultHttpReadTimeout
var err error
if os.Getenv("ACCWEB_HTTP_WRITE_TIMEOUT") != "" {
writeTimeout, err = strconv.Atoi(os.Getenv("ACCWEB_HTTP_WRITE_TIMEOUT"))
if os.Getenv("SCHNITTFEST_HTTP_WRITE_TIMEOUT") != "" {
writeTimeout, err = strconv.Atoi(os.Getenv("SCHNITTFEST_HTTP_WRITE_TIMEOUT"))
if err != nil {
logbuch.Fatal(err.Error())
}
}
if os.Getenv("ACCWEB_HTTP_READ_TIMEOUT") != "" {
readTimeout, err = strconv.Atoi(os.Getenv("ACCWEB_HTTP_READ_TIMEOUT"))
if os.Getenv("SCHNITTFEST_HTTP_READ_TIMEOUT") != "" {
readTimeout, err = strconv.Atoi(os.Getenv("SCHNITTFEST_HTTP_READ_TIMEOUT"))
if err != nil {
logbuch.Fatal(err.Error())
@@ -90,15 +93,15 @@ func start(handler http.Handler) {
server := &http.Server{
Handler: handler,
Addr: os.Getenv("ACCWEB_HOST"),
Addr: os.Getenv("SCHNITTFEST_HOST"),
WriteTimeout: time.Duration(writeTimeout) * time.Second,
ReadTimeout: time.Duration(readTimeout) * time.Second,
}
// TODO certmagic
if strings.ToLower(os.Getenv("ACCWEB_TLS_ENABLE")) == "true" {
if strings.ToLower(os.Getenv("SCHNITTFEST_TLS_ENABLE")) == "true" {
logbuch.Info("TLS enabled")
logbuch.Fatal("Error starting server", server.ListenAndServeTLS(os.Getenv("ACCWEB_TLS_CERT"), os.Getenv("ACCWEB_TLS_PKEY")))
logbuch.Fatal("Error starting server", server.ListenAndServeTLS(os.Getenv("SCHNITTFEST_TLS_CERT"), os.Getenv("SCHNITTFEST_TLS_PKEY")))
} else {
logbuch.Fatal("Error starting server", server.ListenAndServe())
}
@@ -107,6 +110,7 @@ func start(handler http.Handler) {
func main() {
configureLog()
logEnvConfig()
pages.LoadTemplates()
router := setupRouter()
corsConfig := configureCors(router)
start(corsConfig)

5
pages/footer.go Normal file
View File

@@ -0,0 +1,5 @@
package pages
var footerComponentI18n = map[string]map[string]string{
"de": {},
}

View File

@@ -1 +1,38 @@
package pages
import (
"github.com/Kugelschieber/schnittfest/util"
"github.com/emvi/logbuch"
"net/http"
)
var landingPageI18n = map[string]map[string]string{
"de": {
"test": "Hello World",
},
}
func LandingPageHandler(w http.ResponseWriter, r *http.Request) {
tpl := tplCache.GetTemplate(landingPageTemplate)
if tpl == nil {
w.WriteHeader(http.StatusNotFound)
return
}
langCode := util.GetLangCode(r)
data := struct {
Vars map[string]string
NavbarVars map[string]string
FooterVars map[string]string
}{
landingPageI18n[langCode],
navbarComponentI18n[langCode],
footerComponentI18n[langCode],
}
if err := tpl.Execute(w, &data); err != nil {
logbuch.Error("Error rendering landing page", logbuch.Fields{"err": err})
w.WriteHeader(http.StatusInternalServerError)
}
}

5
pages/navbar.go Normal file
View File

@@ -0,0 +1,5 @@
package pages
var navbarComponentI18n = map[string]map[string]string{
"de": {},
}

43
pages/template.go Normal file
View File

@@ -0,0 +1,43 @@
package pages
import (
"github.com/Kugelschieber/schnittfest/util"
"github.com/emvi/logbuch"
"os"
"path/filepath"
)
const (
defaultTemplateBase = "template"
landingPageTemplate = "landing_page"
notfoundPageTemplate = "notfound_page"
)
var (
tplCache *util.TemplateCache
)
func LoadTemplates() {
tplCache = util.NewTemplateCache()
templateBase := os.Getenv("SCHNITTFEST_TEMPLATE_BASE")
if templateBase == "" {
templateBase = defaultTemplateBase
}
if _, err := tplCache.ParseFiles(landingPageTemplate, filepath.Join(templateBase, "landing_page.html"),
filepath.Join(templateBase, "head.html"),
filepath.Join(templateBase, "end.html"),
filepath.Join(templateBase, "navbar.html"),
filepath.Join(templateBase, "footer.html")); err != nil {
logbuch.Fatal("Error loading landing page template", logbuch.Fields{"err": err})
}
if _, err := tplCache.ParseFiles(notfoundPageTemplate, filepath.Join(templateBase, "404_page.html"),
filepath.Join(templateBase, "head.html"),
filepath.Join(templateBase, "end.html"),
filepath.Join(templateBase, "navbar.html"),
filepath.Join(templateBase, "footer.html")); err != nil {
logbuch.Fatal("Error loading 404 page template", logbuch.Fields{"err": err})
}
}

8
run_dev.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
export SCHNITTFEST_HOST=localhost:8080
export SCHNITTFEST_LOGLEVEL=debug
export SCHNITTFEST_ALLOWED_ORIGINS=*
export SCHNITTFEST_TLS_ENABLE=false
go run main.go

0
template/404_page.html Normal file
View File

0
template/end.html Normal file
View File

0
template/footer.html Normal file
View File

0
template/head.html Normal file
View File

View File

@@ -0,0 +1 @@
{{index .Vars "test"}}

0
template/navbar.html Normal file
View File

55
util/cache.go Normal file
View File

@@ -0,0 +1,55 @@
package util
import (
"github.com/emvi/logbuch"
"html/template"
"sync"
)
// TemplateCache caches templates.
type TemplateCache struct {
templates map[string]*template.Template
mutex sync.RWMutex
}
// NewTemplateCache creates a new template cache.
func NewTemplateCache() *TemplateCache {
return &TemplateCache{templates: make(map[string]*template.Template)}
}
// ParseFiles parses the given template files and stores them as one template called name.
func (tplcache *TemplateCache) ParseFiles(name string, files ...string) (*template.Template, error) {
if tplcache.templates[name] != nil {
tplcache.mutex.RLock()
defer tplcache.mutex.RUnlock()
return tplcache.templates[name], nil
}
tplcache.mutex.Lock()
defer tplcache.mutex.Unlock()
logbuch.Debug("Caching template", logbuch.Fields{"name": name})
tpl, err := template.ParseFiles(files...)
if err != nil {
return nil, err
}
tplcache.templates[name] = tpl
return tpl, nil
}
// GetTemplate returns a cached template by name or nil if not found.
func (tplcache *TemplateCache) GetTemplate(name string) *template.Template {
if tplcache.templates[name] != nil {
tplcache.mutex.RLock()
defer tplcache.mutex.RUnlock()
return tplcache.templates[name]
}
return nil
}
// Clear clears the template cache.
func (tplcache *TemplateCache) Clear() {
tplcache.templates = make(map[string]*template.Template)
}

39
util/lang.go Normal file
View File

@@ -0,0 +1,39 @@
package util
import (
iso6391 "github.com/emvi/iso-639-1"
"net/http"
"strings"
)
var (
supportedLangCodes = map[string]bool{"de": true}
defaultLangCode = "de"
)
func getLangCodeFromHeader(r *http.Request) string {
header := r.Header.Get("Accept-Language")
parts := strings.Split(header, ";")
if len(parts) == 0 || len(parts[0]) < 2 {
return defaultLangCode
}
code := strings.ToLower(parts[0][:2])
if iso6391.ValidCode(code) {
return code
}
return defaultLangCode
}
func GetLangCode(r *http.Request) string {
langCode := getLangCodeFromHeader(r)
if _, ok := supportedLangCodes[langCode]; ok {
return langCode
}
return defaultLangCode
}