mirror of
https://github.com/nkanaev/yarr.git
synced 2025-05-24 00:33:14 +00:00
switch assets to embed
This commit is contained in:
parent
a3146926b1
commit
121101de9d
45
assets/assets.go
Normal file
45
assets/assets.go
Normal file
@ -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)
|
||||||
|
}
|
15
assets/assetsfs.go
Normal file
15
assets/assetsfs.go
Normal file
@ -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
|
||||||
|
}
|
@ -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/yarr.git
|
||||||
git clone https://github.com/nkanaev/gofeed.git yarr/gofeed
|
git clone https://github.com/nkanaev/gofeed.git yarr/gofeed
|
||||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
|||||||
module github.com/nkanaev/yarr
|
module github.com/nkanaev/yarr
|
||||||
|
|
||||||
go 1.14
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.5.1
|
github.com/PuerkitoBio/goquery v1.5.1
|
||||||
|
13
makefile
13
makefile
@ -1,24 +1,21 @@
|
|||||||
VERSION=1.3
|
VERSION=1.3
|
||||||
GITHASH=$(shell git rev-parse --short=8 HEAD)
|
GITHASH=$(shell git rev-parse --short=8 HEAD)
|
||||||
|
|
||||||
ASSETS = assets/javascripts/* assets/stylesheets/* assets/graphicarts/* assets/*.html
|
|
||||||
CGO_ENABLED=1
|
CGO_ENABLED=1
|
||||||
|
|
||||||
GO_LDFLAGS = -s -w
|
GO_LDFLAGS = -s -w
|
||||||
GO_LDFLAGS := $(GO_LDFLAGS) -X 'main.Version=$(VERSION)' -X 'main.GitHash=$(GITHASH)'
|
GO_LDFLAGS := $(GO_LDFLAGS) -X 'main.Version=$(VERSION)' -X 'main.GitHash=$(GITHASH)'
|
||||||
|
|
||||||
default: bundle
|
default: build_default
|
||||||
|
|
||||||
server/assets.go: $(ASSETS)
|
server/assets.go: $(ASSETS)
|
||||||
go run scripts/bundle_assets.go >/dev/null
|
go run scripts/bundle_assets.go >/dev/null
|
||||||
|
|
||||||
bundle: server/assets.go
|
build_default:
|
||||||
|
|
||||||
build_default: bundle
|
|
||||||
mkdir -p _output
|
mkdir -p _output
|
||||||
go build -tags "sqlite_foreign_keys release" -ldflags="$(GO_LDFLAGS)" -o _output/yarr main.go
|
go build -tags "sqlite_foreign_keys release" -ldflags="$(GO_LDFLAGS)" -o _output/yarr main.go
|
||||||
|
|
||||||
build_macos: bundle
|
build_macos:
|
||||||
set GOOS=darwin
|
set GOOS=darwin
|
||||||
set GOARCH=amd64
|
set GOARCH=amd64
|
||||||
mkdir -p _output/macos
|
mkdir -p _output/macos
|
||||||
@ -26,13 +23,13 @@ build_macos: bundle
|
|||||||
cp artwork/icon.png _output/macos/icon.png
|
cp artwork/icon.png _output/macos/icon.png
|
||||||
go run scripts/package_macos.go -outdir _output/macos -version "$(VERSION)"
|
go run scripts/package_macos.go -outdir _output/macos -version "$(VERSION)"
|
||||||
|
|
||||||
build_linux: bundle
|
build_linux:
|
||||||
set GOOS=linux
|
set GOOS=linux
|
||||||
set GOARCH=386
|
set GOARCH=386
|
||||||
mkdir -p _output/linux
|
mkdir -p _output/linux
|
||||||
go build -tags "sqlite_foreign_keys release linux" -ldflags="$(GO_LDFLAGS)" -o _output/linux/yarr main.go
|
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 GOOS=windows
|
||||||
set GOARCH=386
|
set GOARCH=386
|
||||||
mkdir -p _output/windows
|
mkdir -p _output/windows
|
||||||
|
@ -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)
|
|
||||||
}
|
|
@ -1,26 +1,22 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/nkanaev/yarr/storage"
|
"github.com/nkanaev/yarr/storage"
|
||||||
|
"github.com/nkanaev/yarr/assets"
|
||||||
"html"
|
"html"
|
||||||
"html/template"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: gzip?
|
||||||
|
var StaticHandler = http.StripPrefix("/static/", http.FileServer(http.FS(assets.FS))).ServeHTTP
|
||||||
|
|
||||||
var routes []Route = []Route{
|
var routes []Route = []Route{
|
||||||
p("/", IndexHandler).ManualAuth(),
|
p("/", IndexHandler).ManualAuth(),
|
||||||
p("/static/*path", StaticHandler).ManualAuth(),
|
p("/static/*path", StaticHandler).ManualAuth(),
|
||||||
@ -43,36 +39,6 @@ var routes []Route = []Route{
|
|||||||
p("/logout", LogoutHandler),
|
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 {
|
type FolderCreateForm struct {
|
||||||
Title string `json:"title"`
|
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-Type", "text/html")
|
||||||
rw.Header().Set("Content-Encoding", "gzip")
|
assets.Render("login.html", rw, nil)
|
||||||
rw.Write(*asset.gzip())
|
return
|
||||||
} 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)
|
|
||||||
}
|
}
|
||||||
|
rw.Header().Set("Content-Type", "text/html")
|
||||||
|
assets.Render("index.html", rw, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func StaticHandler(rw http.ResponseWriter, req *http.Request) {
|
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))
|
ctype := mime.TypeByExtension(filepath.Ext(path))
|
||||||
|
|
||||||
if assets != nil {
|
if assets != nil {
|
||||||
@ -164,6 +104,7 @@ func StaticHandler(rw http.ResponseWriter, req *http.Request) {
|
|||||||
rw.Header().Set("Content-Type", ctype)
|
rw.Header().Set("Content-Type", ctype)
|
||||||
io.Copy(rw, f)
|
io.Copy(rw, f)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func StatusHandler(rw http.ResponseWriter, req *http.Request) {
|
func StatusHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
writeJSON(rw, map[string]interface{}{
|
writeJSON(rw, map[string]interface{}{
|
||||||
|
@ -19,7 +19,7 @@ func (r Route) ManualAuth() Route {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func p(path string, handler func(http.ResponseWriter, *http.Request)) Route {
|
func p(path string, handler http.HandlerFunc) Route {
|
||||||
var urlRegexp string
|
var urlRegexp string
|
||||||
urlRegexp = regexp.MustCompile(`[\*\:]\w+`).ReplaceAllStringFunc(path, func(m string) string {
|
urlRegexp = regexp.MustCompile(`[\*\:]\w+`).ReplaceAllStringFunc(path, func(m string) string {
|
||||||
if m[0:1] == `*` {
|
if m[0:1] == `*` {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user