2020-09-19 10:27:07 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha1"
|
2021-04-02 08:28:04 +00:00
|
|
|
"crypto/sha512"
|
|
|
|
"encoding/base64"
|
2020-09-19 10:27:07 +00:00
|
|
|
"fmt"
|
2021-04-02 08:28:04 +00:00
|
|
|
"io"
|
2021-01-10 16:19:08 +00:00
|
|
|
"mime"
|
2020-09-19 10:27:07 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
2020-09-19 10:57:14 +00:00
|
|
|
"strings"
|
2020-09-19 10:27:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const assetsFolder = "templates/assets"
|
|
|
|
|
2021-01-10 16:19:08 +00:00
|
|
|
var assetFileNames map[string]string = map[string]string{}
|
|
|
|
var assetFiles map[string]*assetFile = map[string]*assetFile{}
|
|
|
|
|
|
|
|
type assetFile struct {
|
|
|
|
contentType string
|
2021-04-02 08:28:04 +00:00
|
|
|
sri string
|
2021-01-10 16:19:08 +00:00
|
|
|
body []byte
|
|
|
|
}
|
2020-09-19 10:27:07 +00:00
|
|
|
|
2020-11-11 15:58:44 +00:00
|
|
|
func initTemplateAssets() (err error) {
|
2020-09-19 10:27:07 +00:00
|
|
|
err = filepath.Walk(assetsFolder, func(path string, info os.FileInfo, err error) error {
|
|
|
|
if info.Mode().IsRegular() {
|
2021-01-10 16:19:08 +00:00
|
|
|
compiled, err := compileAsset(path)
|
2020-09-19 10:27:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if compiled != "" {
|
2021-01-10 16:19:08 +00:00
|
|
|
assetFileNames[strings.TrimPrefix(path, assetsFolder+"/")] = compiled
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-08 17:51:07 +00:00
|
|
|
func compileAsset(name string) (string, error) {
|
2021-04-02 08:28:04 +00:00
|
|
|
content, err := os.ReadFile(name)
|
2020-09-19 10:27:07 +00:00
|
|
|
if err != nil {
|
2021-02-08 17:51:07 +00:00
|
|
|
return "", err
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
|
|
|
ext := path.Ext(name)
|
|
|
|
compiledExt := ext
|
|
|
|
switch ext {
|
|
|
|
case ".js":
|
2021-04-02 08:28:04 +00:00
|
|
|
content, err = minifier.Bytes("application/javascript", content)
|
2020-09-19 10:27:07 +00:00
|
|
|
if err != nil {
|
2021-02-08 17:51:07 +00:00
|
|
|
return "", err
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
|
|
|
case ".css":
|
2021-04-02 08:28:04 +00:00
|
|
|
content, err = minifier.Bytes("text/css", content)
|
2020-09-19 10:27:07 +00:00
|
|
|
if err != nil {
|
2021-02-08 17:51:07 +00:00
|
|
|
return "", err
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
|
|
|
default:
|
2021-04-02 08:28:04 +00:00
|
|
|
// Do nothing
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
2021-04-02 08:28:04 +00:00
|
|
|
// Hashes
|
|
|
|
sha1Hash := sha1.New()
|
|
|
|
sha512Hash := sha512.New()
|
|
|
|
if _, err := io.MultiWriter(sha1Hash, sha512Hash).Write(content); err != nil {
|
2021-02-08 17:51:07 +00:00
|
|
|
return "", err
|
|
|
|
}
|
2021-04-02 08:28:04 +00:00
|
|
|
// File name
|
|
|
|
compiledFileName := fmt.Sprintf("%x", sha1Hash.Sum(nil)) + compiledExt
|
|
|
|
// SRI
|
|
|
|
sriHash := fmt.Sprintf("sha512-%s", base64.StdEncoding.EncodeToString(sha512Hash.Sum(nil)))
|
|
|
|
// Create struct
|
2021-01-10 16:19:08 +00:00
|
|
|
assetFiles[compiledFileName] = &assetFile{
|
|
|
|
contentType: mime.TypeByExtension(compiledExt),
|
2021-04-02 08:28:04 +00:00
|
|
|
sri: sriHash,
|
|
|
|
body: content,
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
2021-02-08 17:51:07 +00:00
|
|
|
return compiledFileName, err
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Function for templates
|
2021-01-10 16:19:08 +00:00
|
|
|
func assetFileName(fileName string) string {
|
|
|
|
return "/" + assetFileNames[fileName]
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 08:28:04 +00:00
|
|
|
func assetSRI(fileName string) string {
|
|
|
|
return assetFiles[assetFileNames[fileName]].sri
|
|
|
|
}
|
|
|
|
|
2020-09-19 10:27:07 +00:00
|
|
|
func allAssetPaths() []string {
|
|
|
|
var paths []string
|
2021-01-10 16:19:08 +00:00
|
|
|
for _, name := range assetFileNames {
|
2020-09-19 10:27:07 +00:00
|
|
|
paths = append(paths, "/"+name)
|
|
|
|
}
|
|
|
|
return paths
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets only called by registered paths
|
2020-10-15 17:50:34 +00:00
|
|
|
func serveAsset(w http.ResponseWriter, r *http.Request) {
|
2021-02-08 17:51:07 +00:00
|
|
|
af, ok := assetFiles[strings.TrimPrefix(r.URL.Path, "/")]
|
2021-01-10 16:19:08 +00:00
|
|
|
if !ok {
|
|
|
|
serve404(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Header().Set("Cache-Control", "public,max-age=31536000,immutable")
|
2021-02-16 15:26:21 +00:00
|
|
|
w.Header().Set(contentType, af.contentType+charsetUtf8Suffix)
|
2021-02-08 17:51:07 +00:00
|
|
|
_, _ = w.Write(af.body)
|
2020-09-19 10:27:07 +00:00
|
|
|
}
|