feat: add Static, StaticFS and SPA file serving

- Add Static(prefix, root) to serve files from disk on Kite and Group
- Add StaticFS(prefix, fs) to serve from any http.FileSystem on Kite and Group
- Add SPA(root) and SPAFS(fs) for Single Page Application serving — falls back to index.html for unknown paths so client-side routing works correctly
This commit is contained in:
2026-04-29 22:15:12 +02:00
parent 22b4d9ace8
commit 36c6d76e53
2 changed files with 49 additions and 0 deletions

View File

@@ -8,6 +8,21 @@ type Group struct {
mws []Middleware
}
func (g *Group) Static(prefix, root string) {
g.StaticFS(prefix, http.Dir(root))
}
func (g *Group) StaticFS(prefix string, fs http.FileSystem) {
if prefix == "" || prefix[len(prefix)-1] != '/' {
prefix += "/"
}
fullPrefix := g.prefix + prefix
handler := http.StripPrefix(fullPrefix, http.FileServer(fs))
g.k.r.Handler(http.MethodGet, fullPrefix+"*filepath", handler)
g.k.r.Handler(http.MethodHead, fullPrefix+"*filepath", handler)
}
func (g *Group) Group(prefix string, mws ...Middleware) *Group {
return &Group{
k: g.k,

34
kite.go
View File

@@ -130,6 +130,40 @@ func (k *Kite) StartTLS(listenAddr, certFile, keyFile string) error {
return srv.Shutdown(ctx)
}
func (k *Kite) SPA(root string) {
k.SPAFS(http.Dir(root))
}
func (k *Kite) SPAFS(fs http.FileSystem) {
fileServer := http.FileServer(fs)
k.r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
f, err := fs.Open(r.URL.Path)
if err != nil {
r.URL.Path = "/"
fileServer.ServeHTTP(w, r)
return
}
f.Close()
fileServer.ServeHTTP(w, r)
})
}
func (k *Kite) Static(prefix, root string) {
k.StaticFS(prefix, http.Dir(root))
}
func (k *Kite) StaticFS(prefix string, fs http.FileSystem) {
if prefix == "" || prefix[len(prefix)-1] != '/' {
prefix += "/"
}
handler := http.StripPrefix(prefix, http.FileServer(fs))
k.r.Handler(http.MethodGet, prefix+"*filepath", handler)
k.r.Handler(http.MethodHead, prefix+"*filepath", handler)
}
func (k *Kite) Group(prefix string, mws ...Middleware) *Group {
return &Group{
k: k,