Files
yovision/Sense/server/cmd/api/web.go
T

71 lines
1.7 KiB
Go

package api
import (
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
// configureWebUI adds an optional SPA fallback to the existing GoAdmin Gin
// engine. API and framework routes keep their normal handlers; the fallback is
// enabled only for Windows delivery packages that set SENSE_WEB_ROOT.
func configureWebUI(r *gin.Engine) {
root := strings.TrimSpace(os.Getenv("SENSE_WEB_ROOT"))
if root == "" {
return
}
absRoot, err := filepath.Abs(root)
if err != nil {
return
}
index := filepath.Join(absRoot, "index.html")
if info, statErr := os.Stat(index); statErr != nil || info.IsDir() {
return
}
r.NoRoute(func(c *gin.Context) {
if c.Request.Method != http.MethodGet && c.Request.Method != http.MethodHead {
c.Status(http.StatusNotFound)
return
}
if isBackendPath(c.Request.URL.Path) {
c.Status(http.StatusNotFound)
return
}
requested := filepath.Clean(filepath.FromSlash(strings.TrimPrefix(c.Request.URL.Path, "/")))
if requested == "." {
requested = ""
}
candidate := filepath.Join(absRoot, requested)
if withinRoot(absRoot, candidate) {
if info, statErr := os.Stat(candidate); statErr == nil && !info.IsDir() {
c.File(candidate)
return
}
}
if filepath.Ext(requested) != "" {
c.Status(http.StatusNotFound)
return
}
c.File(index)
})
}
func withinRoot(root, candidate string) bool {
rel, err := filepath.Rel(root, candidate)
return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator))
}
func isBackendPath(path string) bool {
for _, prefix := range []string{"/api/", "/swagger/", "/static/", "/form-generator/"} {
if strings.HasPrefix(path, prefix) {
return true
}
}
return false
}