Skip to content

Commit d544eba

Browse files
committed
web: allow custom CSS, nav and footer HTML
Add three new configuration options (http-custom-css, http-nav-html, http-footer-html) that accept file paths to user-provided content. The files are read once at startup. The custom CSS is served at /static/custom.css after the built-in stylesheet, and the HTML snippets are injected in the page header and footer respectively. Signed-off-by: Robin Jarry <robin@jarry.cc>
1 parent 04ca73b commit d544eba

3 files changed

Lines changed: 55 additions & 12 deletions

File tree

pkg/config/config.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@ type IngressConfig struct {
3232
}
3333

3434
type HttpConfig struct {
35-
BaseURL string `help:"Base URL of the patchwork instance."`
36-
Listen string `name:"listen" help:"HTTP listen address." default:"127.0.0.1:8080"`
35+
BaseURL string `help:"Base URL of the patchwork instance."`
36+
Listen string `name:"listen" help:"HTTP listen address." default:"127.0.0.1:8080"`
37+
CustomCSS string `name:"custom-css" help:"Path to a custom CSS file served after the built-in stylesheet." type:"existingfile"`
38+
NavHTML string `name:"nav-html" help:"Path to an HTML file whose content is inserted in the navigation bar." type:"existingfile"`
39+
FooterHTML string `name:"footer-html" help:"Path to an HTML file whose content is inserted in the footer." type:"existingfile"`
3740
}
3841

3942
type SMTPConfig struct {

pkg/web/layout.templ

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ type breadcrumb struct {
1313
}
1414

1515
type pageContext struct {
16-
User *db.User
17-
CSRFToken string
18-
Version string
16+
User *db.User
17+
CSRFToken string
18+
Version string
19+
CustomCSS bool
20+
NavHTML string
21+
FooterHTML string
1922
}
2023

2124
templ layout(title string, crumbs []breadcrumb, pc pageContext) {
@@ -26,6 +29,9 @@ templ layout(title string, crumbs []breadcrumb, pc pageContext) {
2629
<meta name="viewport" content="width=device-width, initial-scale=1"/>
2730
<title>{ title } - Patchwork</title>
2831
<link rel="stylesheet" href="/static/style.css"/>
32+
if pc.CustomCSS {
33+
<link rel="stylesheet" href="/static/custom.css"/>
34+
}
2935
</head>
3036
<body>
3137
<header>
@@ -56,12 +62,18 @@ templ layout(title string, crumbs []breadcrumb, pc pageContext) {
5662
}
5763
</span>
5864
</nav>
65+
if pc.NavHTML != "" {
66+
@templ.Raw(pc.NavHTML)
67+
}
5968
</header>
6069
<main>
6170
{ children... }
6271
</main>
6372
<footer>
6473
Patchwork { pc.Version } | <a href="/api/docs">API</a>
74+
if pc.FooterHTML != "" {
75+
@templ.Raw(pc.FooterHTML)
76+
}
6577
</footer>
6678
</body>
6779
</html>

pkg/web/web.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"io/fs"
1616
"net/http"
1717
"net/url"
18+
"os"
1819
"regexp"
1920
"strconv"
2021
"strings"
@@ -25,6 +26,7 @@ import (
2526

2627
"github.com/getpatchwork/patchwork/pkg/config"
2728
"github.com/getpatchwork/patchwork/pkg/db"
29+
"github.com/getpatchwork/patchwork/pkg/log"
2830
)
2931

3032
//go:embed static/*
@@ -41,10 +43,19 @@ func NewRouter(cfg *config.Config, database *bun.DB, bus db.EventBus, version st
4143
panic("generate CSRF key: " + err.Error())
4244
}
4345
h := &webHandler{cfg: cfg, db: database, csrfKey: csrfKey, version: version}
46+
h.customCSS = readOptionalFile(cfg.Http.CustomCSS)
47+
h.navHTML = readOptionalFile(cfg.Http.NavHTML)
48+
h.footerHTML = readOptionalFile(cfg.Http.FooterHTML)
4449
r.Use(h.sessionMiddleware)
4550

4651
sub, _ := fs.Sub(staticFS, "static")
4752
r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.FS(sub))))
53+
if h.customCSS != "" {
54+
r.Get("/static/custom.css", func(w http.ResponseWriter, r *http.Request) {
55+
w.Header().Set("Content-Type", "text/css; charset=utf-8")
56+
_, _ = w.Write([]byte(h.customCSS))
57+
})
58+
}
4859

4960
r.Get("/", h.ProjectList)
5061
r.Get("/project/{linkname}", h.ProjectDetail)
@@ -105,10 +116,13 @@ func NewRouter(cfg *config.Config, database *bun.DB, bus db.EventBus, version st
105116
}
106117

107118
type webHandler struct {
108-
cfg *config.Config
109-
db *bun.DB
110-
csrfKey []byte
111-
version string
119+
cfg *config.Config
120+
db *bun.DB
121+
csrfKey []byte
122+
version string
123+
customCSS string
124+
navHTML string
125+
footerHTML string
112126
}
113127

114128
func intStr(n int) string {
@@ -258,11 +272,25 @@ func isURL(s string) bool {
258272
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
259273
}
260274

275+
func readOptionalFile(path string) string {
276+
if path == "" {
277+
return ""
278+
}
279+
data, err := os.ReadFile(path)
280+
if err != nil {
281+
log.Fatalf("read %s: %s", path, err)
282+
}
283+
return strings.TrimSpace(string(data))
284+
}
285+
261286
func (h *webHandler) pageCtx(r *http.Request) pageContext {
262287
return pageContext{
263-
User: getWebUser(r),
264-
CSRFToken: h.csrfToken(r),
265-
Version: h.version,
288+
User: getWebUser(r),
289+
CSRFToken: h.csrfToken(r),
290+
Version: h.version,
291+
CustomCSS: h.customCSS != "",
292+
NavHTML: h.navHTML,
293+
FooterHTML: h.footerHTML,
266294
}
267295
}
268296

0 commit comments

Comments
 (0)