Commit b247a82d by Thomas Bosch Committed by Johannes Edmeier

Add customization for navbar and title

Using spring.boot.admin.ui.title/spring.boot.admin.ui.brand you can customise the page title and logo/title shown in the navbar closes #212
parent d7712b28
...@@ -98,9 +98,6 @@ Spring, Spring Boot and Spring Cloud are trademarks of [Pivotal Software, Inc.]( ...@@ -98,9 +98,6 @@ Spring, Spring Boot and Spring Cloud are trademarks of [Pivotal Software, Inc.](
![Screenshot traces](/images/screenshot-trace.png) ![Screenshot traces](/images/screenshot-trace.png)
*View http request traces* *View http request traces*
![Screenshot hystrix](/images/screenshot-hystrix.png)
*View Hystrix dashboard*
![Screenshot journal](/images/screenshot-journal.png) ![Screenshot journal](/images/screenshot-journal.png)
*View history of registered applications* *View history of registered applications*
......
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
...@@ -38,6 +38,14 @@ ...@@ -38,6 +38,14 @@
| Headers not to be forwarded when making requests to clients. | Headers not to be forwarded when making requests to clients.
| `"Cookie", "Set-Cookie", "Authorization" | `"Cookie", "Set-Cookie", "Authorization"
| spring.boot.admin.ui.brand
| Brand to be shown in then navbar.
| `"<img src="assets/img/icon-spring-boot-admin.svg"><span>Spring Boot Admin</span>"`
| spring.boot.admin.ui.title
| Page-Title to be shown.
| `"Spring Boot Admin"`
|=== |===
include::server-discovery.adoc[] include::server-discovery.adoc[]
......
...@@ -60,19 +60,19 @@ ...@@ -60,19 +60,19 @@
} }
}, },
"@babel/helper-module-imports": { "@babel/helper-module-imports": {
"version": "7.0.0-beta.49", "version": "7.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.49.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.51.tgz",
"integrity": "sha1-QdfVmJEBbEk0MqRvdGREZVKJDHU=", "integrity": "sha1-zgBCgEX7t9XrwOp7+DV4nxU2arI=",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "7.0.0-beta.49", "@babel/types": "7.0.0-beta.51",
"lodash": "^4.17.5" "lodash": "^4.17.5"
}, },
"dependencies": { "dependencies": {
"@babel/types": { "@babel/types": {
"version": "7.0.0-beta.49", "version": "7.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.49.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.51.tgz",
"integrity": "sha1-t+Oxw/TUz+Eb34yJ8e/V4WF7h6Y=", "integrity": "sha1-2AK3tUO1g2x3iqaReXq/APPZfqk=",
"dev": true, "dev": true,
"requires": { "requires": {
"esutils": "^2.0.2", "esutils": "^2.0.2",
...@@ -984,9 +984,9 @@ ...@@ -984,9 +984,9 @@
"dev": true "dev": true
}, },
"babel-plugin-lodash": { "babel-plugin-lodash": {
"version": "3.3.3", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/babel-plugin-lodash/-/babel-plugin-lodash-3.3.3.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz",
"integrity": "sha512-AyUjm1H3jCU3fdL6GHRW6hc8fTNAgZrvs5vk/LY/q6Z2yqpABdXB2JfEQq2dqCrhJkv7eZbUpRUu7hqBNZfZXw==", "integrity": "sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-module-imports": "^7.0.0-beta.49", "@babel/helper-module-imports": "^7.0.0-beta.49",
...@@ -997,9 +997,9 @@ ...@@ -997,9 +997,9 @@
}, },
"dependencies": { "dependencies": {
"@babel/types": { "@babel/types": {
"version": "7.0.0-beta.49", "version": "7.0.0-beta.51",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.49.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.51.tgz",
"integrity": "sha1-t+Oxw/TUz+Eb34yJ8e/V4WF7h6Y=", "integrity": "sha1-2AK3tUO1g2x3iqaReXq/APPZfqk=",
"dev": true, "dev": true,
"requires": { "requires": {
"esutils": "^2.0.2", "esutils": "^2.0.2",
...@@ -13121,9 +13121,9 @@ ...@@ -13121,9 +13121,9 @@
"integrity": "sha512-/ffmsiVuPC8PsWcFkZngdpas19ABm5mh2wA7iDqcltyCTwlgZjHGeJYOXkBMo422iPwIcviOtrTCUpSfXmToLQ==" "integrity": "sha512-/ffmsiVuPC8PsWcFkZngdpas19ABm5mh2wA7iDqcltyCTwlgZjHGeJYOXkBMo422iPwIcviOtrTCUpSfXmToLQ=="
}, },
"vue-clickaway": { "vue-clickaway": {
"version": "2.2.2", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/vue-clickaway/-/vue-clickaway-2.2.2.tgz", "resolved": "https://registry.npmjs.org/vue-clickaway/-/vue-clickaway-2.2.1.tgz",
"integrity": "sha512-25SpjXKetL06GLYoLoC8pqAV6Cur9cQ//2g35GRFBV4FgoljbZZjTINR8g2NuVXXDMLSUXaKx5dutgO4PaDE7A==", "integrity": "sha512-VArdx5/BGgvSU0vBIbhg0NFypL/nvQr77gUripnGHxzxrEfLADBnadhjGgBM+8l9Uj67SJHGfWUhDhmat47F7A==",
"requires": { "requires": {
"loose-envify": "^1.2.0" "loose-envify": "^1.2.0"
} }
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
"babel-eslint": "^8.2.3", "babel-eslint": "^8.2.3",
"babel-jest": "^23.0.1", "babel-jest": "^23.0.1",
"babel-loader": "^7.1.4", "babel-loader": "^7.1.4",
"babel-plugin-lodash": "^3.3.3", "babel-plugin-lodash": "^3.3.4",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0", "babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1", "babel-preset-stage-2": "^6.24.1",
......
...@@ -24,12 +24,17 @@ ...@@ -24,12 +24,17 @@
<meta name="theme-color" content="#42d3a5"> <meta name="theme-color" content="#42d3a5">
<link rel="shortcut icon" href="assets/img/favicon.png" type="image/png"> <link rel="shortcut icon" href="assets/img/favicon.png" type="image/png">
<link href="assets/css/sba-core.css" rel="stylesheet"> <link href="assets/css/sba-core.css" rel="stylesheet">
<title>Spring Boot Admin</title> <title th:text="${uiSettings.title}">Spring Boot Admin</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script th:inline="javascript">
var SBA = {
uiSettings: /*[[${uiSettings}]]*/ {}
}
</script>
<script lang="javascript" src="assets/js/vendors.js" defer></script> <script lang="javascript" src="assets/js/vendors.js" defer></script>
<script lang="javascript" src="assets/js/sba-core.js" defer></script> <script lang="javascript" src="assets/js/sba-core.js" defer></script>
</body> </body>
......
...@@ -61,6 +61,7 @@ new Vue({ ...@@ -61,6 +61,7 @@ new Vue({
render(h) { render(h) {
return h(sbaShell, { return h(sbaShell, {
props: { props: {
views: this.views,
applications: this.applications, applications: this.applications,
error: this.error error: this.error
} }
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<template> <template>
<div class="instances"> <div class="instances">
<sba-instance-header :instance="instance" :application="application" :class="headerClass"/> <sba-instance-header :instance="instance" :application="application" :class="headerClass"/>
<sba-instance-tabs :views="instanceViews" :instance="instance" :application="application" :class="headerClass"/> <sba-instance-tabs :views="views" :instance="instance" :application="application" :class="headerClass"/>
<router-view v-if="instance" :instance="instance"/> <router-view v-if="instance" :instance="instance"/>
</div> </div>
</template> </template>
...@@ -33,6 +33,10 @@ ...@@ -33,6 +33,10 @@
type: String, type: String,
required: true required: true
}, },
views: {
type: Array,
default: () => []
},
applications: { applications: {
type: Array, type: Array,
default: () => [], default: () => [],
...@@ -49,9 +53,6 @@ ...@@ -49,9 +53,6 @@
application() { application() {
return this.applications.findApplicationForInstance(this.instanceId); return this.applications.findApplicationForInstance(this.instanceId);
}, },
instanceViews() {
return this.$root.views.filter(view => view.name.lastIndexOf('instance/') === 0);
},
headerClass() { headerClass() {
if (!this.instance) { if (!this.instance) {
return ''; return '';
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<template> <template>
<div id="app"> <div id="app">
<sba-navbar :views="mainViews" :applications="applications" :error="error"/> <sba-navbar :views="mainViews" :applications="applications" :error="error"/>
<router-view :applications="applications" :error="error"/> <router-view :views="subViews" :applications="applications" :error="error"/>
</div> </div>
</template> </template>
...@@ -26,6 +26,10 @@ ...@@ -26,6 +26,10 @@
export default { export default {
props: { props: {
views: {
type: Array,
default: () => []
},
applications: { applications: {
type: Array, type: Array,
default: () => [], default: () => [],
...@@ -38,7 +42,14 @@ ...@@ -38,7 +42,14 @@
components: {sbaNavbar}, components: {sbaNavbar},
computed: { computed: {
mainViews() { mainViews() {
return this.$root.views.filter(view => view.name.lastIndexOf('/') < 0); return this.views.filter(view => !view.name.includes('/'))
},
activeMainViewName() {
const idx = this.$route.name.indexOf('/');
return idx < 0 ? this.$route.name : this.$route.name.substr(0, idx);
},
subViews() {
return this.views.filter(view => view.name.includes(this.activeMainViewName))
} }
} }
} }
......
...@@ -18,10 +18,7 @@ ...@@ -18,10 +18,7 @@
<nav id="navigation" class="navbar is-fixed-top"> <nav id="navigation" class="navbar is-fixed-top">
<div class="container"> <div class="container">
<div class="navbar-brand"> <div class="navbar-brand">
<router-link class="navbar-item logo" to="/"> <router-link class="navbar-item logo" to="/" v-html="brand"/>
<img src="assets/img/icon-spring-boot-admin.svg">
<span>Spring Boot Admin</span>
</router-link>
<div class="navbar-burger burger" @click.stop="showMenu = !showMenu"> <div class="navbar-burger burger" @click.stop="showMenu = !showMenu">
<span/> <span/>
...@@ -52,7 +49,8 @@ ...@@ -52,7 +49,8 @@
<script> <script>
export default { export default {
data: () => ({ data: () => ({
showMenu: false showMenu: false,
brand: '<img src="assets/img/icon-spring-boot-admin.svg"><span>Spring Boot Admin</span>'
}), }),
props: { props: {
views: { views: {
...@@ -68,6 +66,12 @@ ...@@ -68,6 +66,12 @@
default: null default: null
} }
}, },
created() {
/* global SBA */
if (SBA && SBA.uiSettings) {
this.brand = SBA.uiSettings.brand || this.brand;
}
},
mounted() { mounted() {
document.documentElement.classList.add('has-navbar-fixed-top'); document.documentElement.classList.add('has-navbar-fixed-top');
}, },
......
...@@ -56,7 +56,9 @@ public class AdminServerUiAutoConfiguration { ...@@ -56,7 +56,9 @@ public class AdminServerUiAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public UiController homeUiController() { public UiController homeUiController() {
return new UiController(adminServerProperties.getContextPath()); return new UiController(adminServerProperties.getContextPath(),
uiProperties.getTitle(),
uiProperties.getBrand());
} }
@Bean @Bean
......
...@@ -38,6 +38,16 @@ public class AdminServerUiProperties { ...@@ -38,6 +38,16 @@ public class AdminServerUiProperties {
*/ */
private String templateLocation = CLASSPATH_RESOURCE_LOCATIONS[0]; private String templateLocation = CLASSPATH_RESOURCE_LOCATIONS[0];
/**
* Page-Title to be shown.
*/
private String title = "Spring Boot Admin";
/**
* Brand to be shown in then navbar.
*/
private String brand = "<img src=\"assets/img/icon-spring-boot-admin.svg\"><span>Spring Boot Admin</span>";
private boolean cacheTemplates = true; private boolean cacheTemplates = true;
private final Cache cache = new Cache(); private final Cache cache = new Cache();
...@@ -55,6 +65,7 @@ public class AdminServerUiProperties { ...@@ -55,6 +65,7 @@ public class AdminServerUiProperties {
* include "no-cache" directive in Cache-Control http header. * include "no-cache" directive in Cache-Control http header.
*/ */
private Boolean noCache = false; private Boolean noCache = false;
/** /**
* include "no-store" directive in Cache-Control http header. * include "no-store" directive in Cache-Control http header.
*/ */
...@@ -74,5 +85,4 @@ public class AdminServerUiProperties { ...@@ -74,5 +85,4 @@ public class AdminServerUiProperties {
} }
} }
} }
...@@ -18,6 +18,8 @@ package de.codecentric.boot.admin.server.ui.web; ...@@ -18,6 +18,8 @@ package de.codecentric.boot.admin.server.ui.web;
import de.codecentric.boot.admin.server.web.AdminController; import de.codecentric.boot.admin.server.web.AdminController;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
...@@ -25,9 +27,13 @@ import org.springframework.web.bind.annotation.ModelAttribute; ...@@ -25,9 +27,13 @@ import org.springframework.web.bind.annotation.ModelAttribute;
@AdminController @AdminController
public class UiController { public class UiController {
private final String adminContextPath; private final String adminContextPath;
private final Map<String, Object> uiSettings;
public UiController(String adminContextPath) { public UiController(String adminContextPath, String title, String brand) {
this.adminContextPath = adminContextPath; this.adminContextPath = adminContextPath;
this.uiSettings = new HashMap<>();
this.uiSettings.put("title", title);
this.uiSettings.put("brand", brand);
} }
@ModelAttribute(value = "adminContextPath", binding = false) @ModelAttribute(value = "adminContextPath", binding = false)
...@@ -35,6 +41,11 @@ public class UiController { ...@@ -35,6 +41,11 @@ public class UiController {
return adminContextPath; return adminContextPath;
} }
@ModelAttribute(value = "uiSettings", binding = false)
public Map<String, Object> getUiSettings() {
return uiSettings;
}
@GetMapping(path = "/", produces = MediaType.TEXT_HTML_VALUE) @GetMapping(path = "/", produces = MediaType.TEXT_HTML_VALUE)
public String index() { public String index() {
return "index"; return "index";
...@@ -44,5 +55,4 @@ public class UiController { ...@@ -44,5 +55,4 @@ public class UiController {
public String login() { public String login() {
return "login"; return "login";
} }
} }
...@@ -171,7 +171,6 @@ public class StatusUpdaterTest { ...@@ -171,7 +171,6 @@ public class StatusUpdaterTest {
.thenCancel() .thenCancel()
.verify(); .verify();
StepVerifier.create(repository.find(instance.getId())).assertNext(app -> { StepVerifier.create(repository.find(instance.getId())).assertNext(app -> {
assertThat(app.getStatusInfo().getStatus()).isEqualTo("DOWN"); assertThat(app.getStatusInfo().getStatus()).isEqualTo("DOWN");
assertThat(app.getStatusInfo().getDetails()).containsEntry("status", 503) assertThat(app.getStatusInfo().getDetails()).containsEntry("status", 503)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment