diff --git a/backend/internal/web/embed_on.go b/backend/internal/web/embed_on.go
index f7ba5c9e5..ca1b6f7ea 100644
--- a/backend/internal/web/embed_on.go
+++ b/backend/internal/web/embed_on.go
@@ -10,6 +10,7 @@ import (
"io"
"io/fs"
"net/http"
+ "reflect"
"strings"
"time"
@@ -17,6 +18,30 @@ import (
"github.com/gin-gonic/gin"
)
+// extractSiteLogo extracts the site_logo field from the settings struct using reflection
+// This avoids tight coupling with the specific struct type returned by GetPublicSettingsForInjection
+func extractSiteLogo(settings any) string {
+ if settings == nil {
+ return ""
+ }
+
+ // Try to access SiteLogo field directly via reflection
+ v := reflect.ValueOf(settings)
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+ if v.Kind() != reflect.Struct {
+ return ""
+ }
+
+ field := v.FieldByName("SiteLogo")
+ if field.IsValid() && field.Kind() == reflect.String {
+ return field.String()
+ }
+
+ return ""
+}
+
const (
// NonceHTMLPlaceholder is the placeholder for nonce in HTML script tags
NonceHTMLPlaceholder = "__CSP_NONCE_VALUE__"
@@ -165,7 +190,10 @@ func (s *FrontendServer) serveIndexHTML(c *gin.Context) {
return
}
- rendered := s.injectSettings(settingsJSON)
+ // Extract site_logo for favicon replacement
+ siteLogo := extractSiteLogo(settings)
+
+ rendered := s.injectSettings(settingsJSON, siteLogo)
s.cache.Set(rendered, settingsJSON)
// Replace nonce placeholder with actual nonce before serving
@@ -180,14 +208,26 @@ func (s *FrontendServer) serveIndexHTML(c *gin.Context) {
c.Abort()
}
-func (s *FrontendServer) injectSettings(settingsJSON []byte) []byte {
+func (s *FrontendServer) injectSettings(settingsJSON []byte, siteLogo string) []byte {
+ html := s.baseHTML
+
+ // If custom site logo is set, replace the default favicon in HTML
+ // This prevents the default logo from flashing before JS loads
+ if siteLogo != "" {
+ // Replace with custom logo
+ // Match various forms of the favicon link tag
+ faviconPattern := []byte(``)
+ newFavicon := []byte(``)
+ html = bytes.Replace(html, faviconPattern, newFavicon, 1)
+ }
+
// Create the script tag to inject with nonce placeholder
// The placeholder will be replaced with actual nonce at request time
script := []byte(``)
// Inject before
headClose := []byte("")
- return bytes.Replace(s.baseHTML, headClose, append(script, headClose...), 1)
+ return bytes.Replace(html, headClose, append(script, headClose...), 1)
}
// replaceNoncePlaceholder replaces the nonce placeholder with actual nonce value
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index d88c6eed1..977dcc6a8 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -20,8 +20,20 @@ function injectPublicSettings(backendUrl: string): Plugin {
if (response.ok) {
const data = await response.json()
if (data.code === 0 && data.data) {
+ // Inject config script
const script = ``
- return html.replace('', `${script}\n`)
+ let result = html.replace('', `${script}\n`)
+
+ // Replace favicon with custom logo if set (prevents default logo flash)
+ const siteLogo = data.data.site_logo
+ if (siteLogo) {
+ result = result.replace(
+ '',
+ ``
+ )
+ }
+
+ return result
}
}
} catch (e) {