Skip to content

Commit 1182051

Browse files
author
Hoang Nguyen
authored
UI: Add multiple management server support (#4885)
* add multiple management server support * display the server on the user menu * remove primary color in server icon * using `/client` from apiBase * add a setting that allows users to customize whether to use multiple servers or not * set default hidden the multiple server config
1 parent 1ccb420 commit 1182051

File tree

9 files changed

+129
-6
lines changed

9 files changed

+129
-6
lines changed

ui/public/config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
{
22
"apiBase": "/client/api",
3+
"servers": [
4+
{
5+
"name": "Local-Server",
6+
"apiHost": "",
7+
"apiBase": "/client/api"
8+
}
9+
],
310
"docBase": "http://docs.cloudstack.apache.org/en/latest",
411
"appTitle": "CloudStack",
512
"footer": "Licensed under the <a href='http://www.apache.org/licenses/' target='_blank'>Apache License</a>, Version 2.0.",
@@ -48,5 +55,6 @@
4855
},
4956
"plugins": [],
5057
"basicZoneEnabled": true,
58+
"multipleServer": false,
5159
"docHelpMappings": {}
5260
}

ui/src/components/header/UserMenu.vue

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
<translation-menu class="action"/>
2222
<header-notice class="action"/>
23+
<label class="user-menu-server-info action" v-if="$config.multipleServer">
24+
<a-icon slot="prefix" type="database" />
25+
{{ server.name || server.apiBase || 'Local-Server' }}
26+
</label>
2327
<a-dropdown>
2428
<span class="user-menu-dropdown action">
2529
<a-avatar class="user-menu-avatar avatar" size="small" :src="avatar()"/>
@@ -59,16 +63,23 @@
5963
</template>
6064

6165
<script>
66+
import Vue from 'vue'
6267
import HeaderNotice from './HeaderNotice'
6368
import TranslationMenu from './TranslationMenu'
6469
import { mapActions, mapGetters } from 'vuex'
70+
import { SERVER_MANAGER } from '@/store/mutation-types'
6571
6672
export default {
6773
name: 'UserMenu',
6874
components: {
6975
TranslationMenu,
7076
HeaderNotice
7177
},
78+
computed: {
79+
server () {
80+
return Vue.ls.get(SERVER_MANAGER) || this.$config.servers[0]
81+
}
82+
},
7283
methods: {
7384
...mapActions(['Logout']),
7485
...mapGetters(['nickname', 'avatar']),
@@ -108,5 +119,11 @@ export default {
108119
min-width: 12px;
109120
margin-right: 8px;
110121
}
122+
123+
&-server-info {
124+
.anticon {
125+
margin-right: 5px;
126+
}
127+
}
111128
}
112129
</style>

ui/src/components/widgets/Console.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<template>
1919
<a
2020
v-if="['vm', 'systemvm', 'router', 'ilbvm'].includes($route.meta.name) && 'updateVirtualMachine' in $store.getters.apis"
21-
:href="'/client/console?cmd=access&vm=' + resource.id"
21+
:href="server + '/console?cmd=access&vm=' + resource.id"
2222
target="_blank">
2323
<a-button style="margin-left: 5px" shape="circle" type="dashed" :size="size" :disabled="['Stopped', 'Error', 'Destroyed'].includes(resource.state)" >
2424
<a-icon type="code" />
@@ -27,6 +27,9 @@
2727
</template>
2828

2929
<script>
30+
import Vue from 'vue'
31+
import { SERVER_MANAGER } from '@/store/mutation-types'
32+
3033
export default {
3134
name: 'Console',
3235
props: {
@@ -38,6 +41,19 @@ export default {
3841
type: String,
3942
default: 'small'
4043
}
44+
},
45+
computed: {
46+
server () {
47+
if (!this.$config.multipleServer) {
48+
return this.$config.apiBase.replace('/api', '')
49+
}
50+
const serverStorage = Vue.ls.get(SERVER_MANAGER)
51+
const apiBase = serverStorage.apiBase.replace('/api', '')
52+
if (!serverStorage.apiHost || serverStorage.apiHost === '/') {
53+
return [location.origin, apiBase].join('')
54+
}
55+
return [serverStorage.apiHost, apiBase].join('')
56+
}
4157
}
4258
}
4359
</script>

ui/src/main.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ Vue.use(toLocaleDatePlugin)
3737

3838
fetch('config.json').then(response => response.json()).then(config => {
3939
Vue.prototype.$config = config
40-
Vue.axios.defaults.baseURL = config.apiBase
40+
let basUrl = config.apiBase
41+
if (config.multipleServer) {
42+
basUrl = (config.servers[0].apiHost || '') + config.servers[0].apiBase
43+
}
44+
45+
Vue.axios.defaults.baseURL = basUrl
4146

4247
loadLanguageAsync().then(() => {
4348
new Vue({

ui/src/permission.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import 'nprogress/nprogress.css' // progress bar style
2626
import message from 'ant-design-vue/es/message'
2727
import notification from 'ant-design-vue/es/notification'
2828
import { setDocumentTitle } from '@/utils/domUtil'
29-
import { ACCESS_TOKEN, APIS } from '@/store/mutation-types'
29+
import { ACCESS_TOKEN, APIS, SERVER_MANAGER } from '@/store/mutation-types'
3030

3131
NProgress.configure({ showSpinner: false }) // NProgress Configuration
3232

@@ -39,6 +39,20 @@ router.beforeEach((to, from, next) => {
3939
const title = i18n.t(to.meta.title) + ' - ' + Vue.prototype.$config.appTitle
4040
setDocumentTitle(title)
4141
}
42+
43+
if (Vue.prototype.$config.multipleServer) {
44+
const servers = Vue.prototype.$config.servers
45+
const serverStorage = Vue.ls.get(SERVER_MANAGER)
46+
let apiFullPath = ''
47+
if (serverStorage) {
48+
apiFullPath = (serverStorage.apiHost || '') + serverStorage.apiBase
49+
}
50+
const serverFilter = servers.filter(ser => (ser.apiHost || '') + ser.apiBase === apiFullPath)
51+
const server = serverFilter[0] || servers[0]
52+
Vue.axios.defaults.baseURL = (server.apiHost || '') + server.apiBase
53+
store.dispatch('SetServer', server)
54+
}
55+
4256
const validLogin = Vue.ls.get(ACCESS_TOKEN) || Cookies.get('userid') || Cookies.get('userid', { path: '/client' })
4357
if (validLogin) {
4458
if (to.path === '/user/login') {

ui/src/store/getters.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const getters = {
3636
zones: state => state.user.zones,
3737
timezoneoffset: state => state.user.timezoneoffset,
3838
usebrowsertimezone: state => state.user.usebrowsertimezone,
39+
server: state => state.app.server,
3940
domainStore: state => state.user.domainStore
4041
}
4142

ui/src/store/modules/app.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import {
2727
DEFAULT_FIXED_HEADER_HIDDEN,
2828
DEFAULT_CONTENT_WIDTH_TYPE,
2929
DEFAULT_MULTI_TAB,
30-
USE_BROWSER_TIMEZONE
30+
USE_BROWSER_TIMEZONE,
31+
SERVER_MANAGER
3132
} from '@/store/mutation-types'
3233

3334
const app = {
@@ -44,7 +45,8 @@ const app = {
4445
color: null,
4546
inverted: true,
4647
multiTab: true,
47-
metrics: false
48+
metrics: false,
49+
server: ''
4850
},
4951
mutations: {
5052
SET_SIDEBAR_TYPE: (state, type) => {
@@ -100,6 +102,10 @@ const app = {
100102
SET_USE_BROWSER_TIMEZONE: (state, bool) => {
101103
Vue.ls.set(USE_BROWSER_TIMEZONE, bool)
102104
state.usebrowsertimezone = bool
105+
},
106+
SET_SERVER: (state, server) => {
107+
Vue.ls.set(SERVER_MANAGER, server)
108+
state.server = server
103109
}
104110
},
105111
actions: {
@@ -147,6 +153,9 @@ const app = {
147153
},
148154
SetUseBrowserTimezone ({ commit }, bool) {
149155
commit('SET_USE_BROWSER_TIMEZONE', bool)
156+
},
157+
SetServer ({ commit }, server) {
158+
commit('SET_SERVER', server)
150159
}
151160
}
152161
}

ui/src/store/mutation-types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const ZONES = 'ZONES'
3232
export const HEADER_NOTICES = 'HEADER_NOTICES'
3333
export const TIMEZONE_OFFSET = 'TIMEZONE_OFFSET'
3434
export const USE_BROWSER_TIMEZONE = 'USE_BROWSER_TIMEZONE'
35+
export const SERVER_MANAGER = 'SERVER_MANAGER'
3536
export const DOMAIN_STORE = 'DOMAIN_STORE'
3637

3738
export const CONTENT_WIDTH_TYPE = {

ui/src/views/auth/Login.vue

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@
3535
<a-icon type="safety" />
3636
{{ $t('label.login.portal') }}
3737
</span>
38+
<a-form-item v-if="$config.multipleServer">
39+
<a-select
40+
size="large"
41+
:placeholder="$t('server')"
42+
v-decorator="[
43+
'server',
44+
{
45+
initialValue: (server.apiHost || '') + server.apiBase
46+
}
47+
]"
48+
@change="onChangeServer">
49+
<a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase">
50+
<a-icon slot="prefix" type="database" :style="{ color: 'rgba(0,0,0,.25)' }"></a-icon>
51+
{{ item.name }}
52+
</a-select-option>
53+
</a-select>
54+
</a-form-item>
3855
<a-form-item>
3956
<a-input
4057
size="large"
@@ -85,6 +102,23 @@
85102
<a-icon type="audit" />
86103
{{ $t('label.login.single.signon') }}
87104
</span>
105+
<a-form-item v-if="$config.multipleServer">
106+
<a-select
107+
size="large"
108+
:placeholder="$t('server')"
109+
v-decorator="[
110+
'server',
111+
{
112+
initialValue: (server.apiHost || '') + server.apiBase
113+
}
114+
]"
115+
@change="onChangeServer">
116+
<a-select-option v-for="item in $config.servers" :key="(item.apiHost || '') + item.apiBase">
117+
<a-icon slot="prefix" type="database" :style="{ color: 'rgba(0,0,0,.25)' }"></a-icon>
118+
{{ item.name }}
119+
</a-select-option>
120+
</a-select>
121+
</a-form-item>
88122
<a-form-item>
89123
<a-select v-decorator="['idp', { initialValue: selectedIdp } ]">
90124
<a-select-option v-for="(idp, idx) in idps" :key="idx" :value="idp.id">
@@ -110,8 +144,11 @@
110144
</template>
111145

112146
<script>
147+
import Vue from 'vue'
113148
import { api } from '@/api'
149+
import store from '@/store'
114150
import { mapActions } from 'vuex'
151+
import { SERVER_MANAGER } from '@/store/mutation-types'
115152
import TranslationMenu from '@/components/header/TranslationMenu'
116153
117154
export default {
@@ -130,10 +167,15 @@ export default {
130167
time: 60,
131168
loginBtn: false,
132169
loginType: 0
133-
}
170+
},
171+
server: ''
134172
}
135173
},
136174
created () {
175+
if (this.$config.multipleServer) {
176+
this.server = Vue.ls.get(SERVER_MANAGER) || this.$config.servers[0]
177+
}
178+
137179
this.fetchData()
138180
},
139181
methods: {
@@ -176,6 +218,11 @@ export default {
176218
177219
validateFields(validateFieldsKey, { force: true }, (err, values) => {
178220
if (!err) {
221+
if (this.$config.multipleServer) {
222+
this.axios.defaults.baseURL = (this.server.apiHost || '') + this.server.apiBase
223+
store.dispatch('SetServer', this.server)
224+
}
225+
179226
if (customActiveKey === 'cs') {
180227
const loginParams = { ...values }
181228
delete loginParams.username
@@ -216,6 +263,11 @@ export default {
216263
} else {
217264
this.$message.error(this.$t('message.login.failed'))
218265
}
266+
},
267+
onChangeServer (server) {
268+
const servers = this.$config.servers || []
269+
const serverFilter = servers.filter(ser => (ser.apiHost || '') + ser.apiBase === server)
270+
this.server = serverFilter[0] || {}
219271
}
220272
}
221273
}

0 commit comments

Comments
 (0)