Reputation: 1620
I'm trying to serve a single binary with embedd to include the SvelteKit website. I use Chi as my router. But I cannot get it to work. I'm getting one of these options below. As I understand the embedd all:
option makes sure that the files prefixed with _
are included. I also have tried variations in the StripPrefix
method in main V1: /uibuild/
or uibuild/
etc...
Can someone shine some light on it?
Svelte config:
import preprocess from "svelte-preprocess";
import adapter from "@sveltejs/adapter-static";
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
pages: "./../server/uibuild",
assets: "./../server/uibuild",
fallback: "index.html",
}),
},
preprocess: [
preprocess({
postcss: true,
}),
],
};
export default config;
Main.go V1:
This gives error 3.
package main
import (
"embed"
"log"
"net/http"
chi "github.com/go-chi/chi/v5"
)
//go:embed all:uibuild
var svelteStatic embed.FS
func main() {
r := chi.NewRouter()
r.Handle("/", http.StripPrefix("/uibuild", http.FileServer(http.FS(svelteStatic))))
log.Fatal(http.ListenAndServe(":8082", r))
}
Main.go V2:
This will give error 2.
static, err := fs.Sub(svelteStatic, "uibuild")
if err != nil {
panic(err)
}
r := chi.NewRouter()
r.Handle("/", http.FileServer(http.FS(static)))
log.Fatal(http.ListenAndServe(":8082", r))
File structure:
.
├── go.mod
├── go.sum
├── main.go
└── uibuild
├── _app
│ ├── immutable
│ │ ├── assets
│ │ │ ├── 0.d7cb9c3b.css
│ │ │ └── _layout.d7cb9c3b.css
│ │ ├── chunks
│ │ │ ├── index.6dba6488.js
│ │ │ └── singletons.b716dd01.js
│ │ ├── entry
│ │ │ ├── app.c5e2a2d5.js
│ │ │ └── start.58733315.js
│ │ └── nodes
│ │ ├── 0.ba05e72f.js
│ │ ├── 1.f4999e32.js
│ │ └── 2.ad52e74a.js
│ └── version.json
├── favicon.png
└── index.html
Upvotes: 3
Views: 951
Reputation: 18370
Frustratingly your "Main.go V2" works with the addition of a single character. You are using:
r.Handle("/", http.FileServer(http.FS(static)))
From the docs:
func (mx *Mux) Handle(pattern string, handler http.Handler)
Each routing method accepts a URL pattern and chain of handlers. The URL pattern supports named params (ie. /users/{userID}) and wildcards (ie. /admin/). URL parameters can be fetched at runtime by calling chi.URLParam(r, "userID") for named parameters and chi.URLParam(r, "") for a wildcard parameter.
So you are passing in "/" as the "pattern"; this will match /
but nothing else; to fix use:
r.Handle("/*", http.FileServer(http.FS(static)))
// or
r.Mount("/", http.FileServer(http.FS(static)))
I tested this with one of my svelte apps and it ran fine. One refinement you might want to consider is redirecting any requests for files that don't exist to /
(otherwise if the user bookmarks a page with a path it will fail to load). See this answer for info.
Further to the above - to demonstrate what I'm saying in the comments add <a href="/about">About</a>
to the end of ui/src/routes/+page.svelte
and rebuild (both svelte, and then go apps). You will then be able to navigate to the about
page (first loading the main page then click on "About"). This is handled by the client side router (so your you will probably not see any requests to the go server). See the linked answer for info on how to get this working when directly accessing a page (e.g. /about
).
Here is a quick (and bit hacky) example that will serve the needed bits from the embedded file system and return the main index.html
for all other requests (so the svelte router can display the needed page).
package main
import (
"embed"
"fmt"
"io/fs"
"log"
"net/http"
"github.com/go-chi/chi/v5"
)
//go:embed all:uibuild
var svelteStatic embed.FS
func main() {
s, err := fs.Sub(svelteStatic, "uibuild")
if err != nil {
panic(err)
}
staticServer := http.FileServer(http.FS(s))
r := chi.NewRouter()
r.Handle("/", staticServer) // Not really needed (as the default will pick this up)
r.Handle("/_app/*", staticServer) // Need to serve any app components from the embedded files
r.Handle("/favicon.png", staticServer) // Also serve favicon :-)
r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { // Everything else returns the index
r.URL.Path = "/" // Replace the request path
staticServer.ServeHTTP(w, r)
})
fmt.Println("Running on port: 8082")
log.Fatal(http.ListenAndServe(":8082", r))
}
Upvotes: 2