Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 24 additions & 13 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,39 @@ func setupStatic(r *gin.Engine) {
assetsGroup := r.Group(base + "/assets")
assetsGroup.Use(middleware.StaticCache())
assetsGroup.StaticFS("/", http.FS(assertsFS))

// Pre-process index.html once at startup: inject base script and optional
// analytics tag so the NoRoute handler only needs to send cached bytes.
processedHTML := preprocessIndexHTML(base)

r.NoRoute(func(c *gin.Context) {
path := c.Request.URL.Path
if len(path) >= len(base)+5 && path[len(base):len(base)+5] == "/api/" {
c.JSON(http.StatusNotFound, gin.H{"error": "API endpoint not found"})
return
}

content, err := static.ReadFile("static/index.html")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read index.html"})
return
}
c.Data(http.StatusOK, "text/html; charset=utf-8", processedHTML)
})
}

htmlContent := string(content)
htmlContent = utils.InjectKiteBase(htmlContent, base)
if common.EnableAnalytics {
htmlContent = utils.InjectAnalytics(htmlContent)
}
// preprocessIndexHTML reads the embedded index.html, injects the kite base
// script and (optionally) the analytics tag, and returns the final bytes.
// Called once at startup — the result is immutable and shared across requests.
func preprocessIndexHTML(base string) []byte {
content, err := static.ReadFile("static/index.html")
if err != nil {
klog.Warningf("Failed to read embedded index.html: %v (UI may not be bundled)", err)
return []byte("<!doctype html><html><body><p>index.html not found</p></body></html>")
}

c.Header("Content-Type", "text/html; charset=utf-8")
c.String(http.StatusOK, htmlContent)
})
htmlContent := string(content)
htmlContent = utils.InjectKiteBase(htmlContent, base)
if common.EnableAnalytics {
htmlContent = utils.InjectAnalytics(htmlContent)
}

return []byte(htmlContent)
}

func setupAPIRouter(r *gin.RouterGroup, cm *cluster.ClusterManager) {
Expand Down
7 changes: 2 additions & 5 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package utils

import (
"fmt"
"regexp"
"strings"

"k8s.io/apimachinery/pkg/util/rand"
Expand All @@ -11,14 +10,12 @@ import (
func InjectAnalytics(htmlContent string) string {
analyticsScript := `<script defer src="https://cloud.umami.is/script.js" data-website-id="c3d8a914-abbc-4eed-9699-a9192c4bef9e" data-exclude-search="true" data-exclude-hash="true" data-do-not-track="true"></script>`

re := regexp.MustCompile(`</head>`)
return re.ReplaceAllString(htmlContent, " "+analyticsScript+"\n </head>")
return strings.Replace(htmlContent, "</head>", " "+analyticsScript+"\n </head>", 1)
}

func InjectKiteBase(htmlContent string, base string) string {
baseScript := fmt.Sprintf(`<script>window.__dynamic_base__='%s';</script>`, base)
re := regexp.MustCompile(`<head>`)
return re.ReplaceAllString(htmlContent, "<head>\n "+baseScript)
return strings.Replace(htmlContent, "<head>", "<head>\n "+baseScript, 1)
}

func RandomString(length int) string {
Expand Down