1
0
Fork 0
datetime.link/app.go

96 lines
2.3 KiB
Go

package main
import (
"html/template"
"net/http"
"github.com/serverwentdown/datetime.link/data"
"go.uber.org/zap"
)
// Datetime is the main application server
type Datetime struct {
*http.ServeMux
tmpl *template.Template
cities map[string]*data.City
}
// NewDatetime creates an application instance. It assumes certain resources
// like templates and data exist.
func NewDatetime() (*Datetime, error) {
// Data
tmpl, err := template.ParseGlob("templates/*")
if err != nil {
return nil, err
}
cities, err := data.ReadCities()
if err != nil {
return nil, err
}
// Mux
mux := http.NewServeMux()
app := &Datetime{mux, tmpl, cities}
// Routes
mux.Handle("/data/", http.FileServer(http.Dir(".")))
mux.Handle("/js/", http.FileServer(http.Dir("assets")))
mux.Handle("/css/", http.FileServer(http.Dir("assets")))
mux.Handle("/favicon.ico", http.FileServer(http.Dir("assets")))
mux.HandleFunc("/", app.index)
return app, nil
}
// index handles all incoming page requests
func (app Datetime) index(w http.ResponseWriter, req *http.Request) {
var err error
if req.Method != http.MethodGet && req.Method != http.MethodHead {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
accept := req.Header.Get("Accept")
tmpl, acceptable := app.chooseTemplate(accept)
if !acceptable {
w.WriteHeader(http.StatusNotAcceptable)
return
}
if tmpl == nil {
l.Error("unable to find template", zap.String("accept", accept))
w.WriteHeader(http.StatusInternalServerError)
return
}
if req.Method == http.MethodHead {
return
}
l.Debug("", zap.Reflect("url", req.URL))
err = tmpl.Execute(w, nil)
if err != nil {
l.Error("templating failed", zap.Error(err))
w.WriteHeader(http.StatusInternalServerError) // Usually this will fail
return
}
}
// chooseTemplate returns a template based on the accepted mime types from the
// client, and if a template cannot be found it returns a nil template.
func (app Datetime) chooseTemplate(accept string) (t *template.Template, acceptable bool) {
responseType := chooseResponseType(accept)
templateName := ""
switch responseType {
case responsePlain:
templateName = "index.txt"
case responseHTML:
templateName = "index.html"
case responseAny:
templateName = "index.txt"
case responseUnknown:
return nil, false
}
return app.tmpl.Lookup(templateName), true
}