From 121101de9d49e699d5fc6999d61a4334974ffe2a Mon Sep 17 00:00:00 2001 From: Nazar Kanaev Date: Fri, 26 Feb 2021 12:33:35 +0000 Subject: [PATCH] switch assets to embed --- assets/assets.go | 45 +++++++++++++++++++ assets/assetsfs.go | 15 +++++++ doc/build.md | 2 +- go.mod | 2 +- makefile | 13 +++--- scripts/bundle_assets.go | 97 ---------------------------------------- server/handlers.go | 81 +++++---------------------------- server/router.go | 2 +- 8 files changed, 79 insertions(+), 178 deletions(-) create mode 100644 assets/assets.go create mode 100644 assets/assetsfs.go delete mode 100644 scripts/bundle_assets.go diff --git a/assets/assets.go b/assets/assets.go new file mode 100644 index 0000000..e9438af --- /dev/null +++ b/assets/assets.go @@ -0,0 +1,45 @@ +package assets + +import ( + "embed" + "html/template" + "io" + "io/ioutil" + "io/fs" + "os" +) + +type assetsfs struct { + embedded *embed.FS + templates map[string]*template.Template +} + +var FS assetsfs + +func (afs assetsfs) Open(name string) (fs.File, error) { + if afs.embedded != nil { + return afs.embedded.Open(name) + } + return os.DirFS("assets").Open(name) +} + +func Render(path string, writer io.Writer, data interface{}) { + var tmpl *template.Template + tmpl, found := FS.templates[path] + if !found { + tmpl = template.Must(template.New(path).Delims("{%", "%}").Funcs(template.FuncMap{ + "inline": func(svg string) template.HTML { + svgfile, _ := FS.Open("graphicarts/" + svg) + content, _ := ioutil.ReadAll(svgfile) + svgfile.Close() + return template.HTML(content) + }, + }).ParseFS(FS, path)) + FS.templates[path] = tmpl + } + tmpl.Execute(writer, data) +} + +func init() { + FS.templates = make(map[string]*template.Template) +} diff --git a/assets/assetsfs.go b/assets/assetsfs.go new file mode 100644 index 0000000..1cb605d --- /dev/null +++ b/assets/assetsfs.go @@ -0,0 +1,15 @@ +// +build release + +package assets + +import "embed" + +//go:embed *.html +//go:embed graphicarts +//go:embed javascripts +//go:embed stylesheets +var embedded embed.FS + +func init() { + FS.embedded = &embedded +} diff --git a/doc/build.md b/doc/build.md index 3bc0a35..cf1634b 100644 --- a/doc/build.md +++ b/doc/build.md @@ -1,4 +1,4 @@ -Install `Go >= 1.14` and `gcc`. Get the source code: +Install `Go >= 1.16` and `gcc`. Get the source code: git clone https://github.com/nkanaev/yarr.git git clone https://github.com/nkanaev/gofeed.git yarr/gofeed diff --git a/go.mod b/go.mod index 2641000..f3a4acf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/nkanaev/yarr -go 1.14 +go 1.16 require ( github.com/PuerkitoBio/goquery v1.5.1 diff --git a/makefile b/makefile index d683b56..1a918b0 100644 --- a/makefile +++ b/makefile @@ -1,24 +1,21 @@ VERSION=1.3 GITHASH=$(shell git rev-parse --short=8 HEAD) -ASSETS = assets/javascripts/* assets/stylesheets/* assets/graphicarts/* assets/*.html CGO_ENABLED=1 GO_LDFLAGS = -s -w GO_LDFLAGS := $(GO_LDFLAGS) -X 'main.Version=$(VERSION)' -X 'main.GitHash=$(GITHASH)' -default: bundle +default: build_default server/assets.go: $(ASSETS) go run scripts/bundle_assets.go >/dev/null -bundle: server/assets.go - -build_default: bundle +build_default: mkdir -p _output go build -tags "sqlite_foreign_keys release" -ldflags="$(GO_LDFLAGS)" -o _output/yarr main.go -build_macos: bundle +build_macos: set GOOS=darwin set GOARCH=amd64 mkdir -p _output/macos @@ -26,13 +23,13 @@ build_macos: bundle cp artwork/icon.png _output/macos/icon.png go run scripts/package_macos.go -outdir _output/macos -version "$(VERSION)" -build_linux: bundle +build_linux: set GOOS=linux set GOARCH=386 mkdir -p _output/linux go build -tags "sqlite_foreign_keys release linux" -ldflags="$(GO_LDFLAGS)" -o _output/linux/yarr main.go -build_windows: bundle +build_windows: set GOOS=windows set GOARCH=386 mkdir -p _output/windows diff --git a/scripts/bundle_assets.go b/scripts/bundle_assets.go deleted file mode 100644 index e3ae748..0000000 --- a/scripts/bundle_assets.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "bytes" - "compress/gzip" - "crypto/sha256" - "encoding/base64" - "fmt" - "io/ioutil" - "path/filepath" - "strings" - "text/template" - htemplate "html/template" -) - -var code_template = `// +build release - -// autogenerated. do not edit! - -package server - -var assets_bundle = map[string]asset{ - {{- range .}} - "{{.Name}}": {etag: "{{.Etag}}", body: "{{.Body}}"}, - {{- end }} -} - -func init() { - assets = assets_bundle -} -` - -type asset struct { - Name, Etag, Body string -} - -func shasum(b []byte) string { - h := sha256.New() - h.Write(b) - return fmt.Sprintf("%x", h.Sum(nil))[:16] -} - -func encode(b []byte) string { - var buf bytes.Buffer - zw := gzip.NewWriter(&buf) - zw.Write(b) - zw.Close() - return base64.StdEncoding.EncodeToString(buf.Bytes()) -} - -func main() { - assets := make([]asset, 0) - filepatterns := []string{ - "assets/login.html", - "assets/graphicarts/*.svg", - "assets/graphicarts/*.png", - "assets/javascripts/*.js", - "assets/stylesheets/*.css", - "assets/stylesheets/*.map", - } - fmt.Printf("%8s %8s %s\n", "original", "encoded", "filename") - for _, pattern := range filepatterns { - filenames, _ := filepath.Glob(pattern) - for _, filename := range filenames { - content, _ := ioutil.ReadFile(filename) - assets = append(assets, asset{ - Name: strings.TrimPrefix(strings.ReplaceAll(filename, "\\", "/"), "assets/"), - Etag: shasum(content), - Body: encode(content), - }) - fmt.Printf( - "%8d %8d %s\n", - len(content), - len(assets[len(assets)-1].Body), - filename, - ) - } - } - var indexbuf bytes.Buffer - htemplate.Must(htemplate.New("index.html").Delims("{%", "%}").Funcs(htemplate.FuncMap{ - "inline": func(svg string) htemplate.HTML { - content, _ := ioutil.ReadFile("assets/graphicarts/" + svg) - return htemplate.HTML(content) - }, - }).ParseFiles("assets/index.html")).Execute(&indexbuf, nil) - indexcontent := indexbuf.Bytes() - assets = append(assets, asset{ - Name: "index.html", - Etag: shasum(indexcontent), - Body: encode(indexcontent), - }) - - var buf bytes.Buffer - template := template.Must(template.New("code").Parse(code_template)) - template.Execute(&buf, assets) - ioutil.WriteFile("server/assets.go", buf.Bytes(), 0644) -} diff --git a/server/handlers.go b/server/handlers.go index c3fc927..94dc6c6 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -1,26 +1,22 @@ package server import ( - "bytes" - "compress/gzip" - "encoding/base64" "encoding/json" "fmt" "github.com/nkanaev/yarr/storage" + "github.com/nkanaev/yarr/assets" "html" - "html/template" - "io" "io/ioutil" "math" - "mime" "net/http" - "os" - "path/filepath" "reflect" "strconv" "strings" ) +// TODO: gzip? +var StaticHandler = http.StripPrefix("/static/", http.FileServer(http.FS(assets.FS))).ServeHTTP + var routes []Route = []Route{ p("/", IndexHandler).ManualAuth(), p("/static/*path", StaticHandler).ManualAuth(), @@ -43,36 +39,6 @@ var routes []Route = []Route{ p("/logout", LogoutHandler), } -type asset struct { - etag string - body string // base64(gzip(content)) - gzipped *[]byte - decoded *string -} - -func (a *asset) gzip() *[]byte { - if a.gzipped == nil { - gzipped, _ := base64.StdEncoding.DecodeString(a.body) - a.gzipped = &gzipped - } - return a.gzipped -} - -func (a *asset) text() *string { - if a.decoded == nil { - gzipped, _ := base64.StdEncoding.DecodeString(a.body) - reader, _ := gzip.NewReader(bytes.NewBuffer(gzipped)) - decoded, _ := ioutil.ReadAll(reader) - reader.Close() - - decoded_string := string(decoded) - a.decoded = &decoded_string - } - return a.decoded -} - -var assets map[string]asset - type FolderCreateForm struct { Title string `json:"title"` } @@ -104,43 +70,17 @@ func IndexHandler(rw http.ResponseWriter, req *http.Request) { } } - if assets != nil { - asset := assets["login.html"] - rw.Header().Set("Content-Type", "text/html") - rw.Header().Set("Content-Encoding", "gzip") - rw.Write(*asset.gzip()) - return - } else { - f, err := os.Open("assets/login.html") - if err != nil { - handler(req).log.Print(err) - return - } - io.Copy(rw, f) - return - } - } - - if assets != nil { - asset := assets["index.html"] - rw.Header().Set("Content-Type", "text/html") - rw.Header().Set("Content-Encoding", "gzip") - rw.Write(*asset.gzip()) - } else { - t := template.Must(template.New("index.html").Delims("{%", "%}").Funcs(template.FuncMap{ - "inline": func(svg string) template.HTML { - content, _ := ioutil.ReadFile("assets/graphicarts/" + svg) - return template.HTML(content) - }, - }).ParseFiles("assets/index.html")) - rw.Header().Set("Content-Type", "text/html") - t.Execute(rw, nil) + assets.Render("login.html", rw, nil) + return } + rw.Header().Set("Content-Type", "text/html") + assets.Render("index.html", rw, nil) } +/* func StaticHandler(rw http.ResponseWriter, req *http.Request) { - path := Vars(req)["path"] + http.StripPrefix("/static/", http.FileServer(http.FS(assets.FS))).ServeHTTP(rw, req) ctype := mime.TypeByExtension(filepath.Ext(path)) if assets != nil { @@ -164,6 +104,7 @@ func StaticHandler(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("Content-Type", ctype) io.Copy(rw, f) } +*/ func StatusHandler(rw http.ResponseWriter, req *http.Request) { writeJSON(rw, map[string]interface{}{ diff --git a/server/router.go b/server/router.go index ea3d6aa..8856851 100644 --- a/server/router.go +++ b/server/router.go @@ -19,7 +19,7 @@ func (r Route) ManualAuth() Route { return r } -func p(path string, handler func(http.ResponseWriter, *http.Request)) Route { +func p(path string, handler http.HandlerFunc) Route { var urlRegexp string urlRegexp = regexp.MustCompile(`[\*\:]\w+`).ReplaceAllStringFunc(path, func(m string) string { if m[0:1] == `*` {