diff --git a/chart/gameshelf/templates/configmap.yaml b/chart/gameshelf/templates/configmap.yaml index b306e89..218acf2 100644 --- a/chart/gameshelf/templates/configmap.yaml +++ b/chart/gameshelf/templates/configmap.yaml @@ -7,3 +7,5 @@ metadata: data: port: "8080" site-name: {{ .Values.siteName | quote }} + site-color: {{ .Values.siteColor | default "#3B82F6" | quote }} + custom-branding-enabled: {{ .Values.customBrandingEnabled | quote }} diff --git a/chart/gameshelf/templates/deployment.yaml b/chart/gameshelf/templates/deployment.yaml index d641a35..4dbbc0e 100644 --- a/chart/gameshelf/templates/deployment.yaml +++ b/chart/gameshelf/templates/deployment.yaml @@ -61,6 +61,16 @@ spec: secretKeyRef: name: {{ include "gameshelf.fullname" . }} key: admin-secret + - name: SITE_COLOR + valueFrom: + configMapKeyRef: + name: {{ include "gameshelf.fullname" . }} + key: site-color + - name: CUSTOM_BRANDING_ENABLED + valueFrom: + configMapKeyRef: + name: {{ include "gameshelf.fullname" . }} + key: custom-branding-enabled - name: SDK_SERVICE_URL value: "http://replicated:3000" livenessProbe: diff --git a/chart/gameshelf/values.yaml b/chart/gameshelf/values.yaml index 28ba7da..8edb3db 100644 --- a/chart/gameshelf/values.yaml +++ b/chart/gameshelf/values.yaml @@ -63,6 +63,8 @@ resources: siteName: "GameShelf" adminSecret: "changeme" # REQUIRED — set a strong secret, e.g. --set adminSecret=... or in a values override +siteColor: "#3B82F6" +customBrandingEnabled: "false" # --- Embedded PostgreSQL (Bitnami subchart) --- postgresql: diff --git a/helmchart.yaml b/helmchart.yaml index 94e52b1..7498bd8 100644 --- a/helmchart.yaml +++ b/helmchart.yaml @@ -11,6 +11,8 @@ spec: integrationLicenseID: repl{{ LicenseFieldValue `licenseID` }} adminSecret: repl{{ ConfigOption `admin_secret`}} siteName: repl{{ ConfigOption `site_name`}} + siteColor: repl{{ ConfigOption `site_color`}} + customBrandingEnabled: repl{{ LicenseFieldValue `custom_branding_enabled` }} builder: image: tag: latest diff --git a/internal/api/admin.go b/internal/api/admin.go index 4e47c01..b22dc7e 100644 --- a/internal/api/admin.go +++ b/internal/api/admin.go @@ -92,6 +92,10 @@ func (s *Server) toggleGameHandler(w http.ResponseWriter, r *http.Request) { // POST /admin/branding — update site branding func (s *Server) updateBrandingHandler(w http.ResponseWriter, r *http.Request) { + if !s.cfg.CustomBrandingEnabled { + http.Error(w, "Custom branding requires an upgraded license", http.StatusForbidden) + return + } if err := r.ParseForm(); err != nil { http.Error(w, "bad request", http.StatusBadRequest) return @@ -141,6 +145,10 @@ func (s *Server) logoHandler(w http.ResponseWriter, r *http.Request) { // POST /admin/logo — upload a new logo image func (s *Server) uploadLogoHandler(w http.ResponseWriter, r *http.Request) { + if !s.cfg.CustomBrandingEnabled { + http.Error(w, "Custom branding requires an upgraded license", http.StatusForbidden) + return + } r.Body = http.MaxBytesReader(w, r.Body, 2<<20) // 2MB if err := r.ParseMultipartForm(2 << 20); err != nil { http.Error(w, "file too large (max 2MB)", http.StatusBadRequest) diff --git a/internal/api/handlers.go b/internal/api/handlers.go index 0a863ac..9e800e3 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -33,7 +33,8 @@ type PageData struct { DBScores []db.Score AllGames []db.Game Site *db.Site - IdentitySecretMasked string // shown (masked) on admin panel + IdentitySecretMasked string // shown (masked) on admin panel + CustomBrandingEnabled bool // true when custom_branding_enabled license field is "true" } // pageBase fills the branding fields from the DB and SDK banner state. @@ -43,7 +44,7 @@ func (s *Server) pageBase(r *http.Request) PageData { if err != nil || site == nil { data = PageData{ SiteName: s.cfg.SiteName, - PrimaryColor: "#3B82F6", + PrimaryColor: s.cfg.SiteColor, SecondaryColor: "#1E40AF", BackgroundColor: "#F9FAFB", FontFamily: "system", @@ -60,6 +61,8 @@ func (s *Server) pageBase(r *http.Request) PageData { } } + data.CustomBrandingEnabled = s.cfg.CustomBrandingEnabled + // Populate SDK banners (fail-open: errors are logged and ignored) if s.sdk.Available() { expiresAt, err := s.sdk.GetExpiresAt(r.Context()) diff --git a/internal/config/config.go b/internal/config/config.go index df59973..d7538ba 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,8 +9,10 @@ type Config struct { Port string SiteName string IdentitySecret string // optional; auto-generated and stored in DB if empty - SDKServiceURL string // URL of Replicated SDK sidecar, e.g. http://localhost:3000 - LocalDev bool // LOCAL_DEV=true bypasses SDK gates when SDK_SERVICE_URL is unset + SDKServiceURL string // URL of Replicated SDK sidecar, e.g. http://localhost:3000 + LocalDev bool // LOCAL_DEV=true bypasses SDK gates when SDK_SERVICE_URL is unset + SiteColor string // default primary color (hex), overridden by DB branding settings + CustomBrandingEnabled bool // set by LicenseFieldValue custom_branding_enabled via KOTS } func Load() Config { @@ -22,6 +24,10 @@ func Load() Config { if siteName == "" { siteName = "GameShelf" } + siteColor := os.Getenv("SITE_COLOR") + if siteColor == "" { + siteColor = "#3B82F6" + } adminSecret := os.Getenv("ADMIN_SECRET") if adminSecret == "" { adminSecret = "changeme" @@ -32,8 +38,10 @@ func Load() Config { AdminSecret: adminSecret, Port: port, SiteName: siteName, - IdentitySecret: os.Getenv("IDENTITY_SECRET"), - SDKServiceURL: os.Getenv("SDK_SERVICE_URL"), - LocalDev: os.Getenv("LOCAL_DEV") == "true", + IdentitySecret: os.Getenv("IDENTITY_SECRET"), + SDKServiceURL: os.Getenv("SDK_SERVICE_URL"), + LocalDev: os.Getenv("LOCAL_DEV") == "true", + SiteColor: siteColor, + CustomBrandingEnabled: os.Getenv("CUSTOM_BRANDING_ENABLED") == "true", } } diff --git a/kots-config.yaml b/kots-config.yaml index 3398ef4..426d367 100644 --- a/kots-config.yaml +++ b/kots-config.yaml @@ -17,3 +17,16 @@ spec: type: text default: "GameShelf" help_text: "The name displayed in the browser title and header." + - name: branding + title: Branding + when: '{{repl LicenseFieldValue "custom_branding_enabled" | eq "true"}}' + items: + - name: site_color + title: Primary Color + type: text + default: "#3B82F6" + help_text: "Primary color for the GameShelf UI (hex format, e.g. #3B82F6). Requires the Custom Branding license entitlement." + validation: + regex: + pattern: '^#[0-9A-Fa-f]{6}$' + message: "Must be a valid hex color code (e.g. #3B82F6)" diff --git a/templates/admin.html b/templates/admin.html index 795e1ef..1e0fbb6 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -5,6 +5,7 @@ {{ define "content" }}