ui: modify content path (#5950)
* Add ui-content-path flag * tests complete, regex validator on string, index.html updated * cleaning up debugging stuff * ui: Enable ember environment configuration to be set via the go binary at runtime (#5934) * ui: Only inject {{.ContentPath}} if we are makeing a prod build... ...otherwise we just use the current rootURL This gets injected into a <base /> node which solves the assets path problem but not the ember problem * ui: Pull out the <base href=""> value and inject it into ember env See previous commit: The <base href=""> value is 'sometimes' injected from go at index serve time. We pass this value down to ember by overwriting the ember config that is injected via a <meta> tag. This has to be done before ember bootup. Sometimes (during testing and development, basically not production) this is injected with the already existing value, in which case this essentially changes nothing. The code here is slightly abstracted away from our specific usage to make it easier for anyone else to use, and also make sure we can cope with using this same method to pass variables down from the CLI through to ember in the future. * ui: We can't use <base /> move everything to javascript (#5941) Unfortuantely we can't seem to be able to use <base> and rootURL together as URL paths will get doubled up (`ui/ui/`). This moves all the things that we need to interpolate with .ContentPath to the `startup` javascript so we can conditionally print out `{{.ContentPath}}` in lots of places (now we can't use base) * fixed when we serve index.html * ui: For writing a ContentPath, we also need to cope with testing... (#5945) ...and potentially more environments Testing has more additional things in a separate index.html in `tests/` This make the entire thing a little saner and uses just javascriopt template literals instead of a pseudo handbrake synatx for our templating of these files. Intead of just templating the entire file this way, we still only template `{{content-for 'head'}}` and `{{content-for 'body'}}` in this way to ensure we support other plugins/addons * build: Loosen up the regex for retrieving the CONSUL_VERSION (#5946) * build: Loosen up the regex for retrieving the CONSUL_VERSION 1. Previously the `sed` replacement was searching for the CONSUL_VERSION comment at the start of a line, it no longer does this to allow for indentation. 2. Both `grep` and `sed` where looking for the omment at the end of the line. We've removed this restriction here. We don't need to remove it right now, but if we ever put the comment followed by something here the searching would break. 3. Added `xargs` for trimming the resulting version string. We aren't using this already in the rest of the scripts, but we are pretty sure this is available on most systems. * ui: Fix erroneous variable, and also force an ember cache clean on build 1. We referenced a variable incorrectly here, this fixes that. 2. We also made sure that every `make` target clears ember's `tmp` cache to ensure that its not using any caches that have since been edited everytime we call a `make` target. * added docs, fixed encoding * fixed go fmt * Update agent/config/config.go Co-Authored-By: R.B. Boyer <public@richardboyer.net> * Completed Suggestions * run gofmt on http.go * fix testsanitize * fix fullconfig/hcl by setting correct 'want' * ran gofmt on agent/config/runtime_test.go * Update website/source/docs/agent/options.html.md Co-Authored-By: Hans Hasselberg <me@hans.io> * Update website/source/docs/agent/options.html.md Co-Authored-By: kaitlincarter-hc <43049322+kaitlincarter-hc@users.noreply.github.com> * remove contentpath from redirectFS struct
This commit is contained in:
parent
1e4a2f009c
commit
e946ed9427
File diff suppressed because one or more lines are too long
|
@ -882,6 +882,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
|
|||
TaggedAddresses: c.TaggedAddresses,
|
||||
TranslateWANAddrs: b.boolVal(c.TranslateWANAddrs),
|
||||
UIDir: b.stringVal(c.UIDir),
|
||||
UIContentPath: UIPathBuilder(b.stringVal(b.Flags.Config.UIContentPath)),
|
||||
UnixSocketGroup: b.stringVal(c.UnixSocket.Group),
|
||||
UnixSocketMode: b.stringVal(c.UnixSocket.Mode),
|
||||
UnixSocketUser: b.stringVal(c.UnixSocket.User),
|
||||
|
@ -907,6 +908,9 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
|
|||
// reDatacenter defines a regexp for a valid datacenter name
|
||||
var reDatacenter = regexp.MustCompile("^[a-z0-9_-]+$")
|
||||
|
||||
// validContentPath defines a regexp for a valid content path name.
|
||||
var validContentPath = regexp.MustCompile(`^[A-Za-z0-9/_-]+$`)
|
||||
var hasVersion = regexp.MustCompile(`^/v\d+/$`)
|
||||
// ----------------------------------------------------------------
|
||||
// check required params we cannot recover from first
|
||||
//
|
||||
|
@ -915,11 +919,20 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
|
|||
return fmt.Errorf("datacenter cannot be empty")
|
||||
}
|
||||
if !reDatacenter.MatchString(rt.Datacenter) {
|
||||
return fmt.Errorf("datacenter cannot be %q. Please use only [a-z0-9-_].", rt.Datacenter)
|
||||
return fmt.Errorf("datacenter cannot be %q. Please use only [a-z0-9-_]", rt.Datacenter)
|
||||
}
|
||||
if rt.DataDir == "" && !rt.DevMode {
|
||||
return fmt.Errorf("data_dir cannot be empty")
|
||||
}
|
||||
|
||||
if !validContentPath.MatchString(rt.UIContentPath) {
|
||||
return fmt.Errorf("ui-content-path can only contain alphanumeric, -, _, or /. received: %s", rt.UIContentPath)
|
||||
}
|
||||
|
||||
if hasVersion.MatchString(rt.UIContentPath) {
|
||||
return fmt.Errorf("ui-content-path cannot have 'v[0-9]'. received: %s", rt.UIContentPath)
|
||||
}
|
||||
|
||||
if !rt.DevMode {
|
||||
fi, err := os.Stat(rt.DataDir)
|
||||
switch {
|
||||
|
@ -976,7 +989,7 @@ func (b *Builder) Validate(rt RuntimeConfig) error {
|
|||
return fmt.Errorf("autopilot.max_trailing_logs cannot be %d. Must be greater than or equal to zero", rt.AutopilotMaxTrailingLogs)
|
||||
}
|
||||
if rt.ACLDatacenter != "" && !reDatacenter.MatchString(rt.ACLDatacenter) {
|
||||
return fmt.Errorf("acl_datacenter cannot be %q. Please use only [a-z0-9-_].", rt.ACLDatacenter)
|
||||
return fmt.Errorf("acl_datacenter cannot be %q. Please use only [a-z0-9-_]", rt.ACLDatacenter)
|
||||
}
|
||||
if rt.EnableUI && rt.UIDir != "" {
|
||||
return fmt.Errorf(
|
||||
|
@ -1672,3 +1685,17 @@ func isUnixAddr(a net.Addr) bool {
|
|||
_, ok := a.(*net.UnixAddr)
|
||||
return ok
|
||||
}
|
||||
|
||||
// UIPathBuilder checks to see if there was a path set
|
||||
// If so, adds beginning and trailing slashes to UI path
|
||||
func UIPathBuilder(UIContentString string) string {
|
||||
if UIContentString != "" {
|
||||
var fmtedPath string
|
||||
fmtedPath = strings.Trim(UIContentString, "/")
|
||||
fmtedPath = "/" + fmtedPath + "/"
|
||||
return fmtedPath
|
||||
|
||||
}
|
||||
return "/ui/"
|
||||
|
||||
}
|
||||
|
|
|
@ -265,6 +265,7 @@ type Config struct {
|
|||
Telemetry Telemetry `json:"telemetry,omitempty" hcl:"telemetry" mapstructure:"telemetry"`
|
||||
TranslateWANAddrs *bool `json:"translate_wan_addrs,omitempty" hcl:"translate_wan_addrs" mapstructure:"translate_wan_addrs"`
|
||||
UI *bool `json:"ui,omitempty" hcl:"ui" mapstructure:"ui"`
|
||||
UIContentPath *string `json:"ui_content_path,omitempty" hcl:"ui_content_path" mapstructure:"ui_content_path"`
|
||||
UIDir *string `json:"ui_dir,omitempty" hcl:"ui_dir" mapstructure:"ui_dir"`
|
||||
UnixSocket UnixSocket `json:"unix_sockets,omitempty" hcl:"unix_sockets" mapstructure:"unix_sockets"`
|
||||
VerifyIncoming *bool `json:"verify_incoming,omitempty" hcl:"verify_incoming" mapstructure:"verify_incoming"`
|
||||
|
|
|
@ -106,6 +106,7 @@ func AddFlags(fs *flag.FlagSet, f *Flags) {
|
|||
add(&f.Config.ServerMode, "server", "Switches agent to server mode.")
|
||||
add(&f.Config.EnableSyslog, "syslog", "Enables logging to syslog.")
|
||||
add(&f.Config.UI, "ui", "Enables the built-in static web UI server.")
|
||||
add(&f.Config.UIContentPath, "ui-content-path", "Sets the external UI path to a string. Defaults to: /ui/ ")
|
||||
add(&f.Config.UIDir, "ui-dir", "Path to directory containing the web UI resources.")
|
||||
add(&f.HCL, "hcl", "hcl config fragment. Can be specified multiple times.")
|
||||
}
|
||||
|
|
|
@ -1387,6 +1387,10 @@ type RuntimeConfig struct {
|
|||
// flag: -ui-dir string
|
||||
UIDir string
|
||||
|
||||
//UIContentPath is a string that sets the external
|
||||
// path to a string. Default: /ui/
|
||||
UIContentPath string
|
||||
|
||||
// UnixSocketGroup contains the group of the file permissions when
|
||||
// Consul binds to UNIX sockets.
|
||||
//
|
||||
|
|
|
@ -743,6 +743,18 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
|
|||
rt.DataDir = dataDir
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "-ui-content-path",
|
||||
args: []string{
|
||||
`-ui-content-path=/a/b`,
|
||||
`-data-dir=` + dataDir,
|
||||
},
|
||||
|
||||
patch: func(rt *RuntimeConfig) {
|
||||
rt.UIContentPath = "/a/b/"
|
||||
rt.DataDir = dataDir
|
||||
},
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// ports and addresses
|
||||
|
@ -4760,6 +4772,7 @@ func TestFullConfig(t *testing.T) {
|
|||
"wan": "78.63.37.19",
|
||||
},
|
||||
TranslateWANAddrs: true,
|
||||
UIContentPath: "/ui/",
|
||||
UIDir: "11IFzAUn",
|
||||
UnixSocketUser: "E0nB1DwA",
|
||||
UnixSocketGroup: "8pFodrV8",
|
||||
|
@ -5369,6 +5382,7 @@ func TestSanitize(t *testing.T) {
|
|||
"StatsiteAddr": ""
|
||||
},
|
||||
"TranslateWANAddrs": false,
|
||||
"UIContentPath": "",
|
||||
"UIDir": "",
|
||||
"UnixSocketGroup": "",
|
||||
"UnixSocketMode": "",
|
||||
|
@ -5709,6 +5723,41 @@ func TestReadPath(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_UIPathBuilder(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
path string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"Letters only string",
|
||||
"hello",
|
||||
"/hello/",
|
||||
},
|
||||
{
|
||||
"Alphanumeric",
|
||||
"Hello1",
|
||||
"/Hello1/",
|
||||
},
|
||||
{
|
||||
"Hyphen and underscore",
|
||||
"-_",
|
||||
"/-_/",
|
||||
},
|
||||
{
|
||||
"Many slashes",
|
||||
"/hello/ui/1/",
|
||||
"/hello/ui/1/",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
actual := UIPathBuilder(tt.path)
|
||||
require.Equal(t, tt.expected, actual)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func splitIPPort(hostport string) (net.IP, int) {
|
||||
h, p, err := net.SplitHostPort(hostport)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/NYTimes/gziphandler"
|
||||
|
@ -83,6 +87,66 @@ type HTTPServer struct {
|
|||
// proto is filled by the agent to "http" or "https".
|
||||
proto string
|
||||
}
|
||||
type templatedFile struct {
|
||||
templated *bytes.Reader
|
||||
name string
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func newTemplatedFile(buf *bytes.Buffer, raw http.File) *templatedFile {
|
||||
info, _ := raw.Stat()
|
||||
return &templatedFile{
|
||||
templated: bytes.NewReader(buf.Bytes()),
|
||||
name: info.Name(),
|
||||
mode: info.Mode(),
|
||||
modTime: info.ModTime(),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *templatedFile) Read(p []byte) (n int, err error) {
|
||||
return t.templated.Read(p)
|
||||
}
|
||||
|
||||
func (t *templatedFile) Seek(offset int64, whence int) (int64, error) {
|
||||
return t.templated.Seek(offset, whence)
|
||||
}
|
||||
|
||||
func (t *templatedFile) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *templatedFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
return nil, errors.New("not a directory")
|
||||
}
|
||||
|
||||
func (t *templatedFile) Stat() (os.FileInfo, error) {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *templatedFile) Name() string {
|
||||
return t.name
|
||||
}
|
||||
|
||||
func (t *templatedFile) Size() int64 {
|
||||
return int64(t.templated.Len())
|
||||
}
|
||||
|
||||
func (t *templatedFile) Mode() os.FileMode {
|
||||
return t.mode
|
||||
}
|
||||
|
||||
func (t *templatedFile) ModTime() time.Time {
|
||||
return t.modTime
|
||||
}
|
||||
|
||||
func (t *templatedFile) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *templatedFile) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
type redirectFS struct {
|
||||
fs http.FileSystem
|
||||
|
@ -96,6 +160,25 @@ func (fs *redirectFS) Open(name string) (http.File, error) {
|
|||
return file, err
|
||||
}
|
||||
|
||||
type templatedIndexFS struct {
|
||||
fs http.FileSystem
|
||||
ContentPath string
|
||||
}
|
||||
|
||||
func (fs *templatedIndexFS) Open(name string) (http.File, error) {
|
||||
file, err := fs.fs.Open(name)
|
||||
if err == nil && name == "/index.html" {
|
||||
content, _ := ioutil.ReadAll(file)
|
||||
file.Seek(0, 0)
|
||||
t, _ := template.New("fmtedindex").Parse(string(content))
|
||||
var out bytes.Buffer
|
||||
err = t.Execute(&out, fs)
|
||||
file = newTemplatedFile(&out, file)
|
||||
}
|
||||
|
||||
return file, err
|
||||
}
|
||||
|
||||
// endpoint is a Consul-specific HTTP handler that takes the usual arguments in
|
||||
// but returns a response object and error, both of which are handled in a
|
||||
// common manner by Consul's HTTP server.
|
||||
|
@ -207,7 +290,6 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
|
|||
|
||||
handleFuncMetrics(pattern, http.HandlerFunc(wrapper))
|
||||
}
|
||||
|
||||
mux.HandleFunc("/", s.Index)
|
||||
for pattern, fn := range endpoints {
|
||||
thisFn := fn
|
||||
|
@ -227,7 +309,6 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
|
|||
|
||||
if s.IsUIEnabled() {
|
||||
var uifs http.FileSystem
|
||||
|
||||
// Use the custom UI dir if provided.
|
||||
if s.agent.config.UIDir != "" {
|
||||
uifs = http.Dir(s.agent.config.UIDir)
|
||||
|
@ -235,10 +316,9 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
|
|||
fs := assetFS()
|
||||
uifs = fs
|
||||
}
|
||||
uifs = &redirectFS{fs: uifs}
|
||||
|
||||
uifs = &redirectFS{fs: &templatedIndexFS{fs: uifs, ContentPath: s.agent.config.UIContentPath}}
|
||||
mux.Handle("/robots.txt", http.FileServer(uifs))
|
||||
mux.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(uifs)))
|
||||
mux.Handle(s.agent.config.UIContentPath, http.StripPrefix(s.agent.config.UIContentPath, http.FileServer(uifs)))
|
||||
}
|
||||
|
||||
// Wrap the whole mux with a handler that bans URLs with non-printable
|
||||
|
@ -489,7 +569,7 @@ func (s *HTTPServer) Index(resp http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
// Redirect to the UI endpoint
|
||||
http.Redirect(resp, req, "/ui/", http.StatusMovedPermanently) // 301
|
||||
http.Redirect(resp, req, s.agent.config.UIContentPath, http.StatusMovedPermanently) // 301
|
||||
}
|
||||
|
||||
// decodeBody is used to decode a JSON request body
|
||||
|
|
|
@ -974,7 +974,7 @@ function ui_version {
|
|||
return 1
|
||||
fi
|
||||
|
||||
local ui_version="$(grep '<!-- CONSUL_VERSION: .* -->$' "$1" | sed 's/^<!-- CONSUL_VERSION: \(.*\) -->$/\1/')" || return 1
|
||||
local ui_version="$(grep '<!-- CONSUL_VERSION: .* -->' "$1" | sed 's/<!-- CONSUL_VERSION: \(.*\) -->/\1/' | xargs)" || return 1
|
||||
echo "$ui_version"
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -2,7 +2,13 @@ ROOT:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
|||
|
||||
all: build
|
||||
|
||||
deps: node_modules
|
||||
deps: node_modules clean
|
||||
|
||||
clean:
|
||||
rm -rf ./tmp
|
||||
|
||||
build-ci: deps
|
||||
yarn run build-ci --output-path=dist
|
||||
|
||||
build: deps
|
||||
yarn run build
|
||||
|
@ -19,6 +25,9 @@ test: deps
|
|||
test-view: deps
|
||||
yarn run test:view
|
||||
|
||||
test-parallel: deps
|
||||
yarn test-parallel
|
||||
|
||||
lint: deps
|
||||
yarn run lint:js
|
||||
|
||||
|
@ -31,4 +40,4 @@ steps:
|
|||
node_modules: yarn.lock package.json
|
||||
yarn install
|
||||
|
||||
.PHONY: all deps build start test test-view lint format
|
||||
.PHONY: all deps build start test test-view lint format clean
|
||||
|
|
|
@ -6,51 +6,12 @@
|
|||
<title>Consul by HashiCorp</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{{content-for "// all HTML content is populated via ./lib/startup/index"}}
|
||||
{{content-for "head"}}
|
||||
<link rel="icon" type="image/png" href="{{rootURL}}assets/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="{{rootURL}}assets/favicon-16x16.png" sizes="16x16">
|
||||
|
||||
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
|
||||
<link integrity="" rel="stylesheet" href="{{rootURL}}assets/consul-ui.css">
|
||||
|
||||
{{content-for "head-footer"}}
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<div style="margin: 0 auto;">
|
||||
<h2>JavaScript Required</h2>
|
||||
<p>Please enable JavaScript in your web browser to use Consul UI.</p>
|
||||
</div>
|
||||
</noscript>
|
||||
{{content-for "body"}}
|
||||
<script src="{{rootURL}}assets/vendor.js"></script>
|
||||
<script>
|
||||
var appendScript = function(src) {
|
||||
var $script = document.createElement('script');
|
||||
$script.src = src;
|
||||
document.body.appendChild($script);
|
||||
}
|
||||
if(!('TextDecoder' in window)) {
|
||||
appendScript('{{rootURL}}assets/encoding-indexes.js');
|
||||
appendScript('{{rootURL}}assets/encoding.js');
|
||||
}
|
||||
</script>
|
||||
<script src="{{rootURL}}assets/consul-ui.js"></script>
|
||||
<script>
|
||||
CodeMirror.modeURL = {
|
||||
replace: function(n, mode) {
|
||||
switch(mode) {
|
||||
case 'javascript':
|
||||
return '{{rootURL}}assets/codemirror/mode/javascript/javascript.js';
|
||||
case 'ruby':
|
||||
return '{{rootURL}}assets/codemirror/mode/ruby/ruby.js';
|
||||
case 'yaml':
|
||||
return '{{rootURL}}assets/codemirror/mode/yaml/yaml.js';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
{{content-for "body-footer"}}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,16 +3,17 @@
|
|||
module.exports = {
|
||||
name: 'startup',
|
||||
contentFor: function(type, config) {
|
||||
const enterprise = config.CONSUL_BINARY_TYPE !== 'oss' && config.CONSUL_BINARY_TYPE !== '';
|
||||
const vars = {
|
||||
appName: config.modulePrefix,
|
||||
environment: config.environment,
|
||||
rootURL: config.environment === 'production' ? '{{.ContentPath}}' : config.rootURL,
|
||||
config: config,
|
||||
};
|
||||
switch (type) {
|
||||
case 'head':
|
||||
return `<!-- CONSUL_VERSION: ${config.CONSUL_VERSION} -->`;
|
||||
return require('./templates/head.html.js')(vars);
|
||||
case 'body':
|
||||
return `<svg width="168" height="53" xmlns="http://www.w3.org/2000/svg"><g fill="#919FA8" fill-rule="evenodd"><path d="M26.078 32.12a5.586 5.586 0 1 1 5.577-5.599 5.577 5.577 0 0 1-5.577 5.6M37.009 29.328a2.56 2.56 0 1 1 2.56-2.56 2.551 2.551 0 0 1-2.56 2.56M46.916 31.669a2.56 2.56 0 1 1 .051-.21c-.028.066-.028.13-.051.21M44.588 25.068a2.565 2.565 0 0 1-2.672-.992 2.558 2.558 0 0 1-.102-2.845 2.564 2.564 0 0 1 4.676.764c.072.328.081.667.027 1a2.463 2.463 0 0 1-1.925 2.073M53.932 31.402a2.547 2.547 0 0 1-2.95 2.076 2.559 2.559 0 0 1-2.064-2.965 2.547 2.547 0 0 1 2.948-2.077 2.57 2.57 0 0 1 2.128 2.716.664.664 0 0 0-.05.228M51.857 25.103a2.56 2.56 0 1 1 2.108-2.945c.034.218.043.439.027.658a2.547 2.547 0 0 1-2.135 2.287M49.954 40.113a2.56 2.56 0 1 1 .314-1.037c-.02.366-.128.721-.314 1.037M48.974 16.893a2.56 2.56 0 1 1 .97-3.487c.264.446.375.965.317 1.479a2.56 2.56 0 0 1-1.287 2.008"/><path d="M26.526 52.603c-14.393 0-26.06-11.567-26.06-25.836C.466 12.498 12.133.931 26.526.931a25.936 25.936 0 0 1 15.836 5.307l-3.167 4.117A20.962 20.962 0 0 0 17.304 8.23C10.194 11.713 5.7 18.9 5.714 26.763c-.014 7.862 4.48 15.05 11.59 18.534a20.962 20.962 0 0 0 21.89-2.127l3.168 4.123a25.981 25.981 0 0 1-15.836 5.31z"/>${
|
||||
enterprise
|
||||
? `<path data-enterprise-logo d="M61 42.083h3.975v.785H61.87v2.136h2.882v.784H61.87v2.31h3.114v.785H61v-6.8zm6.907 1.018V48.9h-.828v-6.817h1.2l2.94 5.84v-5.84h.829V48.9h-1.193L67.907 43.1zm7.826-.225h-2.012v-.784h4.911v.784h-2.02V48.9h-.879v-6.024zm4.564-.793h3.975v.785h-3.106v2.136h2.882v.784h-2.882v2.31h3.114v.785h-3.992l.009-6.8zm8.605 4.347h-1.657v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.657v2.778h1.657c.828 0 1.118-.234 1.118-.901v-.968c.024-.676-.265-.901-1.094-.901l-.024-.008zm4.488-.785h2.485c1.45 0 1.963.635 1.963 1.67v1.009c0 1.05-.505 1.668-1.963 1.668H94.3v2.47h-.87l-.04-6.817zm2.419.785h-1.54v2.803h1.54c.828 0 1.118-.234 1.118-.901v-1.001c0-.668-.282-.893-1.118-.893v-.008zm6.368 3.562h-1.656v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.656v2.778h1.656c.829 0 1.118-.234 1.118-.901v-.968c.017-.676-.265-.901-1.101-.901l-.017-.008zm5.392 6.032h-.828v-6.817h.828V48.9zm4.14.1a5.76 5.76 0 0 1-2.012-.359l.141-.717c.605.195 1.236.3 1.872.308 1.085 0 1.308-.283 1.308-1.06 0-.917 0-1-1.4-1.317-1.656-.368-1.83-.685-1.83-2.095 0-1.184.49-1.76 2.162-1.76a7.648 7.648 0 0 1 1.83.225l-.074.743a8.223 8.223 0 0 0-1.74-.192c-1.11 0-1.308.225-1.308 1.01 0 .942 0 .984 1.342 1.318 1.797.45 1.888.717 1.888 2.044.033 1.176-.315 1.852-2.178 1.852zm4.332-6.917h3.95v.785h-3.105v2.136h2.882v.784h-2.882v2.31H120v.785h-3.992l.033-6.8z" fill-rule="nonzero"/>`
|
||||
: ''
|
||||
}<path d="M61 30.15V17.948c0-4.962 2.845-7.85 9.495-7.85 2.484 0 5.048.326 7.252.895l-.561 4.433c-2.164-.406-4.688-.691-6.53-.691-3.486 0-4.608 1.22-4.608 4.108v10.412c0 2.888 1.122 4.108 4.607 4.108 1.843 0 4.367-.284 6.53-.691l.562 4.433c-2.204.57-4.768.895-7.252.895C63.845 38 61 35.112 61 30.15zm36.808.04c0 4.068-1.802 7.81-8.493 7.81-6.69 0-8.494-3.742-8.494-7.81v-5.002c0-4.067 1.803-7.81 8.494-7.81 6.69 0 8.493 3.743 8.493 7.81v5.003zm-4.887-5.165c0-2.237-1.002-3.416-3.606-3.416s-3.606 1.18-3.606 3.416v5.328c0 2.237 1.002 3.417 3.606 3.417s3.606-1.18 3.606-3.417v-5.328zm25.79 12.568h-4.887V23.764c0-1.057-.44-1.586-1.563-1.586-1.201 0-3.325.732-5.088 1.668v13.747h-4.887V17.785h3.726l.48 1.668c2.444-1.22 5.53-2.074 7.813-2.074 3.245 0 4.407 2.318 4.407 5.857v14.357zm18.26-5.775c0 3.823-1.162 6.182-7.052 6.182-2.083 0-4.927-.488-6.73-1.139l.68-3.782c1.643.488 3.807.854 5.81.854 2.164 0 2.484-.488 2.484-1.993 0-1.22-.24-1.83-3.405-2.603-4.768-1.18-5.329-2.4-5.329-6.223 0-3.986 1.723-5.735 7.292-5.735 1.803 0 4.166.244 5.85.691l-.482 3.945c-1.482-.284-3.846-.569-5.368-.569-2.124 0-2.484.488-2.484 1.708 0 1.587.12 1.709 2.764 2.4 5.449 1.464 5.97 2.196 5.97 6.264zm4.357-14.033h4.887v13.83c0 1.057.441 1.586 1.563 1.586 1.202 0 3.325-.733 5.088-1.668V17.785h4.888v19.808h-3.726l-.481-1.667c-2.444 1.22-5.529 2.074-7.812 2.074-3.246 0-4.407-2.318-4.407-5.857V17.785zM168 37.593h-4.888V9.691L168 9v28.593z"/></g></svg>`;
|
||||
return require('./templates/body.html.js')(vars);
|
||||
case 'root-class':
|
||||
return 'ember-loading';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
module.exports = ({ appName, environment, rootURL, config }) => `
|
||||
<noscript>
|
||||
<div style="margin: 0 auto;">
|
||||
<h2>JavaScript Required</h2>
|
||||
<p>Please enable JavaScript in your web browser to use Consul UI.</p>
|
||||
</div>
|
||||
</noscript>
|
||||
<svg width="168" height="53" xmlns="http://www.w3.org/2000/svg"><g fill="#919FA8" fill-rule="evenodd"><path d="M26.078 32.12a5.586 5.586 0 1 1 5.577-5.599 5.577 5.577 0 0 1-5.577 5.6M37.009 29.328a2.56 2.56 0 1 1 2.56-2.56 2.551 2.551 0 0 1-2.56 2.56M46.916 31.669a2.56 2.56 0 1 1 .051-.21c-.028.066-.028.13-.051.21M44.588 25.068a2.565 2.565 0 0 1-2.672-.992 2.558 2.558 0 0 1-.102-2.845 2.564 2.564 0 0 1 4.676.764c.072.328.081.667.027 1a2.463 2.463 0 0 1-1.925 2.073M53.932 31.402a2.547 2.547 0 0 1-2.95 2.076 2.559 2.559 0 0 1-2.064-2.965 2.547 2.547 0 0 1 2.948-2.077 2.57 2.57 0 0 1 2.128 2.716.664.664 0 0 0-.05.228M51.857 25.103a2.56 2.56 0 1 1 2.108-2.945c.034.218.043.439.027.658a2.547 2.547 0 0 1-2.135 2.287M49.954 40.113a2.56 2.56 0 1 1 .314-1.037c-.02.366-.128.721-.314 1.037M48.974 16.893a2.56 2.56 0 1 1 .97-3.487c.264.446.375.965.317 1.479a2.56 2.56 0 0 1-1.287 2.008"/><path d="M26.526 52.603c-14.393 0-26.06-11.567-26.06-25.836C.466 12.498 12.133.931 26.526.931a25.936 25.936 0 0 1 15.836 5.307l-3.167 4.117A20.962 20.962 0 0 0 17.304 8.23C10.194 11.713 5.7 18.9 5.714 26.763c-.014 7.862 4.48 15.05 11.59 18.534a20.962 20.962 0 0 0 21.89-2.127l3.168 4.123a25.981 25.981 0 0 1-15.836 5.31z"/>${
|
||||
config.CONSUL_BINARY_TYPE !== 'oss' && config.CONSUL_BINARY_TYPE !== ''
|
||||
? `<path data-enterprise-logo d="M61 42.083h3.975v.785H61.87v2.136h2.882v.784H61.87v2.31h3.114v.785H61v-6.8zm6.907 1.018V48.9h-.828v-6.817h1.2l2.94 5.84v-5.84h.829V48.9h-1.193L67.907 43.1zm7.826-.225h-2.012v-.784h4.911v.784h-2.02V48.9h-.879v-6.024zm4.564-.793h3.975v.785h-3.106v2.136h2.882v.784h-2.882v2.31h3.114v.785h-3.992l.009-6.8zm8.605 4.347h-1.657v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.657v2.778h1.657c.828 0 1.118-.234 1.118-.901v-.968c.024-.676-.265-.901-1.094-.901l-.024-.008zm4.488-.785h2.485c1.45 0 1.963.635 1.963 1.67v1.009c0 1.05-.505 1.668-1.963 1.668H94.3v2.47h-.87l-.04-6.817zm2.419.785h-1.54v2.803h1.54c.828 0 1.118-.234 1.118-.901v-1.001c0-.668-.282-.893-1.118-.893v-.008zm6.368 3.562h-1.656v2.503h-.87v-6.85h2.576c1.45 0 1.963.635 1.963 1.67v.984a1.435 1.435 0 0 1-1.077 1.585l1.756 2.57h-1.002l-1.69-2.462zm0-3.562h-1.656v2.778h1.656c.829 0 1.118-.234 1.118-.901v-.968c.017-.676-.265-.901-1.101-.901l-.017-.008zm5.392 6.032h-.828v-6.817h.828V48.9zm4.14.1a5.76 5.76 0 0 1-2.012-.359l.141-.717c.605.195 1.236.3 1.872.308 1.085 0 1.308-.283 1.308-1.06 0-.917 0-1-1.4-1.317-1.656-.368-1.83-.685-1.83-2.095 0-1.184.49-1.76 2.162-1.76a7.648 7.648 0 0 1 1.83.225l-.074.743a8.223 8.223 0 0 0-1.74-.192c-1.11 0-1.308.225-1.308 1.01 0 .942 0 .984 1.342 1.318 1.797.45 1.888.717 1.888 2.044.033 1.176-.315 1.852-2.178 1.852zm4.332-6.917h3.95v.785h-3.105v2.136h2.882v.784h-2.882v2.31H120v.785h-3.992l.033-6.8z" fill-rule="nonzero"/>`
|
||||
: ``
|
||||
}<path d="M61 30.15V17.948c0-4.962 2.845-7.85 9.495-7.85 2.484 0 5.048.326 7.252.895l-.561 4.433c-2.164-.406-4.688-.691-6.53-.691-3.486 0-4.608 1.22-4.608 4.108v10.412c0 2.888 1.122 4.108 4.607 4.108 1.843 0 4.367-.284 6.53-.691l.562 4.433c-2.204.57-4.768.895-7.252.895C63.845 38 61 35.112 61 30.15zm36.808.04c0 4.068-1.802 7.81-8.493 7.81-6.69 0-8.494-3.742-8.494-7.81v-5.002c0-4.067 1.803-7.81 8.494-7.81 6.69 0 8.493 3.743 8.493 7.81v5.003zm-4.887-5.165c0-2.237-1.002-3.416-3.606-3.416s-3.606 1.18-3.606 3.416v5.328c0 2.237 1.002 3.417 3.606 3.417s3.606-1.18 3.606-3.417v-5.328zm25.79 12.568h-4.887V23.764c0-1.057-.44-1.586-1.563-1.586-1.201 0-3.325.732-5.088 1.668v13.747h-4.887V17.785h3.726l.48 1.668c2.444-1.22 5.53-2.074 7.813-2.074 3.245 0 4.407 2.318 4.407 5.857v14.357zm18.26-5.775c0 3.823-1.162 6.182-7.052 6.182-2.083 0-4.927-.488-6.73-1.139l.68-3.782c1.643.488 3.807.854 5.81.854 2.164 0 2.484-.488 2.484-1.993 0-1.22-.24-1.83-3.405-2.603-4.768-1.18-5.329-2.4-5.329-6.223 0-3.986 1.723-5.735 7.292-5.735 1.803 0 4.166.244 5.85.691l-.482 3.945c-1.482-.284-3.846-.569-5.368-.569-2.124 0-2.484.488-2.484 1.708 0 1.587.12 1.709 2.764 2.4 5.449 1.464 5.97 2.196 5.97 6.264zm4.357-14.033h4.887v13.83c0 1.057.441 1.586 1.563 1.586 1.202 0 3.325-.733 5.088-1.668V17.785h4.888v19.808h-3.726l-.481-1.667c-2.444 1.22-5.529 2.074-7.812 2.074-3.246 0-4.407-2.318-4.407-5.857V17.785zM168 37.593h-4.888V9.691L168 9v28.593z"/></g></svg>
|
||||
<script src="${rootURL}assets/vendor.js"></script>
|
||||
${environment === 'test' ? `<script src="${rootURL}assets/test-support.js"></script>` : ``}
|
||||
<script>
|
||||
var appendScript = function(src) {
|
||||
var $script = document.createElement('script');
|
||||
$script.src = src;
|
||||
document.body.appendChild($script);
|
||||
}
|
||||
if(!('TextDecoder' in window)) {
|
||||
appendScript('${rootURL}assets/encoding-indexes.js');
|
||||
appendScript('${rootURL}assets/encoding.js');
|
||||
}
|
||||
</script>
|
||||
<script src="${rootURL}assets/${appName}.js"></script>
|
||||
<script>
|
||||
CodeMirror.modeURL = {
|
||||
replace: function(n, mode) {
|
||||
switch(mode) {
|
||||
case 'javascript':
|
||||
return '${rootURL}assets/codemirror/mode/javascript/javascript.js';
|
||||
case 'ruby':
|
||||
return '${rootURL}assets/codemirror/mode/ruby/ruby.js';
|
||||
case 'yaml':
|
||||
return '${rootURL}assets/codemirror/mode/yaml/yaml.js';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
${environment === 'test' ? `<script src="${rootURL}assets/tests.js"></script>` : ``}
|
||||
`;
|
|
@ -0,0 +1,37 @@
|
|||
module.exports = ({ appName, environment, rootURL, config }) => `
|
||||
<!-- CONSUL_VERSION: ${config.CONSUL_VERSION} -->
|
||||
<script>
|
||||
var setConfig = function(appName, config) {
|
||||
var $meta = document.querySelector('meta[name="' + appName + '/config/environment"]');
|
||||
var defaultConfig = JSON.parse(decodeURIComponent($meta.getAttribute('content')));
|
||||
(
|
||||
function set(blob, config) {
|
||||
Object.keys(config).forEach(
|
||||
function(key) {
|
||||
var value = config[key];
|
||||
if(Object.prototype.toString.call(value) === '[object Object]') {
|
||||
set(blob[key], config[key]);
|
||||
} else {
|
||||
blob[key] = config[key];
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
)(defaultConfig, config);
|
||||
$meta.setAttribute('content', encodeURIComponent(JSON.stringify(defaultConfig)));
|
||||
}
|
||||
setConfig(
|
||||
'${appName}',
|
||||
{
|
||||
rootURL: '${rootURL}'
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<link rel="icon" type="image/png" href="${rootURL}assets/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="${rootURL}assets/favicon-16x16.png" sizes="16x16">
|
||||
<link integrity="" rel="stylesheet" href="${rootURL}assets/vendor.css">
|
||||
<link integrity="" rel="stylesheet" href="${rootURL}assets/${appName}.css">
|
||||
${
|
||||
environment === 'test' ? `<link rel="stylesheet" href="${rootURL}assets/test-support.css">` : ``
|
||||
}
|
||||
`;
|
|
@ -6,14 +6,9 @@
|
|||
<title>ConsulUi Tests</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
{{content-for "head"}}
|
||||
{{content-for "test-head"}}
|
||||
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/consul-ui.css">
|
||||
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
|
||||
|
||||
{{content-for "head-footer"}}
|
||||
{{content-for "test-head-footer"}}
|
||||
</head>
|
||||
|
@ -22,10 +17,6 @@
|
|||
{{content-for "test-body"}}
|
||||
|
||||
<script src="/testem.js" integrity=""></script>
|
||||
<script src="{{rootURL}}assets/vendor.js"></script>
|
||||
<script src="{{rootURL}}assets/test-support.js"></script>
|
||||
<script src="{{rootURL}}assets/consul-ui.js"></script>
|
||||
<script src="{{rootURL}}assets/tests.js"></script>
|
||||
|
||||
{{content-for "body-footer"}}
|
||||
{{content-for "test-body-footer"}}
|
||||
|
|
|
@ -465,6 +465,8 @@ will exit with an error at startup.
|
|||
the Web UI resources for Consul. This will automatically enable the Web UI. The directory must be
|
||||
readable to the agent. Starting with Consul version 0.7.0 and later, the Web UI assets are included in the binary so this flag is no longer necessary; specifying only the `-ui` flag is enough to enable the Web UI. Specifying both the '-ui' and '-ui-dir' flags will result in an error.
|
||||
|
||||
* <a name="_ui_content_path"></a><a href="#_ui_content_path">`-ui-content-path`</a> - This flag provides the option to change the path the Consul UI loads from and will be displayed in the browser. By default, the path is `/ui/`, for example `http://localhost:8500/ui/`. Only alphanumerics, `-`, and `_` are allowed in a custom path. `/v1/` is not allowed as it would overwrite the API endpoint.
|
||||
|
||||
## <a name="configuration_files"></a>Configuration Files
|
||||
|
||||
In addition to the command-line options, configuration can be put into
|
||||
|
|
Loading…
Reference in New Issue