Files
sproutclaw-web/backend/internal/static/static.go
2026-06-13 20:16:09 +08:00

69 lines
1.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package static
import (
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
// Register attaches the static file handler to the Gin router.
// distDir is the path to frontend/dist/.
func Register(r *gin.Engine, distDir string) {
r.NoRoute(func(c *gin.Context) {
path := c.Request.URL.Path
// Skip API routes they're handled elsewhere
if strings.HasPrefix(path, "/api/") {
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
return
}
// Try to serve a real file
fsPath := filepath.Join(distDir, filepath.FromSlash(path))
if info, err := os.Stat(fsPath); err == nil && !info.IsDir() {
serveFile(c, fsPath)
return
}
// SPA fallback: serve index.html
indexPath := filepath.Join(distDir, "index.html")
if _, err := os.Stat(indexPath); err != nil {
c.String(http.StatusNotFound, "frontend not built run npm run build:web in frontend/")
return
}
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
c.File(indexPath)
})
}
func serveFile(c *gin.Context, fsPath string) {
name := filepath.Base(fsPath)
ext := strings.ToLower(filepath.Ext(name))
if name == "index.html" || strings.HasSuffix(name, ".webmanifest") {
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
} else if isHashedAsset(name, ext) {
c.Header("Cache-Control", "public, max-age=31536000, immutable")
} else if ext == ".woff" || ext == ".woff2" || ext == ".ttf" || ext == ".otf" {
c.Header("Cache-Control", "public, max-age=31536000, immutable")
} else {
c.Header("Cache-Control", "public, max-age=3600")
}
// Gin's File() handles Content-Type and ETag automatically
c.File(fsPath)
}
// isHashedAsset returns true if the filename looks like a Vite hashed asset
// (e.g. index-BxYt3k2a.js, chunk-abc123.css).
func isHashedAsset(name, ext string) bool {
switch ext {
case ".js", ".css", ".mjs":
return strings.Contains(name, "-") || strings.Contains(name, ".")
}
return false
}