diff --git a/README.md b/README.md index fabef00d..78b603f7 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,6 @@ Note that you can also add environment variables in the configuration file (i.e. | `web` | Web configuration | `{}` | | `web.address` | Address to listen on | `0.0.0.0` | | `web.port` | Port to listen on | `8080` | -| `web.context-root` | Context root at which Gatus will be exposed (frontend and backend) | `/` | For Kubernetes configuration, see [Kubernetes](#kubernetes-alpha) diff --git a/config/config.go b/config/config.go index 420ec8e2..731451d0 100644 --- a/config/config.go +++ b/config/config.go @@ -28,9 +28,6 @@ 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 ( @@ -153,7 +150,7 @@ func parseAndValidateConfigBytes(yamlBytes []byte) (config *Config, err error) { func validateWebConfig(config *Config) { if config.Web == nil { - config.Web = &webConfig{Address: DefaultAddress, Port: DefaultPort, ContextRoot: DefaultContextRoot} + config.Web = &webConfig{Address: DefaultAddress, Port: DefaultPort} } else { config.Web.validateAndSetDefaults() } diff --git a/config/config_test.go b/config/config_test.go index af47d62d..5b9fb8ef 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -119,9 +119,6 @@ services: if config.Web.Port != DefaultPort { t.Errorf("Port should have been %d, because it is the default value", DefaultPort) } - if config.Web.ContextRoot != DefaultContextRoot { - t.Errorf("ContextRoot should have been %s, because it is the default value", DefaultContextRoot) - } } func TestParseAndValidateConfigBytesWithAddress(t *testing.T) { @@ -226,44 +223,6 @@ services: } } -func TestParseAndValidateConfigBytesWithPortAndHostAndContextRoot(t *testing.T) { - config, err := parseAndValidateConfigBytes([]byte(` -web: - port: 12345 - address: 127.0.0.1 - context-root: /deeply/nested/down=/their -services: - - name: twinnation - url: https://twinnation.org/health - conditions: - - "[STATUS] == 200" -`)) - if err != nil { - t.Error("No error should've been returned") - } - if config == nil { - t.Fatal("Config shouldn't have been nil") - } - if config.Metrics { - t.Error("Metrics should've been false by default") - } - if config.Services[0].URL != "https://twinnation.org/health" { - t.Errorf("URL should have been %s", "https://twinnation.org/health") - } - if config.Services[0].Interval != 60*time.Second { - t.Errorf("Interval should have been %s, because it is the default value", 60*time.Second) - } - if config.Web.Address != "127.0.0.1" { - t.Errorf("Bind address should have been %s, because it is specified in config", "127.0.0.1") - } - if config.Web.Port != 12345 { - t.Errorf("Port should have been %d, because it is specified in config", 12345) - } - if config.Web.ContextRoot != "/deeply/nested/down=/their/" { - t.Errorf("Port should have been %s, because it is specified in config", "/deeply/nested/down=/their/") - } -} - func TestParseAndValidateConfigBytesWithInvalidPort(t *testing.T) { defer func() { recover() }() _, _ = parseAndValidateConfigBytes([]byte(` @@ -311,9 +270,6 @@ services: if config.Web.Port != DefaultPort { t.Errorf("Port should have been %d, because it is the default value", DefaultPort) } - if config.Web.ContextRoot != DefaultContextRoot { - t.Errorf("ContextRoot should have been %s, because it is the default value", DefaultContextRoot) - } if userAgent := config.Services[0].Headers["User-Agent"]; userAgent != "Test/2.0" { t.Errorf("User-Agent should've been %s, got %s", "Test/2.0", userAgent) } diff --git a/config/web.go b/config/web.go index 5ea0daf0..e69e9fc4 100644 --- a/config/web.go +++ b/config/web.go @@ -3,8 +3,6 @@ package config import ( "fmt" "math" - "net/url" - "strings" ) // webConfig is the structure which supports the configuration of the endpoint @@ -15,9 +13,6 @@ type webConfig struct { // Port to listen on (default to 8080 specified by DefaultPort) Port int `yaml:"port"` - - // ContextRoot set the root context for the web application - ContextRoot string `yaml:"context-root"` } // validateAndSetDefaults checks and sets the default values for fields that are not set @@ -32,32 +27,9 @@ func (web *webConfig) validateAndSetDefaults() { } else if web.Port < 0 || web.Port > math.MaxUint16 { panic(fmt.Sprintf("invalid port: value should be between %d and %d", 0, math.MaxUint16)) } - // Validate the ContextRoot - if len(web.ContextRoot) == 0 { - web.ContextRoot = DefaultContextRoot - } else { - trimmedContextRoot := strings.Trim(web.ContextRoot, "/") - if len(trimmedContextRoot) == 0 { - web.ContextRoot = DefaultContextRoot - return - } - rootContextURL, err := url.Parse(trimmedContextRoot) - if err != nil { - panic("invalid context root:" + err.Error()) - } - if rootContextURL.Path != trimmedContextRoot { - panic("invalid context root: too complex") - } - web.ContextRoot = "/" + strings.Trim(rootContextURL.Path, "/") + "/" - } } // SocketAddress returns the combination of the Address and the Port func (web *webConfig) SocketAddress() string { return fmt.Sprintf("%s:%d", web.Address, web.Port) } - -// PrependWithContextRoot appends the given path to the ContextRoot -func (web *webConfig) PrependWithContextRoot(path string) string { - return web.ContextRoot + strings.Trim(path, "/") -} diff --git a/config/web_test.go b/config/web_test.go index 02b1bca0..6375381a 100644 --- a/config/web_test.go +++ b/config/web_test.go @@ -1,7 +1,6 @@ package config import ( - "fmt" "testing" ) @@ -14,86 +13,3 @@ func TestWebConfig_SocketAddress(t *testing.T) { t.Errorf("expected %s, got %s", "0.0.0.0:8081", web.SocketAddress()) } } - -func TestWebConfig_PrependWithContextRoot(t *testing.T) { - web := &webConfig{ContextRoot: "/status/"} - if result := web.PrependWithContextRoot("/api/v1/results"); result != "/status/api/v1/results" { - t.Errorf("expected %s, got %s", "/status/api/v1/results", result) - } - if result := web.PrependWithContextRoot("/health"); result != "/status/health" { - t.Errorf("expected %s, got %s", "/status/health", result) - } - if result := web.PrependWithContextRoot("/health/"); result != "/status/health" { - t.Errorf("expected %s, got %s", "/status/health", result) - } -} - -// validContextRootTest specifies all test case which should end up in -// a valid context root used to bind the web interface to -var validContextRootTests = []struct { - name string - path string - expectedPath string -}{ - {"Empty", "", "/"}, - {"/", "/", "/"}, - {"///", "///", "/"}, - {"Single character 'a'", "a", "/a/"}, - {"Slash at the beginning", "/status", "/status/"}, - {"Slashes at start and end", "/status/", "/status/"}, - {"Multiple slashes at start", "//status", "/status/"}, - {"Multiple slashes at start and end", "///status////", "/status/"}, - {"Contains '@' in path'", "me@/status/gatus", "/me@/status/gatus/"}, - {"Nested context with trailing slash", "/status/gatus/", "/status/gatus/"}, - {"Nested context without trailing slash", "/status/gatus/system", "/status/gatus/system/"}, -} - -func TestWebConfig_ValidContextRoots(t *testing.T) { - for idx, test := range validContextRootTests { - t.Run(fmt.Sprintf("%d: %s", idx, test.name), func(t *testing.T) { - expectValidResultForContextRoot(t, test.path, test.expectedPath) - }) - } -} - -func expectValidResultForContextRoot(t *testing.T, path string, expected string) { - web := &webConfig{ - ContextRoot: path, - } - web.validateAndSetDefaults() - if web.ContextRoot != expected { - t.Errorf("expected %s, got %s", expected, web.ContextRoot) - } -} - -// invalidContextRootTests contains all tests for context root which are -// expected to fail and stop program execution -var invalidContextRootTests = []struct { - name string - path string -}{ - {"Only a fragment identifier", "#"}, - {"Invalid character in path", "/invalid" + string([]byte{0x7F})}, - {"Starts with protocol", "http://status/gatus"}, - {"Path with fragment", "/status/gatus#here"}, - {"Starts with '://'", "://status"}, - {"Contains query parameter", "/status/h?ello=world"}, - {"Contains '?'", "/status?"}, -} - -func TestWebConfig_InvalidContextRoots(t *testing.T) { - for idx, test := range invalidContextRootTests { - t.Run(fmt.Sprintf("%d: %s", idx, test.name), func(t *testing.T) { - expectInvalidResultForContextRoot(t, test.path) - }) - } -} - -func expectInvalidResultForContextRoot(t *testing.T, path string) { - defer func() { recover() }() - - web := &webConfig{ContextRoot: path} - web.validateAndSetDefaults() - - t.Fatal(fmt.Sprintf("Should've panicked because the configuration specifies an invalid context root: %s", path)) -} diff --git a/controller/controller.go b/controller/controller.go index 50b2781f..5e6bbb85 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -47,7 +47,7 @@ func Handle() { WriteTimeout: 15 * time.Second, IdleTimeout: 15 * time.Second, } - log.Printf("[controller][Handle] Listening on %s%s\n", cfg.Web.SocketAddress(), cfg.Web.ContextRoot) + log.Println("[controller][Handle] Listening on" + cfg.Web.SocketAddress()) log.Fatal(server.ListenAndServe()) } @@ -56,13 +56,13 @@ func CreateRouter(cfg *config.Config) *mux.Router { router := mux.NewRouter() router.HandleFunc("/favicon.ico", favIconHandler).Methods("GET") // favicon needs to be always served from the root router.HandleFunc("/services/{service}", spaHandler).Methods("GET") - router.HandleFunc(cfg.Web.PrependWithContextRoot("/api/v1/statuses"), secureIfNecessary(cfg, serviceStatusesHandler)).Methods("GET") - router.HandleFunc(cfg.Web.PrependWithContextRoot("/api/v1/statuses/{key}"), secureIfNecessary(cfg, GzipHandlerFunc(serviceStatusHandler))).Methods("GET") - router.HandleFunc(cfg.Web.PrependWithContextRoot("/api/v1/badges/uptime/{duration}/{identifier}"), badgeHandler).Methods("GET") - router.HandleFunc(cfg.Web.PrependWithContextRoot("/health"), healthHandler).Methods("GET") - router.PathPrefix(cfg.Web.ContextRoot).Handler(GzipHandler(http.StripPrefix(cfg.Web.ContextRoot, http.FileServer(http.Dir("./web/static"))))) + router.HandleFunc("/api/v1/statuses", secureIfNecessary(cfg, serviceStatusesHandler)).Methods("GET") + router.HandleFunc("/api/v1/statuses/{key}", secureIfNecessary(cfg, GzipHandlerFunc(serviceStatusHandler))).Methods("GET") + router.HandleFunc("/api/v1/badges/uptime/{duration}/{identifier}", badgeHandler).Methods("GET") + router.HandleFunc("/health", healthHandler).Methods("GET") + router.PathPrefix("/").Handler(GzipHandler(http.FileServer(http.Dir("./web/static")))) if cfg.Metrics { - router.Handle(cfg.Web.PrependWithContextRoot("/metrics"), promhttp.Handler()).Methods("GET") + router.Handle("/metrics", promhttp.Handler()).Methods("GET") } return router } diff --git a/web/app/src/components/Service.vue b/web/app/src/components/Service.vue index 17c81d18..a4569240 100644 --- a/web/app/src/components/Service.vue +++ b/web/app/src/components/Service.vue @@ -72,7 +72,7 @@ export default { if (!this.data) { return '/'; } - return '/services/' + this.data.key; + return `/services/${this.data.key}`; }, showTooltip(result, event) { this.$emit('showTooltip', result, event); diff --git a/web/app/src/router/index.js b/web/app/src/router/index.js index 8c3ef5ca..12567e5a 100644 --- a/web/app/src/router/index.js +++ b/web/app/src/router/index.js @@ -1,23 +1,23 @@ -import { createRouter, createWebHistory } from 'vue-router' -import Home from '../views/Home.vue' +import {createRouter, createWebHistory} from 'vue-router' +import Home from '@/views/Home' import Details from "@/views/Details"; const routes = [ - { - path: '/', - name: 'Home', - component: Home - }, - { - path: '/services/:key', - name: 'Details', - component: Details - } + { + path: '/', + name: 'Home', + component: Home + }, + { + path: '/services/:key', + name: 'Details', + component: Details, + }, ] const router = createRouter({ - history: createWebHistory(process.env.BASE_URL), - routes + history: createWebHistory(process.env.BASE_URL), + routes }) export default router diff --git a/web/app/src/views/Details.vue b/web/app/src/views/Details.vue index 9a139fc5..94adfd2f 100644 --- a/web/app/src/views/Details.vue +++ b/web/app/src/views/Details.vue @@ -1,5 +1,5 @@