package httpx
import (
"html/template"
"net/http"
"time"
"github.com/FAU-CDI/wisski-distillery/pkg/timex"
"github.com/rs/zerolog"
)
const HTMLFlushInterval = time.Second / 10
// WriteHTML writes a html response of type T to w.
// If an error occured, writes an error response instead.
func WriteHTML[T any](result T, err error, template *template.Template, templateName string, w http.ResponseWriter, r *http.Request) (e error) {
// log any error that occurs;
defer func() {
if e != nil {
zerolog.Ctx(r.Context()).Err(e).Str("path", r.URL.String()).Msg("error rendering template")
}
}()
// create a synced respone writer
sw := &SyncedResponseWriter{ResponseWriter: w}
done := make(chan struct{})
defer close(done)
// and regularly flush it until the end of the function
go func() {
timer := timex.NewTimer()
defer timex.ReleaseTimer(timer)
for {
timer.Reset(HTMLFlushInterval)
select {
case <-timer.C:
sw.Flush()
case <-done:
return
}
}
}()
// intercept any errors
if HTMLInterceptor.Intercept(sw, r, err) {
return nil
}
// write out the response as html
sw.Header().Set("Content-Type", "text/html")
sw.WriteHeader(http.StatusOK)
// minify html!
minifier := MinifyHTMLWriter(sw)
defer minifier.Close()
// and return the template
if templateName != "" {
return template.ExecuteTemplate(minifier, templateName, result)
} else {
return template.Execute(minifier, result)
}
}
type HTMLHandler[T any] struct {
Handler func(r *http.Request) (T, error)
Template *template.Template // called with T
TemplateName string // name of template to render, defaults to root
}
// ServeHTTP calls j(r) and returns json
func (h HTMLHandler[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// call the function
result, err := h.Handler(r)
WriteHTML(result, err, h.Template, h.TemplateName, w, r)
}