feat(serverembed): add initial HTML and SVG assets
* Create index.html for the web application entry point * Add vite.svg as a favicon * Update frontend.go to embed all files in the dist directory * Modify vite.config.ts to set output directory for builds
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"git.warky.dev/wdevs/whatshooked/pkg/config"
|
||||
"git.warky.dev/wdevs/whatshooked/pkg/handlers"
|
||||
"git.warky.dev/wdevs/whatshooked/pkg/models"
|
||||
"git.warky.dev/wdevs/whatshooked/pkg/serverembed"
|
||||
"git.warky.dev/wdevs/whatshooked/pkg/storage"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
|
||||
@@ -80,9 +82,12 @@ func NewServer(cfg *config.Config, db *bun.DB, wh WhatsHookedInterface) (*Server
|
||||
// Add custom routes (login, logout, etc.) on main router
|
||||
SetupCustomRoutes(router, secProvider, db)
|
||||
|
||||
// Add static file serving for React app at /ui/ route
|
||||
// Serve React app from configurable filesystem path
|
||||
spa := spaHandler{staticPath: cfg.Server.UIPath, indexPath: "index.html"}
|
||||
// Serve React SPA from the embedded filesystem at /ui/
|
||||
distFS, err := fs.Sub(serverembed.RootEmbedFS, "dist")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sub embedded dist FS: %w", err)
|
||||
}
|
||||
spa := embeddedSPAHandler{fs: distFS, indexPath: "index.html"}
|
||||
router.PathPrefix("/ui/").Handler(http.StripPrefix("/ui", spa))
|
||||
router.PathPrefix("/ui").Handler(http.StripPrefix("/ui", spa))
|
||||
|
||||
@@ -603,42 +608,46 @@ func extractToken(r *http.Request) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// spaHandler implements the http.Handler interface for serving a SPA
|
||||
type spaHandler struct {
|
||||
staticPath string
|
||||
indexPath string
|
||||
// embeddedSPAHandler serves a React SPA from an embedded fs.FS.
|
||||
// Static assets are served directly; all other paths fall back to index.html
|
||||
// to support client-side routing.
|
||||
type embeddedSPAHandler struct {
|
||||
fs fs.FS
|
||||
indexPath string
|
||||
}
|
||||
|
||||
// ServeHTTP inspects the URL path to locate a file within the static dir
|
||||
// If a file is found, it is served. If not, the index.html file is served for client-side routing
|
||||
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Get the path
|
||||
func (h embeddedSPAHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.URL.Path
|
||||
// Strip leading slash so fs.FS Open calls work correctly.
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
// Check whether a file exists at the given path
|
||||
info, err := http.Dir(h.staticPath).Open(path)
|
||||
if path == "" {
|
||||
path = h.indexPath
|
||||
}
|
||||
|
||||
// Try to open the requested path in the embedded FS.
|
||||
f, err := h.fs.Open(path)
|
||||
if err != nil {
|
||||
// File does not exist, serve index.html for client-side routing
|
||||
http.ServeFile(w, r, h.staticPath+"/"+h.indexPath)
|
||||
// Not found — serve index.html for client-side routing.
|
||||
r2 := r.Clone(r.Context())
|
||||
r2.URL.Path = "/" + h.indexPath
|
||||
http.FileServer(http.FS(h.fs)).ServeHTTP(w, r2)
|
||||
return
|
||||
}
|
||||
defer info.Close()
|
||||
defer f.Close()
|
||||
|
||||
// Check if path is a directory
|
||||
stat, err := info.Stat()
|
||||
if err != nil {
|
||||
http.ServeFile(w, r, h.staticPath+"/"+h.indexPath)
|
||||
stat, err := f.Stat()
|
||||
if err != nil || stat.IsDir() {
|
||||
r2 := r.Clone(r.Context())
|
||||
r2.URL.Path = "/" + h.indexPath
|
||||
http.FileServer(http.FS(h.fs)).ServeHTTP(w, r2)
|
||||
return
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
// Serve index.html for directories
|
||||
http.ServeFile(w, r, h.staticPath+"/"+h.indexPath)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, serve the file
|
||||
http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
|
||||
// Serve the real file.
|
||||
http.FileServer(http.FS(h.fs)).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// corsMiddleware adds CORS headers to allow frontend requests
|
||||
|
||||
Reference in New Issue
Block a user