Skip to content

Commit

Permalink
support configurable base_path (#250)
Browse files Browse the repository at this point in the history
* support configurable base_path

* temporarily enable docker builds for this branch

* rewrite assets path in prod too

* handle no trailing slash

* strip trailing slash (#246)

* document usage

* redirect user if they somehow bypass the subpath

* redirect user when trailing slash provided (#246)

* remove temporary build test
  • Loading branch information
drewhammond authored Nov 3, 2023
1 parent 31f783f commit ca54790
Show file tree
Hide file tree
Showing 25 changed files with 95 additions and 49 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
${{ env.GHCR_REPO }}
tags: |
type=semver,pattern={{version}}
type=ref,event=branch
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
Expand Down
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ format = json
request_logging = true
[server]
base_path = /
trusted_proxies =
`)

Expand All @@ -42,6 +43,7 @@ type loggingConfig struct {
}

type serverConfig struct {
BasePath string `mapstructure:"base_path"`
EnableGzip bool `mapstructure:"enable_gzip"`
TrustedProxies string `mapstructure:"trusted_proxies"`
}
Expand Down
4 changes: 4 additions & 0 deletions defaults.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ destination = stdout
request_logging = true

[server]
# To serve from a sub path (e.g. in a reverse proxy configuration), specify the sub path here.
# If accessed without the sub path, chefbrowser will redirect the user to the sub path.
base_path = /

# Comma-separated list of proxy servers or networks (in CIDR format) from which to trust request headers
# containing alternate client IP addresses (e.g. X-Forwarded-For or X-Real-IP). Leave empty to ignore these headers
trusted_proxies =
Expand Down
9 changes: 8 additions & 1 deletion internal/app/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v4/middleware"
)

var basePath = ""

type Service struct {
log *logging.Logger
config *config.Config
Expand All @@ -24,13 +26,14 @@ func New(config *config.Config, engine *echo.Echo, chef *chef.Service, logger *l
log: logger,
engine: engine,
}
basePath = config.Server.BasePath
return &s
}

func (s *Service) RegisterRoutes() {
s.log.Info("registering API routes")

router := s.engine.Group("/api")
router := s.engine.Group(urlWithBasePath("/api"))
{
router.Use(middleware.CORS())
// nodes
Expand Down Expand Up @@ -72,6 +75,10 @@ func (s *Service) RegisterRoutes() {
}
}

func urlWithBasePath(path string) string {
return basePath + path
}

type HealthResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Expand Down
17 changes: 17 additions & 0 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package app
import (
"fmt"
"net"
"net/http"
"path"
"strings"

"github.com/drewhammond/chefbrowser/config"
Expand Down Expand Up @@ -39,6 +41,10 @@ func New(cfg *config.Config) {
engine.Debug = true
}

engine.Pre(middleware.RemoveTrailingSlashWithConfig(middleware.TrailingSlashConfig{
RedirectCode: http.StatusMovedPermanently,
}))

engine.Use(middleware.Recover())

if cfg.Logging.RequestLogging {
Expand Down Expand Up @@ -68,6 +74,8 @@ func New(cfg *config.Config) {

chefService := chef.New(cfg, logger)

cfg.Server.BasePath = normalizeBasePath(cfg.Server.BasePath)

app := AppService{
Log: logger,
Chef: chefService,
Expand All @@ -83,3 +91,12 @@ func New(cfg *config.Config) {
app.Log.Fatal("failed to start web server", zap.Error(err))
}
}

// normalizeBasePath cleans and strips trailing slashes from the configured base_path
func normalizeBasePath(p string) string {
p = path.Clean(p)
if p == "." || p == "/" {
return ""
}
return p
}
4 changes: 2 additions & 2 deletions internal/app/ui/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ func (v *Vite) generateTags() error {
return err
}
} else {
v.HTMLTags = `<script type="module" src="http://localhost:5173/ui/@vite/client"></script>
<script type="module" src="http://localhost:5173/ui/main.js"></script>`
v.HTMLTags = `<script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173/main.js"></script>`
}

return nil
Expand Down
28 changes: 21 additions & 7 deletions internal/app/ui/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
"go.uber.org/zap"
)

var viteFS = echo.MustSubFS(ui.Embedded, "dist")
var (
viteFS = echo.MustSubFS(ui.Embedded, "dist")
basePath = ""
)

func embeddedFH(config goview.Config, tmpl string) (string, error) {
path := filepath.Join(config.Root, tmpl)
Expand All @@ -42,6 +45,7 @@ func New(config *config.Config, engine *echo.Echo, chef *chef.Service, logger *l
log: logger,
engine: engine,
}
basePath = config.Server.BasePath
return &s
}

Expand All @@ -52,7 +56,7 @@ func (s *Service) RegisterRoutes() {
disableCache := false
if s.config.App.AppMode == "development" {
s.log.Warn("development mode enabled! view cache is disabled and templates are not loaded from embed.FS")
templateRoot = "internal/app/ui/templates"
templateRoot = "ui/templates"
disableCache = true
}

Expand All @@ -68,7 +72,7 @@ func (s *Service) RegisterRoutes() {

vCfg := ViteConfig{
Environment: s.config.App.AppMode,
Base: "/ui",
Base: urlWithBasePath("/ui"),
}

if s.config.App.AppMode == "production" {
Expand All @@ -83,6 +87,7 @@ func (s *Service) RegisterRoutes() {

viteTags := vite.HTMLTags
cfg.Funcs["makeRunListURL"] = s.makeRunListURL
cfg.Funcs["base_path"] = func() string { return basePath }
cfg.Funcs["app_version"] = func() string { return version.Get().Version }
cfg.Funcs["vite_assets"] = func() template.HTML {
return template.HTML(viteTags)
Expand All @@ -95,8 +100,13 @@ func (s *Service) RegisterRoutes() {

s.engine.Renderer = ev

s.engine.GET(urlWithBasePath(""), func(c echo.Context) error {
return c.Redirect(http.StatusFound, urlWithBasePath("/ui/nodes"))
})

// Always redirect to base path if somehow bypassed
s.engine.GET("/", func(c echo.Context) error {
return c.Redirect(http.StatusFound, "/ui/nodes")
return c.Redirect(http.StatusFound, urlWithBasePath("/ui/nodes"))
})

s.engine.RouteNotFound("/*", func(c echo.Context) error {
Expand All @@ -105,10 +115,10 @@ func (s *Service) RegisterRoutes() {
})
})

router := s.engine.Group("/ui")
router := s.engine.Group(urlWithBasePath("/ui"))
{
router.GET("/", func(c echo.Context) error {
return c.Redirect(http.StatusFound, "/ui/nodes")
return c.Redirect(http.StatusFound, urlWithBasePath("/ui/nodes"))
})
router.GET("/nodes", s.getNodes)
router.GET("/nodes/:name", s.getNode)
Expand Down Expand Up @@ -156,7 +166,7 @@ func CacheControlMiddleware(next echo.HandlerFunc) echo.HandlerFunc {

func ViteHandler() echo.HandlerFunc {
fs := http.FS(viteFS)
h := http.StripPrefix("/ui", http.FileServer(fs))
h := http.StripPrefix(urlWithBasePath("/ui"), http.FileServer(fs))
return echo.WrapHandler(h)
}

Expand Down Expand Up @@ -615,3 +625,7 @@ func (s *Service) getPolicyGroup(c echo.Context) error {
"title": fmt.Sprintf("Policy groups > %s", name),
})
}

func urlWithBasePath(path string) string {
return basePath + path
}
2 changes: 1 addition & 1 deletion ui/templates/cookbook_recipes.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ <h4>Recipes</h4>
<ul class="list-unstyled">
{{ range .recipes }}
<li>
<a href="/ui/cookbooks/{{ $.cookbook.Metadata.Name }}/{{ $.cookbook.Metadata.Version }}/file/{{ .Path }}">{{ .Name }}</a>
<a href="{{ base_path }}/ui/cookbooks/{{ $.cookbook.Metadata.Name }}/{{ $.cookbook.Metadata.Version }}/file/{{ .Path }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
Expand Down
2 changes: 1 addition & 1 deletion ui/templates/cookbooks.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ <h2>Cookbooks <small class="text-muted">({{ len .cookbooks }})</small></h2>
<li>
<span class="">{{$versions.Name}}</span>
{{range .Versions}}
<span class="badge cb-cookbook-version-badge"><a href="/ui/cookbooks/{{$versions.Name}}/{{.}}">{{.}}</a></span>
<span class="badge cb-cookbook-version-badge"><a href="{{ base_path }}/ui/cookbooks/{{$versions.Name}}/{{.}}">{{.}}</a></span>
{{end}}
</li>
{{end}}
Expand Down
2 changes: 1 addition & 1 deletion ui/templates/databag_items.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h2 class="databag-headline">{{.databag}}</h2>
<ul id="databag-list" class="list-unstyled">
{{range $name, $url := .items}}
<li><a href="/ui/databags/{{$.databag}}/{{$name}}/">{{$name}}</a></li>
<li><a href="{{ base_path }}/ui/databags/{{$.databag}}/{{$name}}/">{{$name}}</a></li>
{{end}}
</ul>
{{end}}
2 changes: 1 addition & 1 deletion ui/templates/databags.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h2>Data Bags <small class="text-muted">({{ len .databags }})</small></h2>
<ul id="databag-list" class="list-unstyled">
{{range $name, $url := .databags}}
<li><a href="/ui/databags/{{$name}}">{{$name}}</a></li>
<li><a href="{{ base_path }}/ui/databags/{{$name}}">{{$name}}</a></li>
{{end}}
</ul>
{{end}}
2 changes: 1 addition & 1 deletion ui/templates/environment.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ <h2 class="environment-headline">{{.environment.Name}}</h2>
<p class="lead">{{.environment.Description}}</p>
</div>
<div class="">
<a type="button" href="/ui/nodes?q=chef_environment:{{.environment.Name}}" class="btn btn-outline-primary">View
<a type="button" href="{{ base_path }}/ui/nodes?q=chef_environment:{{.environment.Name}}" class="btn btn-outline-primary">View
Nodes</a>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion ui/templates/environments.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h2>Environments <small class="text-muted">({{ len .environments }})</small></h2>
<ul id="environment-list" class="list-unstyled">
{{range $name, $url := .environments }}
<li><a href="/ui/environments/{{$name}}">{{$name}}</a></li>
<li><a href="{{ base_path }}/ui/environments/{{$name}}">{{$name}}</a></li>
{{end}}
</ul>
{{end}}
4 changes: 2 additions & 2 deletions ui/templates/layouts/master.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@

</script>
{{include "layouts/head"}}
<link rel="alternate icon" class="js-site-favicon" type="image/png" href="/ui/favicons/favicon.png">
<link rel="icon" class="js-site-favicon" type="image/svg+xml" href="/ui/favicons/favicon.svg">
<link rel="alternate icon" class="js-site-favicon" type="image/png" href="{{ base_path }}/ui/favicons/favicon.png">
<link rel="icon" class="js-site-favicon" type="image/svg+xml" href="{{ base_path }}/ui/favicons/favicon.svg">
</head>
<body class="d-flex flex-column h-100">
{{include "layouts/nav"}}
Expand Down
14 changes: 7 additions & 7 deletions ui/templates/layouts/nav.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<header>
<nav class="navbar navbar-expand-sm cb-navbar">
<div class="container">
<a class="navbar-brand" href="/ui/nodes">Chef Browser</a>
<a class="navbar-brand" href="{{ base_path }}/ui/nodes">Chef Browser</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
Expand All @@ -10,24 +10,24 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0 flex-grow-1">
<li class="nav-item">
<a class="nav-link {{ if eq .active_nav "nodes"}}active{{end}}" href="/ui/nodes">Nodes</a>
<a class="nav-link {{ if eq .active_nav "nodes"}}active{{end}}" href="{{ base_path }}/ui/nodes">Nodes</a>
</li>
<li class="nav-item">
<a class="nav-link {{ if eq .active_nav "environments"}}active{{end}}" href="/ui/environments">Environments</a>
<a class="nav-link {{ if eq .active_nav "environments"}}active{{end}}" href="{{ base_path }}/ui/environments">Environments</a>
</li>
<li class="nav-item">
<a class="nav-link {{ if eq .active_nav "roles"}}active{{end}}" href="/ui/roles">Roles</a>
<a class="nav-link {{ if eq .active_nav "roles"}}active{{end}}" href="{{ base_path }}/ui/roles">Roles</a>
</li>
<li class="nav-item">
<a class="nav-link {{ if eq .active_nav "databags"}}active{{end}}" href="/ui/databags">Data
<a class="nav-link {{ if eq .active_nav "databags"}}active{{end}}" href="{{ base_path }}/ui/databags">Data
Bags</a>
</li>
<li class="nav-item">
<a class="nav-link {{ if eq .active_nav "cookbooks"}}active{{end}}" href="/ui/cookbooks">Cookbooks</a>
<a class="nav-link {{ if eq .active_nav "cookbooks"}}active{{end}}" href="{{ base_path }}/ui/cookbooks">Cookbooks</a>
</li>
</ul>
{{ if eq .search_enabled true }}
<form class="d-flex flex-grow-1" role="search" action="/ui/nodes" method="GET">
<form class="d-flex flex-grow-1" role="search" action="{{ base_path }}/ui/nodes" method="GET">
<input id="search-box" name="q" class="form-control me-2" type="search" placeholder="Search"
aria-label="Search">
<button class="btn cb-search-btn" type="submit">Search</button>
Expand Down
2 changes: 1 addition & 1 deletion ui/templates/nodes.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h2>Nodes <small class="text-muted">({{ len .nodes }})</small></h2>
<ul id="node-list" class="list-unstyled">
{{range .nodes}}
<li><a href="/ui/nodes/{{.}}">{{.}}</a></li>
<li><a href="{{ base_path }}/ui/nodes/{{.}}">{{.}}</a></li>
{{end}}
</ul>
{{end}}
11 changes: 6 additions & 5 deletions ui/templates/partials/cookbook/header.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="row align-items-center">
<div class="col-auto">
<h1 class="cookbook-headline">
<a href="/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}">{{.cookbook.Metadata.Name}}</a>
<a href="{{ base_path }}/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}">{{.cookbook.Metadata.Name}}</a>
</h1>
</div>
<div class="col-auto">
Expand All @@ -14,8 +14,9 @@ <h1 class="cookbook-headline">
<p class="lead">{{ .cookbook.Metadata.Description }}</p>
</div>
<script>
baseUrl = {{ base_path }}
async function fetchVersions() {
const response = await fetch('/api/cookbooks/{{.cookbook.Metadata.Name}}/versions');
const response = await fetch(baseUrl + '/api/cookbooks/{{.cookbook.Metadata.Name}}/versions');
const data = await response.json();
return data
}
Expand Down Expand Up @@ -45,15 +46,15 @@ <h1 class="cookbook-headline">
<ul class="nav nav-tabs justify-content-end">
<li class="nav-item">
<a class="nav-link {{ if eq .active_tab "recipes"}}active{{end}}"
href="/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}/recipes" data-bs-target="#recipes-tab-pane">Recipes</a>
href="{{ base_path }}/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}/recipes" data-bs-target="#recipes-tab-pane">Recipes</a>
</li>
<li class="nav-item">
<a class="nav-link {{ if eq .active_tab "files"}}active{{end}}"
href="/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}/files" data-bs-target="#files-tab-pane">Files</a>
href="{{ base_path }}/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}/files" data-bs-target="#files-tab-pane">Files</a>
</li>
<li class="nav-item">
<a class="nav-link {{ if eq .active_tab "overview"}}active{{end}}" data-bs-target="#overview-tab-pane"
href="/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}">Overview</a>
href="{{ base_path }}/ui/cookbooks/{{.cookbook.Metadata.Name}}/{{.cookbook.Metadata.Version}}">Overview</a>
</li>
</ul>
</div>
Loading

0 comments on commit ca54790

Please sign in to comment.