package main import ( "crypto/sha1" "fmt" "io/ioutil" "mime" "net/http" "os" "path" "path/filepath" "strings" ) const assetsFolder = "templates/assets" var assetFileNames map[string]string = map[string]string{} var assetFiles map[string]*assetFile = map[string]*assetFile{} type assetFile struct { contentType string body []byte } func initTemplateAssets() (err error) { err = filepath.Walk(assetsFolder, func(path string, info os.FileInfo, err error) error { if info.Mode().IsRegular() { compiled, err := compileAsset(path) if err != nil { return err } if compiled != "" { assetFileNames[strings.TrimPrefix(path, assetsFolder+"/")] = compiled } } return nil }) if err != nil { return err } return nil } func compileAsset(name string) (compiledFileName string, err error) { originalContent, err := ioutil.ReadFile(name) if err != nil { return } ext := path.Ext(name) var compiledContent []byte compiledExt := ext switch ext { case ".js": compiledContent, err = minifier.Bytes("application/javascript", originalContent) if err != nil { return } case ".css": compiledContent, err = minifier.Bytes("text/css", originalContent) if err != nil { return } default: // Just copy the file compiledContent = originalContent } sha := sha1.New() sha.Write(compiledContent) hash := fmt.Sprintf("%x", sha.Sum(nil)) compiledFileName = hash + compiledExt assetFiles[compiledFileName] = &assetFile{ contentType: mime.TypeByExtension(compiledExt), body: compiledContent, } return } // Function for templates func assetFileName(fileName string) string { return "/" + assetFileNames[fileName] } func allAssetPaths() []string { var paths []string for _, name := range assetFileNames { paths = append(paths, "/"+name) } return paths } // Gets only called by registered paths func serveAsset(w http.ResponseWriter, r *http.Request) { f := strings.TrimPrefix(r.URL.Path, "/") af, ok := assetFiles[f] if !ok { serve404(w, r) return } w.Header().Set("Cache-Control", "public,max-age=31536000,immutable") w.Header().Set(contentType, af.contentType) w.Write(af.body) }