mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 10:33:29 +00:00
fix(cloudcp): serve control plane favicon
This commit is contained in:
parent
140aa2bdb1
commit
8ecea8dbdf
5 changed files with 98 additions and 0 deletions
|
|
@ -781,6 +781,7 @@ func TestPortalPageTemplate_AccountServicesRendered(t *testing.T) {
|
|||
|
||||
mustContain := []string{
|
||||
"<title>Pulse Account</title>",
|
||||
`<link rel="icon" href="/favicon.svg" type="image/svg+xml">`,
|
||||
`id="portal-user-info"`,
|
||||
`id="portal-app-root"`,
|
||||
`id="pulse-account-bootstrap"`,
|
||||
|
|
@ -1074,6 +1075,7 @@ func TestPortalPageTemplate_UsesPulseAccountBrandingWhenSignedOut(t *testing.T)
|
|||
|
||||
mustContain := []string{
|
||||
"<title>Pulse Account</title>",
|
||||
`<link rel="icon" href="/favicon.svg" type="image/svg+xml">`,
|
||||
"Pulse Account",
|
||||
`id="portal-app-root"`,
|
||||
"Enter the commercial email address for your Pulse account.",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Pulse Account</title>
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<style nonce="{{.Nonce}}">{{.Styles}}</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ func RegisterRoutes(mux *http.ServeMux, deps *Deps) {
|
|||
// Health / readiness are unauthenticated liveness/readiness probes.
|
||||
mux.HandleFunc("/healthz", admin.HandleHealthz)
|
||||
mux.HandleFunc("/readyz", admin.HandleReadyz(deps.Registry))
|
||||
mux.HandleFunc("/favicon.svg", handleControlPlaneFaviconSVG)
|
||||
mux.HandleFunc("/favicon.ico", handleControlPlaneFaviconICO)
|
||||
|
||||
// Status and metrics are private by default.
|
||||
statusHandler := http.HandlerFunc(admin.HandleStatus(deps.Registry, deps.Version))
|
||||
|
|
|
|||
60
internal/cloudcp/routes_favicon_test.go
Normal file
60
internal/cloudcp/routes_favicon_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package cloudcp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/cloudcp/registry"
|
||||
)
|
||||
|
||||
func TestRegisterRoutes_ControlPlaneFavicons(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
reg, err := registry.NewTenantRegistry(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTenantRegistry: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = reg.Close() })
|
||||
trialStore, err := NewTrialSignupStore(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTrialSignupStore: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { trialStore.Close() })
|
||||
|
||||
mux := http.NewServeMux()
|
||||
RegisterRoutes(mux, &Deps{
|
||||
Config: &CPConfig{
|
||||
DataDir: dir,
|
||||
AdminKey: "test-admin-key",
|
||||
BaseURL: "https://cloud.example.com",
|
||||
StripeWebhookSecret: "whsec_test",
|
||||
},
|
||||
Registry: reg,
|
||||
TrialSignupStore: trialStore,
|
||||
Version: "test",
|
||||
})
|
||||
|
||||
svgReq := httptest.NewRequest(http.MethodGet, "/favicon.svg", nil)
|
||||
svgRec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(svgRec, svgReq)
|
||||
if svgRec.Code != http.StatusOK {
|
||||
t.Fatalf("GET /favicon.svg status=%d, want %d", svgRec.Code, http.StatusOK)
|
||||
}
|
||||
if got := svgRec.Header().Get("Content-Type"); got != "image/svg+xml" {
|
||||
t.Fatalf("GET /favicon.svg content-type=%q, want %q", got, "image/svg+xml")
|
||||
}
|
||||
if !strings.Contains(svgRec.Body.String(), "<svg") {
|
||||
t.Fatalf("expected svg favicon payload")
|
||||
}
|
||||
|
||||
icoReq := httptest.NewRequest(http.MethodGet, "/favicon.ico", nil)
|
||||
icoRec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(icoRec, icoReq)
|
||||
if icoRec.Code != http.StatusMovedPermanently {
|
||||
t.Fatalf("GET /favicon.ico status=%d, want %d", icoRec.Code, http.StatusMovedPermanently)
|
||||
}
|
||||
if got := icoRec.Header().Get("Location"); got != "/favicon.svg" {
|
||||
t.Fatalf("GET /favicon.ico location=%q, want %q", got, "/favicon.svg")
|
||||
}
|
||||
}
|
||||
33
internal/cloudcp/static_assets.go
Normal file
33
internal/cloudcp/static_assets.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package cloudcp
|
||||
|
||||
import "net/http"
|
||||
|
||||
const controlPlaneFaviconSVG = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="14" fill="#0f172a"/>
|
||||
<path d="M18 18h13c9.941 0 18 8.059 18 18s-8.059 18-18 18H18V18zm11 10h-3v16h3c4.418 0 8-3.582 8-8s-3.582-8-8-8z" fill="#38bdf8"/>
|
||||
<circle cx="47" cy="18" r="5" fill="#1d4ed8"/>
|
||||
</svg>
|
||||
`
|
||||
|
||||
func handleControlPlaneFaviconSVG(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "image/svg+xml")
|
||||
w.Header().Set("Cache-Control", "public, max-age=86400")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if r.Method == http.MethodHead {
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte(controlPlaneFaviconSVG))
|
||||
}
|
||||
|
||||
func handleControlPlaneFaviconICO(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/favicon.svg", http.StatusMovedPermanently)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue