Remove web.context-root
This commit is contained in:
		| @ -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) | ||||
|  | ||||
|  | ||||
| @ -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() | ||||
| 	} | ||||
|  | ||||
| @ -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) | ||||
| 	} | ||||
|  | ||||
| @ -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, "/") | ||||
| } | ||||
|  | ||||
| @ -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)) | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| 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 = [ | ||||
| @ -11,8 +11,8 @@ const routes = [ | ||||
| 	{ | ||||
| 		path: '/services/:key', | ||||
| 		name: 'Details', | ||||
|     component: Details | ||||
|   } | ||||
| 		component: Details, | ||||
| 	}, | ||||
| ] | ||||
|  | ||||
| const router = createRouter({ | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <router-link to="/" class="absolute top-2 left-2 inline-block px-2 py-0 text-lg text-black transition bg-gray-100 rounded shadow ripple hover:shadow-lg hover:bg-gray-200 focus:outline-none"> | ||||
|   <router-link to="../" class="absolute top-2 left-2 inline-block px-2 py-0 text-lg text-black transition bg-gray-100 rounded shadow ripple hover:shadow-lg hover:bg-gray-200 focus:outline-none"> | ||||
|     ← | ||||
|   </router-link> | ||||
|   <div class="container mx-auto"> | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| module.exports = { | ||||
| 	filenameHashing: false, | ||||
| 	productionSourceMap: false, | ||||
| 	outputDir: '../static' | ||||
| 	outputDir: '../static', | ||||
| 	publicPath: '/' | ||||
| } | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user