web.go raw
1 package app
2
3 import (
4 "embed"
5 "io/fs"
6 "net/http"
7 "strings"
8 )
9
10 //go:embed smesh/dist
11 var reactAppFS embed.FS
12
13 var webDistFS fs.FS
14
15 func getWebDistFS() fs.FS {
16 if webDistFS == nil {
17 var err error
18 webDistFS, err = fs.Sub(reactAppFS, "smesh/dist")
19 if err != nil {
20 panic("Failed to load embedded web app: " + err.Error())
21 }
22 }
23 return webDistFS
24 }
25
26 // GetReactAppFS returns a http.FileSystem from the embedded React app
27 func GetReactAppFS() http.FileSystem {
28 return http.FS(getWebDistFS())
29 }
30
31 // ServeEmbeddedWeb serves the embedded web application with SPA fallback routing.
32 // For paths that don't map to a real file, serve index.html so React router can handle them.
33 func ServeEmbeddedWeb(w http.ResponseWriter, r *http.Request) {
34 distFS := getWebDistFS()
35 fileServer := http.FileServer(http.FS(distFS))
36
37 path := r.URL.Path
38 if path == "/" {
39 fileServer.ServeHTTP(w, r)
40 return
41 }
42
43 // Check if the path maps to a real file in the embedded FS
44 cleanPath := strings.TrimPrefix(path, "/")
45 if f, err := distFS.Open(cleanPath); err == nil {
46 f.Close()
47 fileServer.ServeHTTP(w, r)
48 return
49 }
50
51 // SPA fallback: serve index.html for client-side routing
52 r.URL.Path = "/"
53 fileServer.ServeHTTP(w, r)
54 }
55
56 // GetEmbeddedWebFS returns the raw embedded filesystem for branding initialization.
57 // This is used by the init-branding command to extract default assets.
58 func GetEmbeddedWebFS() embed.FS {
59 return reactAppFS
60 }
61