From 55cf8bbdc7ad2a3c40ed68a92cacfc7d817cc0a4 Mon Sep 17 00:00:00 2001 From: Ravi Suhag Date: Tue, 13 Sep 2022 00:25:21 -0500 Subject: [PATCH] feat: add spa package (#49) * feat: add spa package * chore:fix typo * fix: remove spa type * fix: remove panic * fix: merge handlers --- go.mod | 1 + go.sum | 1 + spa/router.go | 32 ++++++++++++++++++++++++++++++++ spa/spa.go | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 spa/router.go create mode 100644 spa/spa.go diff --git a/go.mod b/go.mod index a69bb08..8f86f43 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.5 github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/MakeNowJust/heredoc v1.0.0 + github.com/NYTimes/gziphandler v1.1.1 github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/briandowns/spinner v1.18.0 github.com/cenkalti/backoff v2.2.1+incompatible // indirect diff --git a/go.sum b/go.sum index 0efa314..7a0be0f 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,7 @@ github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfy github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= diff --git a/spa/router.go b/spa/router.go new file mode 100644 index 0000000..e0f4c14 --- /dev/null +++ b/spa/router.go @@ -0,0 +1,32 @@ +package spa + +import ( + "errors" + "io/fs" + "net/http" +) + +// router is the http filesystem which only serves files +// and prevent the directory traversal. +type router struct { + index string + fs http.FileSystem +} + +// Open inspects the URL path to locate a file within the static dir. +// If a file is found, it will be served. If not, the file located at +// the index path on the SPA handler will be served. +func (r *router) Open(name string) (http.File, error) { + file, err := r.fs.Open(name) + + if err == nil { + return file, nil + } + // Serve index if file does not exist. + if errors.Is(err, fs.ErrNotExist) { + file, err := r.fs.Open(r.index) + return file, err + } + + return nil, err +} diff --git a/spa/spa.go b/spa/spa.go new file mode 100644 index 0000000..5bda3fd --- /dev/null +++ b/spa/spa.go @@ -0,0 +1,40 @@ +package spa + +import ( + "embed" + "errors" + "fmt" + "io/fs" + "net/http" + + "github.com/NYTimes/gziphandler" +) + +// Handler return a file server http handler for single page +// application. This handler can be mounted on your mux server. +// +// If gzip is set true, handler gzip the response body, for clients +// which support it. Usually it also can be left to proxies like Nginx, +// this method is useful when that's undesirable. +func Handler(build embed.FS, dir string, index string, gzip bool) (http.Handler, error) { + fsys, err := fs.Sub(build, dir) + if err != nil { + return nil, fmt.Errorf("couldn't create sub filesystem: %w", err) + } + + if _, err = fsys.Open(index); err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil, fmt.Errorf("ui is enabled but no index.html found: %w", err) + } else { + return nil, fmt.Errorf("ui assets error: %w", err) + } + } + router := &router{index: index, fs: http.FS(fsys)} + + hlr := http.FileServer(router) + + if !gzip { + return hlr, nil + } + return gziphandler.GzipHandler(hlr), nil +}