diff --git a/.gitignore b/.gitignore index b214b1379..f9641df70 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ fabio.sublime-* demo/cert/ /pkg/ dist/ +*.app +*.hugo_build.lock \ No newline at end of file diff --git a/admin/server.go b/admin/server.go index dec3c0a56..824a73f6c 100644 --- a/admin/server.go +++ b/admin/server.go @@ -63,7 +63,7 @@ func (s *Server) handler() http.Handler { mux.Handle("/api/config", &api.ConfigHandler{Config: s.Cfg}) mux.Handle("/api/routes", &api.RoutesHandler{}) mux.Handle("/api/version", &api.VersionHandler{Version: s.Version}) - mux.Handle("/routes", &ui.RoutesHandler{Color: s.Color, Title: s.Title, Version: s.Version}) + mux.Handle("/routes", &ui.RoutesHandler{Color: s.Color, Title: s.Title, Version: s.Version, RoutingTable: s.Cfg.UI.RoutingTable}) mux.HandleFunc("/health", handleHealth) mux.Handle("/assets/", http.FileServer(http.FS(ui.Static))) diff --git a/admin/ui/route.go b/admin/ui/route.go index 3faf6b64b..afb7a02cd 100644 --- a/admin/ui/route.go +++ b/admin/ui/route.go @@ -3,13 +3,16 @@ package ui import ( "html/template" "net/http" + + "github.com/fabiolb/fabio/config" ) // RoutesHandler provides the UI for managing the routing table. type RoutesHandler struct { - Color string - Title string - Version string + Color string + Title string + Version string + RoutingTable config.RoutingTable } func (h *RoutesHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { @@ -17,8 +20,7 @@ func (h *RoutesHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { } var tmplRoutes = template.Must(template.New("routes").Parse( // language=HTML - ` - + ` @@ -37,6 +39,21 @@ var tmplRoutes = template.Must(template.New("routes").Parse( // language=HTML @media (min-width: 78em) { td.tags{ display: table-cell; } } + + .tooltip { position: relative; display: inline-block; } + .tooltip .tooltiptext { + visibility: hidden; + width: 250px; + background-color: #000000; + color: #ffffff; + text-align: center; + margin: 30px 0px 0px 30px; + padding: 5px 0; + border-radius: 6px; + position: absolute; + z-index: 1000; + } + .tooltip:hover .tooltiptext { visibility: visible; } @@ -91,15 +108,31 @@ $(function(){ let $tbody = $(''); + console.log(routes); + if (routes != null) { for (let i=0; i < routes.length; i++) { const r = routes[i]; + + let $tr = $(''); + if (/^https?:\/+/i.exec(r.src) != null) { + $tr = $('').attr('style', 'background-color: #ff1a1a;'); + $tr.append($('').addClass('tooltip').append($('').text("Route Source cannot start with the protocol or scheme (e.g. - 'http' and 'https' are invalid to have listed in the route source)")).append($('').append(i+1).append($('error_outline')))); + } else { + $tr.append($('').text(i+1)); + } + $tr.append($('').text(r.service)); - const $tr = $('') + if ({{.RoutingTable.Source.LinkEnabled}} == true && /^https?:\/+/i.exec(r.dst) != null && /^https?:\/+/i.exec(r.src) == null) { + const hrefScheme = ({{.RoutingTable.Source.Scheme}} != '' ? {{.RoutingTable.Source.Scheme}} + ':' : window.location.protocol) + '//'; + const hrefHost = ({{.RoutingTable.Source.Host}} != '' ? {{.RoutingTable.Source.Host}} : window.location.hostname); + const hrefPort = (/:/gi.exec(r.src) != null ? /:[0-9]*\/?/gi.exec(r.src)[0] : '{{if .RoutingTable.Source.Port}}:{{.RoutingTable.Source.Port}}{{end}}'); + const hrefStr = (r.src.startsWith('/') ? hrefScheme + hrefHost + hrefPort : '{{if .RoutingTable.Source.Scheme}}{{.RoutingTable.Source.Scheme}}:{{end}}//') + r.src; + $tr.append($('').append($('').attr('href', hrefStr){{if .RoutingTable.Source.NewTab}}.attr('target', '_blank'){{end}}.text(r.src))); + } else { + $tr.append($('').text(r.src)); + } - $tr.append($('').text(i+1)); - $tr.append($('').text(r.service)); - $tr.append($('').text(r.src)); $tr.append($('').append($('').attr('href', r.dst).text(r.dst))); $tr.append($('').text(r.opts)); $tr.append($('').text((r.weight * 100).toFixed(2) + '%')); @@ -145,7 +178,7 @@ $(function(){ $.each(data, function(idx, val) { let path = val; if (val == "") { - val = "default" + val = "default"; } d.append( $('
  • ').append( diff --git a/config/config.go b/config/config.go index b84c7e18e..484af7293 100644 --- a/config/config.go +++ b/config/config.go @@ -50,11 +50,24 @@ type Listen struct { Refresh time.Duration } +type Source struct { + LinkEnabled bool + NewTab bool + Scheme string + Host string + Port string +} + +type RoutingTable struct { + Source Source +} + type UI struct { - Listen Listen - Color string - Title string - Access string + Listen Listen + Color string + Title string + Access string + RoutingTable RoutingTable } type Proxy struct { diff --git a/config/default.go b/config/default.go index f917f50dd..de9f00594 100644 --- a/config/default.go +++ b/config/default.go @@ -100,6 +100,13 @@ var defaultConfig = &Config{ }, Color: "light-green", Access: "rw", + RoutingTable: RoutingTable{ + Source: Source{ + LinkEnabled: false, + NewTab: true, + Scheme: "http", + }, + }, }, Tracing: Tracing{ diff --git a/config/load.go b/config/load.go index 2c37d5e02..f0e38397a 100644 --- a/config/load.go +++ b/config/load.go @@ -220,6 +220,13 @@ func load(cmdline, environ, envprefix []string, props *properties.Properties) (c f.StringVar(&uiListenerValue, "ui.addr", defaultValues.UIListenerValue, "Address the UI/API is listening on") f.StringVar(&cfg.UI.Color, "ui.color", defaultConfig.UI.Color, "background color of the UI") f.StringVar(&cfg.UI.Title, "ui.title", defaultConfig.UI.Title, "optional title for the UI") + + f.BoolVar(&cfg.UI.RoutingTable.Source.LinkEnabled, "ui.routingtable.source.linkenabled", defaultConfig.UI.RoutingTable.Source.LinkEnabled, "optional true/false flag if the source in the routing table of the admin UI should have a link") + f.BoolVar(&cfg.UI.RoutingTable.Source.NewTab, "ui.routingtable.source.newtab", defaultConfig.UI.RoutingTable.Source.NewTab, "optional true/false flag if the source link should be opened in a new tab, not affected if linkenabled is false") + f.StringVar(&cfg.UI.RoutingTable.Source.Scheme, "ui.routingtable.source.scheme", defaultConfig.UI.RoutingTable.Source.Scheme, "optional protocol scheme for the source link on the routing table in the admin UI, not affected if linkenabled is false") + f.StringVar(&cfg.UI.RoutingTable.Source.Host, "ui.routingtable.source.host", defaultConfig.UI.RoutingTable.Source.Host, "optional host for the source link on the routing table in the admin UI, not affected if linkenabled is false") + f.StringVar(&cfg.UI.RoutingTable.Source.Port, "ui.routingtable.source.port", defaultConfig.UI.RoutingTable.Source.Port, "optional port for the host of the source link on the routing table in the admin UI, not affected if linkenabled is false") + f.StringVar(&cfg.ProfileMode, "profile.mode", defaultConfig.ProfileMode, "enable profiling mode, one of [cpu, mem, mutex, block, trace]") f.StringVar(&cfg.ProfilePath, "profile.path", defaultConfig.ProfilePath, "path to profile dump file") f.BoolVar(&cfg.Tracing.TracingEnabled, "tracing.TracingEnabled", defaultConfig.Tracing.TracingEnabled, "Enable/Disable OpenTrace, one of [true, false]") diff --git a/docs/content/ref/ui.routingtable.source.host.md b/docs/content/ref/ui.routingtable.source.host.md new file mode 100644 index 000000000..7204328eb --- /dev/null +++ b/docs/content/ref/ui.routingtable.source.host.md @@ -0,0 +1,14 @@ +--- +title: "ui.routingtable.source.host" +--- + +`ui.routingtable.source.host` configures an optional host or base address for the link in the source column. + +This is only used when the source is not a separate server (does not begin with '/', e.g. 'dev.google.net'). If source is subdirectory it will set the link for the source to this host. +If this is not set, and the source link is enabled, the link will default to current host. + +This is only applicable if the [linkenabled](/ref/ui.routingtable.source.linkenabled/) is set to true. + +The default is + + ui.routingtable.source.host = diff --git a/docs/content/ref/ui.routingtable.source.linkenabled.md b/docs/content/ref/ui.routingtable.source.linkenabled.md new file mode 100644 index 000000000..a94210ffc --- /dev/null +++ b/docs/content/ref/ui.routingtable.source.linkenabled.md @@ -0,0 +1,9 @@ +--- +title: "ui.routingtable.source.linkenabled" +--- + +`ui.routingtable.source.linkenabled` optionally configures if the routing table's column "source" should be a clickable link. + +The default is + + ui.routingtable.source.linkenabled = false diff --git a/docs/content/ref/ui.routingtable.source.newtab.md b/docs/content/ref/ui.routingtable.source.newtab.md new file mode 100644 index 000000000..ec51edeaf --- /dev/null +++ b/docs/content/ref/ui.routingtable.source.newtab.md @@ -0,0 +1,12 @@ +--- +title: "ui.routingtable.source.newtab" +--- + +`ui.routingtable.source.scheme` configures the scheme protocol for the link of the source on the routing table. This is useful when the scheme is different than the current page +or to force the traffic to a certain protocol. + +This is only applicable if the [linkenabled](/ref/ui.routingtable.source.linkenabled/) is set to true. + +The default is + + ui.routingtable.source.scheme = http \ No newline at end of file diff --git a/docs/content/ref/ui.routingtable.source.port.md b/docs/content/ref/ui.routingtable.source.port.md new file mode 100644 index 000000000..f8e8da8c3 --- /dev/null +++ b/docs/content/ref/ui.routingtable.source.port.md @@ -0,0 +1,13 @@ +--- +title: "ui.routingtable.source.port" +--- + +`ui.routingtable.source.port` configures an optional port for the routing table source column link. This is used in conjunction with the [scheme](/ref/ui.routingtable.source.scheme/) and [host](/ref/ui.routingtable.source.host/). + +If the source is not a separate server (does not begin with '/', e.g. 'dev.google.net'), and the [host](/ref/ui.routingtable.source.host/) is set, this will use the port that is set, or default to the current scheme protocol port (80 for http or 443 for https). + +This is only applicable if the [linkenabled](/ref/ui.routingtable.source.linkenabled/) is set to true. + +The default is + + ui.routingtable.source.port = diff --git a/docs/content/ref/ui.routingtable.source.scheme.md b/docs/content/ref/ui.routingtable.source.scheme.md new file mode 100644 index 000000000..c452b683a --- /dev/null +++ b/docs/content/ref/ui.routingtable.source.scheme.md @@ -0,0 +1,12 @@ +--- +title: "ui.routingtable.source.scheme" +--- + +`ui.routingtable.source.scheme` configures the scheme protocol for the link of the source on the routing table. This is useful when the scheme is different than the current page or to force the traffic to a certain protocol. + + +This is only applicable if the [linkenabled](/ref/ui.routingtable.source.linkenabled/) is set to true. + +The default is + + ui.routingtable.source.scheme = http diff --git a/fabio.properties b/fabio.properties index 8e717f1c7..74a0cb1a2 100644 --- a/fabio.properties +++ b/fabio.properties @@ -1315,6 +1315,61 @@ # ui.title = +# ui.routingtable.source.linkenabled optionally configure if the +# routing table's column "source" should be a clickable link. +# +# The default is +# +# ui.routingtable.source.linkenabled = false + + +# ui.routingtable.source.newtab configures if the source +# link should open in a new tab. +# This is only applicable if the 'linkenabled' is set to true. +# +# The default is +# +# ui.routingtable.source.newtab = true + + +# ui.routingtable.source.scheme configures the scheme protocol +# for the link of the source on the routing table. This is +# useful when the scheme is different than the current page +# or to force the traffic to a certain protocol. +# This is only applicable if the 'linkenabled' is set to true. +# +# The default is +# +# ui.routingtable.source.scheme = http + + +# ui.routingtable.source.host configures an optional host or +# base address for the link in the source column. +# This is only used when the source is not a separate +# server (does not begin with '/', e.g. 'dev.google.net'). If +# source is subdirectory it will set the link for the source to +# this host. If this is not set, and the source link is +# enabled, the link will default to current host. +# This is only applicable if the 'linkenabled' is set to true. +# +# The default is +# +# ui.routingtable.source.host = + + +# ui.routingtable.source.port configures an optional port +# for the routing table source column link. This +# is used in conjunction with the host and scheme. If the +# source is not a separate server (does not begin with '/', +# e.g. 'dev.google.net'), and the host is set, or default to +# the current scheme protocol port (80 for http or 443 for https). +# This is only applicable if the 'linkenabled' is set to true. +# +# The default is +# +# ui.routingtable.source.port = + + # Open Trace Configuration Currently supports ZipKin Collector # tracing.TracingEnabled enables/disables Open Tracing in Fabio. Bool value true/false #