mirror of
https://github.com/TwiN/gatus.git
synced 2024-12-14 11:58:04 +00:00
start working on context root configuration
This commit is contained in:
parent
76d45d7eb8
commit
f9706a98ed
6 changed files with 126 additions and 9 deletions
|
@ -139,6 +139,7 @@ Note that you can also add environment variables in the configuration file (i.e.
|
|||
| `disable-monitoring-lock` | Whether to [disable the monitoring lock](#disable-monitoring-lock) | `false` |
|
||||
| `web.address` | Address to listen on | `0.0.0.0` |
|
||||
| `web.port` | Port to listen on | `8080` |
|
||||
| `web.context-root` | Context root which should be used by the web frontent | `/` |
|
||||
|
||||
For Kubernetes configuration, see [Kubernetes](#kubernetes-alpha)
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ const (
|
|||
|
||||
// DefaultPort is the default port the service will listen on
|
||||
DefaultPort = 8080
|
||||
|
||||
// DefaultContextRoot is the default context root of the web application
|
||||
DefaultContextRoot = "/"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -3,6 +3,8 @@ package config
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// webConfig is the structure which supports the configuration of the endpoint
|
||||
|
@ -13,6 +15,10 @@ type webConfig struct {
|
|||
|
||||
// Port to listen on (default to 8080 specified by DefaultPort)
|
||||
Port int `yaml:"port"`
|
||||
|
||||
ContextRoot string `yaml:"context-root"`
|
||||
|
||||
safeContextRoot string
|
||||
}
|
||||
|
||||
// validateAndSetDefaults checks and sets missing values based on the defaults
|
||||
|
@ -26,9 +32,43 @@ func (web *webConfig) validateAndSetDefaults() {
|
|||
} else if web.Port < 0 || web.Port > math.MaxUint16 {
|
||||
panic(fmt.Sprintf("port has an invalid: value should be between %d and %d", 0, math.MaxUint16))
|
||||
}
|
||||
if len(web.ContextRoot) == 0 {
|
||||
web.safeContextRoot = DefaultContextRoot
|
||||
} else {
|
||||
// url.PathEscape escapes all "/", in order to build a secure path
|
||||
// (1) split into path fragements using "/" as delimiter
|
||||
// (2) use url.PathEscape() on each fragment
|
||||
// (3) re-concatinate the path using "/" as join character
|
||||
const splitJoinChar = "/"
|
||||
pathes := strings.Split(web.ContextRoot, splitJoinChar)
|
||||
escapedPathes := make([]string, len(pathes))
|
||||
for i, path := range pathes {
|
||||
escapedPathes[i] = url.PathEscape(path)
|
||||
}
|
||||
|
||||
web.safeContextRoot = strings.Join(escapedPathes, splitJoinChar)
|
||||
|
||||
// assure that we have still a valid url
|
||||
_, err := url.Parse(web.safeContextRoot)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Invalid context root %s - Error %s", web.ContextRoot, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SocketAddress returns the combination of the Address and the Port
|
||||
func (web *webConfig) SocketAddress() string {
|
||||
return fmt.Sprintf("%s:%d", web.Address, web.Port)
|
||||
}
|
||||
|
||||
// CtxRoot returns the context root
|
||||
func (web *webConfig) CtxRoot() string {
|
||||
return web.safeContextRoot
|
||||
}
|
||||
|
||||
// AppendToCtxRoot appends the given string to the context root
|
||||
// AppendToCtxRoot takes care of having only one "/" character at
|
||||
// the join point and exactly on "/" at the end
|
||||
func (web *webConfig) AppendToCtxRoot(fragment string) string {
|
||||
return strings.TrimSuffix(web.safeContextRoot, "/") + "/" + strings.Trim(fragment, "/") + "/"
|
||||
}
|
||||
|
|
|
@ -11,3 +11,68 @@ func TestWebConfig_SocketAddress(t *testing.T) {
|
|||
t.Errorf("expected %s, got %s", "0.0.0.0:8081", web.SocketAddress())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebConfig_ContextRoot(t *testing.T) {
|
||||
const expected = "/status/"
|
||||
|
||||
web := &webConfig{
|
||||
ContextRoot: "/status/",
|
||||
}
|
||||
|
||||
web.validateAndSetDefaults()
|
||||
|
||||
if web.CtxRoot() != expected {
|
||||
t.Errorf("expected %s, got %s", expected, web.CtxRoot())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebConfig_ContextRootWithEscapableChars(t *testing.T) {
|
||||
const expected = "/s%3F=ta%20t%20u&s/"
|
||||
|
||||
web := &webConfig{
|
||||
ContextRoot: "/s?=ta t u&s/",
|
||||
}
|
||||
|
||||
web.validateAndSetDefaults()
|
||||
|
||||
if web.CtxRoot() != expected {
|
||||
t.Errorf("expected %s, got %s", expected, web.CtxRoot())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebConfig_ContextRootMultiPath(t *testing.T) {
|
||||
const expected = "/app/status"
|
||||
web := &webConfig{
|
||||
ContextRoot: "/app/status",
|
||||
}
|
||||
|
||||
web.validateAndSetDefaults()
|
||||
|
||||
if web.CtxRoot() != expected {
|
||||
t.Errorf("expected %s, got %s", expected, web.CtxRoot())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebConfig_ContextRootAppendWithEmptyContextRoot(t *testing.T) {
|
||||
const expected = "/bla/"
|
||||
web := &webConfig{}
|
||||
|
||||
web.validateAndSetDefaults()
|
||||
|
||||
if web.AppendToCtxRoot("/bla/") != expected {
|
||||
t.Errorf("expected %s, got %s", expected, web.AppendToCtxRoot("/bla/"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebConfig_ContextRootAppendWithContext(t *testing.T) {
|
||||
const expected = "/app/status/bla/"
|
||||
web := &webConfig{
|
||||
ContextRoot: "/app/status",
|
||||
}
|
||||
|
||||
web.validateAndSetDefaults()
|
||||
|
||||
if web.AppendToCtxRoot("/bla/") != expected {
|
||||
t.Errorf("expected %s, got %s", expected, web.AppendToCtxRoot("/bla/"))
|
||||
}
|
||||
}
|
||||
|
|
18
main.go
18
main.go
|
@ -29,13 +29,16 @@ func main() {
|
|||
if cfg.Security != nil && cfg.Security.IsValid() {
|
||||
resultsHandler = security.Handler(serviceResultsHandler, cfg.Security)
|
||||
}
|
||||
http.HandleFunc("/api/v1/results", resultsHandler)
|
||||
http.HandleFunc("/health", healthHandler)
|
||||
http.Handle("/", GzipHandler(http.FileServer(http.Dir("./static"))))
|
||||
// favicon needs to be always served from the root
|
||||
http.HandleFunc("/favicon.ico", favIconHandler)
|
||||
http.HandleFunc(cfg.Web.AppendToCtxRoot("/api/v1/results"), resultsHandler)
|
||||
http.HandleFunc(cfg.Web.AppendToCtxRoot("/health"), healthHandler)
|
||||
http.Handle(cfg.Web.CtxRoot(), GzipHandler(http.StripPrefix(cfg.Web.CtxRoot(), http.FileServer(http.Dir("./static")))))
|
||||
|
||||
if cfg.Metrics {
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
http.Handle(cfg.Web.AppendToCtxRoot("/metrics"), promhttp.Handler())
|
||||
}
|
||||
log.Printf("[main][main] Listening on %s\n", cfg.Web.SocketAddress())
|
||||
log.Printf("[main][main] Listening on %s%s\n", cfg.Web.SocketAddress(), cfg.Web.CtxRoot())
|
||||
go watchdog.Monitor(cfg)
|
||||
log.Fatal(http.ListenAndServe(cfg.Web.SocketAddress(), nil))
|
||||
}
|
||||
|
@ -88,3 +91,8 @@ func healthHandler(writer http.ResponseWriter, _ *http.Request) {
|
|||
writer.WriteHeader(http.StatusOK)
|
||||
_, _ = writer.Write([]byte("{\"status\":\"UP\"}"))
|
||||
}
|
||||
|
||||
// favIconHanlder responds to /favicon.ico requests
|
||||
func favIconHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
http.ServeFile(writer, request, "./static/favicon.ico")
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<title>Health Dashboard</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="./bootstrap.min.css" />
|
||||
<style>
|
||||
html, body {
|
||||
background-color: #f7f9fb;
|
||||
|
@ -129,11 +129,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/jquery.min.js"></script>
|
||||
<script src="./jquery.min.js"></script>
|
||||
|
||||
<div id="social">
|
||||
<a href="https://github.com/TwinProduction/gatus" target="_blank" title="Gatus on GitHub">
|
||||
<img src="/github.png" alt="GitHub" width="32" height="auto" />
|
||||
<img src="./github.png" alt="GitHub" width="32" height="auto" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
@ -220,7 +220,7 @@
|
|||
}
|
||||
|
||||
function refreshResults() {
|
||||
$.getJSON("/api/v1/results", function (data) {
|
||||
$.getJSON("./api/v1/results", function (data) {
|
||||
// Update the table only if there's a change
|
||||
if (JSON.stringify(serviceStatuses) !== JSON.stringify(data)) {
|
||||
serviceStatuses = data;
|
||||
|
|
Loading…
Reference in a new issue