Skip to content

Commit eff73bb

Browse files
SteKoeulischulte
andauthored
feat(#2538): implement configs to hide URLs in UI (#3757)
Co-authored-by: ulrichschulte <[email protected]>
1 parent c410e9c commit eff73bb

File tree

12 files changed

+145
-34
lines changed

12 files changed

+145
-34
lines changed

spring-boot-admin-docs/src/site/asciidoc/customize_ui.adoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,19 @@ You can very simply hide views in the navbar:
216216
----
217217
include::{samples-dir}/spring-boot-admin-sample-servlet/src/main/resources/application.yml[tags=customization-view-settings]
218218
----
219+
220+
== Hide Service URL ==
221+
To hide service URLs in Spring Boot Admin UI entirely, set the following property in your Server's configuration:
222+
223+
|===
224+
| Property name | Default | Usage
225+
226+
| `spring.boot.admin.ui.hide-instance-url`
227+
| `false`
228+
| Set to `true` to hide service URLs as well as actions that require them in UI (e.g. jump to /health or /actuator).
229+
230+
|===
231+
232+
If you want to hide the URL for specific instances oncly, you can set the `hide-url` property in the instance metadata while registering a service.
233+
When using Spring Boot Admin Client you can set the property `spring.boot.admin.client.metadata.hide-url=true` in the corresponding config file.
234+
The value set in `metadata` does not have any effect, when the URLs are disabled in Server.

spring-boot-admin-server-ui/src/main/frontend/global.d.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ declare global {
2020

2121
type UITheme = {
2222
color: string;
23-
palette: {
23+
backgroundEnabled: boolean;
24+
palette?: {
2425
shade50: string;
2526
shade100: string;
2627
shade200: string;
@@ -60,18 +61,18 @@ declare global {
6061
type UISettings = {
6162
title: string;
6263
brand: string;
63-
loginIcon: string;
6464
favicon: string;
6565
faviconDanger: string;
6666
pollTimer: PollTimer;
67-
uiTheme: UITheme;
67+
theme: UITheme;
6868
notificationFilterEnabled: boolean;
6969
rememberMeEnabled: boolean;
7070
availableLanguages: string[];
7171
routes: string[];
7272
externalViews: ExternalView[];
7373
viewSettings: ViewSettings[];
7474
enableToasts: boolean;
75+
hideInstanceUrl: boolean;
7576
};
7677

7778
type SBASettings = {
@@ -81,8 +82,8 @@ declare global {
8182
[key: string]: any;
8283
};
8384
extensions: {
84-
js: Extension[];
85-
css: Extension[];
85+
js?: Extension[];
86+
css?: Extension[];
8687
};
8788
csrf: {
8889
headerName: string;

spring-boot-admin-server-ui/src/main/frontend/sba-config.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,16 @@ import { merge } from 'lodash-es';
1818
const brand =
1919
'<img src="assets/img/icon-spring-boot-admin.svg">Spring Boot Admin';
2020

21-
const DEFAULT_CONFIG = {
21+
const DEFAULT_CONFIG: SBASettings = {
2222
uiSettings: {
23+
title: 'Spring Boot Admin',
2324
brand,
2425
theme: {
2526
backgroundEnabled: true,
2627
color: '#42d3a5',
2728
},
28-
notifications: {
29-
enabled: true,
30-
},
3129
rememberMeEnabled: true,
30+
enableToasts: false,
3231
externalViews: [] as ExternalView[],
3332
favicon: 'assets/img/favicon.png',
3433
faviconDanger: 'assets/img/favicon-danger.png',
@@ -45,9 +44,10 @@ const DEFAULT_CONFIG = {
4544
threads: 2500,
4645
logfile: 1000,
4746
},
47+
hideInstanceUrl: false,
4848
},
4949
user: null,
50-
extensions: [],
50+
extensions: {},
5151
csrf: {
5252
parameterName: '_csrf',
5353
headerName: 'X-XSRF-TOKEN',
@@ -57,10 +57,14 @@ const DEFAULT_CONFIG = {
5757
},
5858
};
5959

60-
const mergedConfig = merge(DEFAULT_CONFIG, window.SBA);
60+
const mergedConfig = merge(DEFAULT_CONFIG, window.SBA) as SBASettings;
6161

6262
export const getCurrentUser = () => {
6363
return mergedConfig.user;
6464
};
6565

6666
export default mergedConfig;
67+
68+
export const useSbaConfig = () => {
69+
return mergedConfig;
70+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, expect, test, vi } from 'vitest';
2+
3+
import Instance from '@/services/instance';
4+
5+
const { useSbaConfig } = vi.hoisted(() => ({
6+
useSbaConfig: vi.fn().mockReturnValue(true),
7+
}));
8+
9+
vi.mock('@/sba-config', async (importOriginal) => ({
10+
...(await importOriginal<typeof import('@/sba-config')>()),
11+
useSbaConfig,
12+
}));
13+
14+
describe('Instance', () => {
15+
test.each`
16+
hideInstanceUrl | metadataHideUrl | expectUrlToBeShownOnUI
17+
${false} | ${'true'} | ${false}
18+
${false} | ${'false'} | ${true}
19+
${false} | ${undefined} | ${true}
20+
${true} | ${'true'} | ${false}
21+
${true} | ${'false'} | ${false}
22+
`(
23+
'showUrl when hideInstanceUrl=$hideInstanceUrl and metadataHideUrl=$metadataHideUrl should return $expectUrlToBeShownOnUI',
24+
({ hideInstanceUrl, metadataHideUrl, expectUrlToBeShownOnUI }) => {
25+
useSbaConfig.mockReturnValue({
26+
uiSettings: {
27+
hideInstanceUrl,
28+
},
29+
});
30+
31+
const instance = new Instance({
32+
id: 'id',
33+
registration: {
34+
metadata: {
35+
['hide-url']: metadataHideUrl,
36+
},
37+
},
38+
});
39+
40+
expect(instance.showUrl()).toBe(expectUrlToBeShownOnUI);
41+
},
42+
);
43+
});

spring-boot-admin-server-ui/src/main/frontend/services/instance.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import waitForPolyfill from '../utils/eventsource-polyfill';
2525
import logtail from '../utils/logtail';
2626
import uri from '../utils/uri';
2727

28+
import { useSbaConfig } from '@/sba-config';
29+
2830
const actuatorMimeTypes = [
2931
'application/vnd.spring-boot.actuator.v2+json',
3032
'application/vnd.spring-boot.actuator.v1+json',
@@ -117,6 +119,16 @@ class Instance {
117119
}));
118120
}
119121

122+
showUrl() {
123+
const sbaConfig = useSbaConfig();
124+
if (sbaConfig.uiSettings.hideInstanceUrl) {
125+
return false;
126+
}
127+
128+
const hideUrlMetadata = this.registration.metadata?.['hide-url'];
129+
return hideUrlMetadata !== 'true';
130+
}
131+
120132
getId() {
121133
return this.id;
122134
}

spring-boot-admin-server-ui/src/main/frontend/shell/navbar.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('Navbar', function () {
3737
});
3838

3939
render(Navbar);
40-
screen.logTestingPlaygroundURL();
40+
4141
expect(screen.getByTestId('usermenu')).toBeVisible();
4242
expect(screen.getByText('[email protected]')).toBeVisible();
4343
});

spring-boot-admin-server-ui/src/main/frontend/tests/setup.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import '@testing-library/jest-dom';
12
import '@testing-library/jest-dom/vitest';
23
import { cleanup } from '@testing-library/vue';
34
import { afterAll, afterEach, beforeAll, vi } from 'vitest';
45

56
import { server } from '@/mocks/server';
7+
import sbaConfig from '@/sba-config';
68

79
global.IntersectionObserver = vi.fn().mockImplementation(() => ({
810
observe: vi.fn(),
@@ -15,6 +17,8 @@ global.ResizeObserver = vi.fn().mockImplementation(() => ({
1517
disconnect: vi.fn(),
1618
}));
1719

20+
global.SBA = sbaConfig;
21+
1822
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
1923
afterAll(() => server.close());
2024
afterEach(() => server.resetHandlers());

spring-boot-admin-server-ui/src/main/frontend/views/applications/InstancesList.vue

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
- Copyright 2014-2018 the original author or authors.
2+
- Copyright 2014-2024 the original author or authors.
33
-
44
- Licensed under the Apache License, Version 2.0 (the "License");
55
- you may not use this file except in compliance with the License.
@@ -30,25 +30,39 @@
3030
/>
3131
</div>
3232
<div class="flex-auto xl:flex-1 xl:w-1/4 truncate">
33-
<a
34-
:href="
35-
instance.registration.serviceUrl || instance.registration.healthUrl
36-
"
37-
@click.stop
38-
v-text="
39-
instance.registration.serviceUrl || instance.registration.healthUrl
40-
"
41-
/>
42-
<sba-tag
43-
v-if="instance.registration.metadata?.['group']"
44-
class="ml-2"
45-
:value="instance.registration.metadata?.['group']"
46-
small
47-
/>
48-
<br />
49-
<span class="text-sm italic" v-text="instance.id" />
33+
<template v-if="instance.showUrl()">
34+
<a
35+
:href="
36+
instance.registration.serviceUrl ||
37+
instance.registration.healthUrl
38+
"
39+
@click.stop
40+
v-text="
41+
instance.registration.serviceUrl ||
42+
instance.registration.healthUrl
43+
"
44+
/>
45+
<sba-tag
46+
v-if="instance.registration.metadata?.['group']"
47+
class="ml-2"
48+
:value="instance.registration.metadata?.['group']"
49+
small
50+
/>
51+
<br />
52+
<span class="text-sm italic" v-text="instance.id" />
53+
</template>
54+
<template v-else>
55+
<span v-text="instance.id"></span>
56+
<sba-tag
57+
v-if="instance.registration.metadata?.['group']"
58+
class="ml-2"
59+
:value="instance.registration.metadata?.['group']"
60+
small
61+
/>
62+
</template>
5063
</div>
5164
<div
65+
v-if="Array.isArray(instance.tags)"
5266
class="hidden xl:block w-1/4"
5367
:class="{
5468
'overflow-x-scroll': Object.keys(instance.tags ?? {}).length > 0,
@@ -71,6 +85,10 @@
7185
import { PropType } from 'vue';
7286
import { useRouter } from 'vue-router';
7387
88+
import SbaStatus from '@/components/sba-status.vue';
89+
import SbaTag from '@/components/sba-tag.vue';
90+
import SbaTags from '@/components/sba-tags.vue';
91+
7492
import Instance from '@/services/instance';
7593
7694
const router = useRouter();

spring-boot-admin-server-ui/src/main/frontend/views/instances/details/details-nav.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<div class="flex-1 text-right">
1111
<sba-button-group>
1212
<sba-button
13+
v-if="instance.showUrl()"
1314
:title="instance.registration.serviceUrl"
1415
class="border-gray-400 ml-1"
1516
@click="openLink(instance.registration.serviceUrl)"
@@ -31,6 +32,7 @@
3132
</sba-button>
3233

3334
<sba-button
35+
v-if="instance.showUrl()"
3436
:title="instance.registration.managementUrl"
3537
class="border-gray-400 ml-1"
3638
@click="openLink(instance.registration.managementUrl)"
@@ -52,6 +54,7 @@
5254
</sba-button>
5355

5456
<sba-button
57+
v-if="instance.showUrl()"
5558
:title="instance.registration.healthUrl"
5659
class="border-gray-400 ml-1"
5760
@click="openLink(instance.registration.healthUrl)"
@@ -79,14 +82,16 @@
7982

8083
<script>
8184
import SbaButtonGroup from '@/components/sba-button-group';
85+
import SbaButton from '@/components/sba-button.vue';
86+
import SbaStickySubnav from '@/components/sba-sticky-subnav.vue';
8287
8388
import Application from '@/services/application';
8489
import Instance from '@/services/instance';
8590
import InstanceSwitcher from '@/views/instances/details/instance-switcher';
8691
8792
export default {
8893
name: 'DetailsNav',
89-
components: { SbaButtonGroup, InstanceSwitcher },
94+
components: { SbaButton, SbaStickySubnav, SbaButtonGroup, InstanceSwitcher },
9095
props: {
9196
application: {
9297
type: Application,

spring-boot-admin-server-ui/src/main/java/de/codecentric/boot/admin/server/ui/config/AdminServerUiAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2023 the original author or authors.
2+
* Copyright 2014-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -109,6 +109,7 @@ public UiController homeUiController(UiExtensions uiExtensions) throws IOExcepti
109109
.favicon(this.adminUi.getFavicon())
110110
.faviconDanger(this.adminUi.getFaviconDanger())
111111
.enableToasts(this.adminUi.getEnableToasts())
112+
.hideInstanceUrl(this.adminUi.getHideInstanceUrl())
112113
.notificationFilterEnabled(
113114
!this.applicationContext.getBeansOfType(NotificationFilterController.class).isEmpty())
114115
.routes(routes)

0 commit comments

Comments
 (0)