package httputils import ( "fmt" "net/http" "os" "path/filepath" "strings" "text/template" "time" ) type Handler = func(http.ResponseWriter, *http.Request) const templateFolder = "templates" var templatePaths, templateModTimes, _ = getTemplates() var templates *template.Template = template.Must(template.ParseFiles(templatePaths...)) func getTemplates() ([]string, map[string]time.Time, error) { var modTimes map[string]time.Time = make(map[string]time.Time) err := filepath.Walk(templateFolder, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.HasSuffix(path, ".html") { modTimes[path] = info.ModTime() } return nil }) paths := make([]string, len(modTimes)) i := 0 for path := range modTimes { paths[i] = path i++ } return paths, modTimes, err } func reloadTemplateIfModified(path string) { fileInfo, _ := os.Stat(path) modTime := fileInfo.ModTime() if modTime.After(templateModTimes[path]) { fmt.Printf("Reloading template %s...\n", path) templates.ParseFiles(path) templateModTimes[path] = modTime } } func reloadTemplatesIfModified() { for _, path := range templatePaths { reloadTemplateIfModified(path) } } const reloadTemplates = true func GenerateHandler( file string, handler func(http.ResponseWriter, *http.Request) bool, data func(http.ResponseWriter, *http.Request) any, methods []string, ) Handler { return func(w http.ResponseWriter, r *http.Request) { // All templates must be reloaded in case of dependencies if reloadTemplates { reloadTemplatesIfModified() } for _, method := range methods { if method == r.Method { goto ok } } w.WriteHeader(http.StatusMethodNotAllowed) return ok: renderTemplate := handler(w, r) if renderTemplate && file != "" { w.Header().Set("Content-Type", "text/html; charset=utf-8") templates.ExecuteTemplate(w, file, data(w, r)) } } } func GenerateSseHandler(handler Handler) Handler { return func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") w.WriteHeader(http.StatusOK) handler(w, r) } }