fix(web): PWA asset and route handling (#898)

* fix(web): PWA asset and fallback route handling

* fix(web): strip baseurl

* fix(web): add back manifest.json

* fix(web): add back manifest.json

* fix(web): log file

* fix(web): do not trim baseurl

* fix(web): try different start_url

* fix(web): pwa with subfolder
This commit is contained in:
ze0s 2023-05-02 19:06:53 +02:00 committed by GitHub
parent d085d894d4
commit 319bc2f200
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 30 deletions

View file

@ -3,6 +3,7 @@ node_modules/
.gitignore .gitignore
.github .github
.dev .dev
dev/
config.toml config.toml
.goreleaser.yml .goreleaser.yml
Dockerfile Dockerfile
@ -12,18 +13,18 @@ README.md
bin bin
config config
test test
web/* #web/*
!web/public #!web/public
!web/src #!web/src
!web/dist* #!web/dist*
!web/package.json #!web/package.json
!web/yarn.lock #!web/yarn.lock
!web/.yarnrc.yml #!web/.yarnrc.yml
!web/.yarn/releases #!web/.yarn/releases
!web/.eslintrc.js #!web/.eslintrc.js
!web/postcss.config.js #!web/postcss.config.js
!web/tailwind.config.js #!web/tailwind.config.js
!web/tsconfig.json #!web/tsconfig.json
!web/index.html #!web/index.html
!web/vite.config.ts #!web/vite.config.ts
!web/build.go #!web/build.go

View file

@ -108,7 +108,6 @@ func (s Server) Handler() http.Handler {
r.Use(c.Handler) r.Use(c.Handler)
encoder := encoder{} encoder := encoder{}
web.RegisterHandler(r)
r.Route("/api", func(r chi.Router) { r.Route("/api", func(r chi.Router) {
r.Route("/auth", newAuthHandler(encoder, s.log, s.config.Config, s.cookieStore, s.authService).Routes) r.Route("/auth", newAuthHandler(encoder, s.log, s.config.Config, s.cookieStore, s.authService).Routes)
@ -145,9 +144,8 @@ func (s Server) Handler() http.Handler {
}) })
}) })
// serve the parsed index.html // serve the web
r.Get("/", s.index) web.RegisterHandler(r, s.version, s.config.Config.BaseURL)
r.Get("/*", s.index)
return r return r
} }

View file

@ -14,6 +14,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
) )
@ -92,6 +93,7 @@ func StaticFS(r *chi.Mux, pathPrefix string, filesystem fs.FS) {
// fsFile is a helper function to serve a file from the provided file system. // fsFile is a helper function to serve a file from the provided file system.
func fsFile(w http.ResponseWriter, r *http.Request, file string, filesystem fs.FS) { func fsFile(w http.ResponseWriter, r *http.Request, file string, filesystem fs.FS) {
//fmt.Printf("file: %s\n", file)
f, err := filesystem.Open(file) f, err := filesystem.Open(file)
if err != nil { if err != nil {
http.Error(w, "File not found", http.StatusNotFound) http.Error(w, "File not found", http.StatusNotFound)
@ -115,16 +117,74 @@ func fsFile(w http.ResponseWriter, r *http.Request, file string, filesystem fs.F
http.ServeContent(w, r, file, stat.ModTime(), reader) http.ServeContent(w, r, file, stat.ModTime(), reader)
} }
func RegisterHandler(c *chi.Mux) { var validRoutes = []string{"/", "filters", "releases", "settings", "logs", "onboard", "login", "logout"}
func validRoute(route string) bool {
for _, valid := range validRoutes {
if strings.Contains(route, valid) {
return true
}
}
return false
}
// RegisterHandler register web routes and file serving
func RegisterHandler(c *chi.Mux, version, baseUrl string) {
// Serve static files without a prefix // Serve static files without a prefix
assets, _ := fs.Sub(DistDirFS, "assets") assets, _ := fs.Sub(DistDirFS, "assets")
static, _ := fs.Sub(DistDirFS, "static") static, _ := fs.Sub(DistDirFS, "static")
StaticFS(c, "/assets", assets) StaticFS(c, "/assets", assets)
StaticFS(c, "/static", static) StaticFS(c, "/static", static)
p := IndexParams{
Title: "Dashboard",
Version: version,
BaseUrl: baseUrl,
}
// serve on base route
c.Get("/", func(w http.ResponseWriter, r *http.Request) {
Index(w, p)
})
// handle all other routes
c.Get("/*", func(w http.ResponseWriter, r *http.Request) { c.Get("/*", func(w http.ResponseWriter, r *http.Request) {
// Serve index.html for unmatched routes file := strings.TrimPrefix(r.RequestURI, "/")
fsFile(w, r, "dist/index.html", Dist)
// if valid web route then serve html
if validRoute(file) || file == "index.html" {
Index(w, p)
return
}
if strings.Contains(file, "manifest.webmanifest") {
Manifest(w, p)
return
}
// if not valid web route then try and serve files
f, err := DistDirFS.Open(file)
if err != nil {
http.Error(w, "File not found", http.StatusNotFound)
return
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
http.Error(w, "File not found", http.StatusNotFound)
return
}
data, err := io.ReadAll(f)
if err != nil {
http.Error(w, "Failed to read the file", http.StatusInternalServerError)
return
}
reader := bytes.NewReader(data)
http.ServeContent(w, r, file, stat.ModTime(), reader)
}) })
} }
@ -135,3 +195,11 @@ func Index(w io.Writer, p IndexParams) error {
func parseIndex() *template.Template { func parseIndex() *template.Template {
return template.Must(template.New("index.html").ParseFS(Dist, "dist/index.html")) return template.Must(template.New("index.html").ParseFS(Dist, "dist/index.html"))
} }
func Manifest(w io.Writer, p IndexParams) error {
return parseManifest().Execute(w, p)
}
func parseManifest() *template.Template {
return template.Must(template.New("manifest.webmanifest").ParseFS(Dist, "dist/manifest.webmanifest"))
}

View file

@ -19,7 +19,7 @@
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-ipad-76x76.png"> <link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-ipad-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-iphone-retina-120x120.png"> <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-iphone-retina-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-ipad-retina-152x152.png"> <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-ipad-retina-152x152.png">
<link crossorigin="use-credentials" rel="manifest" href="/manifest.json" /> <!-- <link crossorigin="use-credentials" rel="manifest" href="/manifest.json" />-->
<title>autobrr</title> <title>autobrr</title>
<base href="{{.BaseUrl}}"> <base href="{{.BaseUrl}}">
<script> <script>

View file

@ -18,8 +18,8 @@
"sizes": "512x512" "sizes": "512x512"
} }
], ],
"start_url": ".", "start_url": "/",
"display": "standalone", "display": "standalone",
"theme_color": "#000000", "theme_color": "#000000",
"background_color": "#ffffff" "background_color": "#ffffff"
} }

BIN
web/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

@ -13,12 +13,16 @@ export default ({ mode }: { mode: any }) => {
base: "", base: "",
plugins: [react(), VitePWA({ plugins: [react(), VitePWA({
registerType: "autoUpdate", registerType: "autoUpdate",
injectRegister: "auto", injectRegister: "inline",
scope: "{{.BaseUrl}}",
// strategies: "injectManifest",
useCredentials: true,
includeAssets: [ includeAssets: [
"favicon.svg", "favicon.svg",
"favicon.ico", "favicon.ico",
"robots.txt", "robots.txt",
"apple-touch-icon.png", "logo.png",
"apple-touch-icon-*.png",
"manifest.webmanifest", "manifest.webmanifest",
"assets/**/*" "assets/**/*"
], ],
@ -29,6 +33,11 @@ export default ({ mode }: { mode: any }) => {
theme_color: "#141415", theme_color: "#141415",
background_color: "#141415", background_color: "#141415",
icons: [ icons: [
{
src: "logo.png",
sizes: "192x192",
type: "image/png"
},
{ {
src: "logo192.png", src: "logo192.png",
sizes: "192x192", sizes: "192x192",
@ -39,17 +48,21 @@ export default ({ mode }: { mode: any }) => {
sizes: "512x512", sizes: "512x512",
type: "image/png" type: "image/png"
}, },
{ {
src: "logo512.png", src: "logo512.png",
sizes: "512x512", sizes: "512x512",
type: "image/png", type: "image/png",
purpose: "any maskable" purpose: "any maskable"
},
{
src: "apple-touch-icon-iphone-retina-120x120.png",
sizes: "120x120",
type: "image/png"
} }
], ],
start_url: "/", start_url: "{{.BaseUrl}}",
scope: "{{.BaseUrl}}",
display: "standalone" display: "standalone"
}, },
workbox: { workbox: {
globPatterns: ["**/*.{js,css,html,svg}"], globPatterns: ["**/*.{js,css,html,svg}"],