From 36c6d76e539748e619b376cc07f77356db0f1388 Mon Sep 17 00:00:00 2001 From: Timo Riegebauer Date: Wed, 29 Apr 2026 22:15:12 +0200 Subject: [PATCH] feat: add Static, StaticFS and SPA file serving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- group.go | 15 +++++++++++++++ kite.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/group.go b/group.go index 0eec3c4..f8885f3 100644 --- a/group.go +++ b/group.go @@ -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, diff --git a/kite.go b/kite.go index 4cd0ad4..92cab87 100644 --- a/kite.go +++ b/kite.go @@ -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,