feat: Traefik Pilot WebUI
This commit is contained in:
parent
4a31544024
commit
58bf1a2ca5
16 changed files with 564 additions and 50 deletions
128
webui/package-lock.json
generated
128
webui/package-lock.json
generated
|
@ -7269,6 +7269,11 @@
|
|||
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
|
||||
"dev": true
|
||||
},
|
||||
"iframe-resizer": {
|
||||
"version": "4.2.10",
|
||||
"resolved": "https://registry.npmjs.org/iframe-resizer/-/iframe-resizer-4.2.10.tgz",
|
||||
"integrity": "sha512-9T/AWavGI5Q7nw2ch7qatkKvhK6S11eatuSh0SXpPXN3MV0HtN97KyifWJSuMj47rD6jbqe1CXT91PLQbexvEQ=="
|
||||
},
|
||||
"ignore": {
|
||||
"version": "3.3.10",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
|
||||
|
@ -9003,7 +9008,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -9024,12 +9030,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -9044,17 +9052,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -9171,7 +9182,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -9183,6 +9195,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -9197,6 +9210,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -9204,12 +9218,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -9228,6 +9244,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -9308,7 +9325,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -9320,6 +9338,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -9405,7 +9424,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -9441,6 +9461,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -9460,6 +9481,7 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -9503,12 +9525,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -14418,7 +14442,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -14439,12 +14464,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -14459,17 +14486,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -14586,7 +14616,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -14598,6 +14629,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -14612,6 +14644,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -14619,12 +14652,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -14643,6 +14678,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -14723,7 +14759,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -14735,6 +14772,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -14820,7 +14858,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -14856,6 +14895,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -14875,6 +14915,7 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -14918,12 +14959,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -15465,7 +15508,8 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -15486,12 +15530,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -15506,17 +15552,20 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -15633,7 +15682,8 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -15645,6 +15695,7 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -15659,6 +15710,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -15666,12 +15718,14 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -15690,6 +15744,7 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -15770,7 +15825,8 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -15782,6 +15838,7 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -15867,7 +15924,8 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -15903,6 +15961,7 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -15922,6 +15981,7 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -15965,12 +16025,14 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"bowser": "^2.5.2",
|
||||
"chart.js": "^2.8.0",
|
||||
"dot-prop": "^5.2.0",
|
||||
"iframe-resizer": "^4.2.10",
|
||||
"lodash.isequal": "4.5.0",
|
||||
"moment": "^2.24.0",
|
||||
"quasar": "^1.4.4",
|
||||
|
|
|
@ -118,11 +118,13 @@ module.exports = function (ctx) {
|
|||
env: process.env.APP_ENV === 'development'
|
||||
? { // staging:
|
||||
APP_ENV: JSON.stringify(process.env.APP_ENV),
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api')
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api'),
|
||||
PLATFORM_URL: JSON.stringify(process.env.PLATFORM_URL || 'https://pilot.traefik.io')
|
||||
}
|
||||
: { // production:
|
||||
APP_ENV: JSON.stringify(process.env.APP_ENV),
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api')
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api'),
|
||||
PLATFORM_URL: JSON.stringify(process.env.PLATFORM_URL || 'https://pilot.traefik.io')
|
||||
},
|
||||
uglifyOptions: {
|
||||
compress: {
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
<template>
|
||||
<div id="q-app">
|
||||
<router-view />
|
||||
<platform-panel />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { APP } from './_helpers/APP'
|
||||
import PlatformPanel from './components/platform/PlatformPanel'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
PlatformPanel
|
||||
},
|
||||
beforeCreate () {
|
||||
// Set vue instance
|
||||
APP.vue = () => this.$root
|
||||
|
|
8
webui/src/_directives/resize.js
Normal file
8
webui/src/_directives/resize.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import Vue from 'vue'
|
||||
import iFrameResize from 'iframe-resizer/js/iframeResizer'
|
||||
|
||||
Vue.directive('resize', {
|
||||
bind: function (el, { value = {} }) {
|
||||
el.addEventListener('load', () => iFrameResize(value, el))
|
||||
}
|
||||
})
|
|
@ -1,7 +1,8 @@
|
|||
const APP = {
|
||||
config: {
|
||||
env: process.env.APP_ENV,
|
||||
apiUrl: process.env.APP_API
|
||||
apiUrl: process.env.APP_API,
|
||||
platformUrl: process.env.PLATFORM_URL
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ const Boot = {
|
|||
env () {
|
||||
return APP.config.env
|
||||
},
|
||||
platformUrl () {
|
||||
return APP.config.platformUrl
|
||||
},
|
||||
appThumbStyle () {
|
||||
return {
|
||||
right: '2px',
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<q-toolbar class="row no-wrap items-center">
|
||||
<div class="q-pr-md logo">
|
||||
<img alt="logo" src="~assets/logo.svg">
|
||||
<q-btn v-if="version" type="a" href="https://github.com/containous/traefik/" target="_blank" stretch flat no-caps :label="version" class="btn-menu version" />
|
||||
</div>
|
||||
<q-tabs align="left" inline-label indicator-color="transparent" active-color="white" stretch>
|
||||
<q-route-tab to="/" icon="eva-home-outline" no-caps label="Dashboard" />
|
||||
|
@ -12,10 +13,23 @@
|
|||
<q-route-tab to="/tcp" icon="eva-globe-2-outline" no-caps label="TCP" />
|
||||
<q-route-tab to="/udp" icon="eva-globe-2-outline" no-caps label="UDP" />
|
||||
</q-tabs>
|
||||
<q-space />
|
||||
<q-btn @click="$q.dark.toggle()" stretch flat no-caps icon="invert_colors" :label="`${$q.dark.isActive ? 'Light' : 'Dark'} theme`" class="btn-menu" />
|
||||
<q-btn type="a" :href="`https://docs.traefik.io/${parsedVersion}`" target="_blank" stretch flat no-caps label="Documentation" class="btn-menu" />
|
||||
<q-btn v-if="version" type="a" href="https://github.com/containous/traefik/" target="_blank" stretch flat no-caps :label="`${name} ${version}`" class="btn-menu" />
|
||||
<div class="right-menu">
|
||||
<q-tabs>
|
||||
<q-btn @click="$q.dark.toggle()" stretch flat no-caps icon="invert_colors" :label="`${$q.dark.isActive ? 'Light' : 'Dark'} theme`" class="btn-menu" />
|
||||
<q-btn stretch flat icon="eva-question-mark-circle-outline">
|
||||
<q-menu anchor="bottom left" auto-close>
|
||||
<q-item>
|
||||
<q-btn type="a" :href="`https://docs.traefik.io/${parsedVersion}`" target="_blank" flat color="accent" align="left" icon="eva-book-open-outline" no-caps label="Documentation" class="btn-submenu full-width"/>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
<q-item>
|
||||
<q-btn type="a" href="https://github.com/containous/traefik/" target="_blank" flat color="accent" align="left" icon="eva-github-outline" no-caps label="Github repository" class="btn-submenu full-width"/>
|
||||
</q-item>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</q-tabs>
|
||||
<platform-auth-state />
|
||||
</div>
|
||||
</q-toolbar>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -30,14 +44,12 @@
|
|||
|
||||
<script>
|
||||
import config from '../../../package'
|
||||
import PlatformAuthState from '../platform/PlatformAuthState'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'NavBar',
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
components: { PlatformAuthState },
|
||||
computed: {
|
||||
...mapGetters('core', { coreVersion: 'version' }),
|
||||
version () {
|
||||
|
@ -73,14 +85,29 @@ export default {
|
|||
min-height: 64px;
|
||||
}
|
||||
|
||||
.body--dark .sub-nav {
|
||||
background-color: #0e204c;
|
||||
.body--dark {
|
||||
.sub-nav {
|
||||
background-color: #0e204c;
|
||||
}
|
||||
}
|
||||
|
||||
.q-item--dark {
|
||||
background: var(--q-color-dark);
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.version {
|
||||
min-height: inherit;
|
||||
line-height: inherit;
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,9 +124,25 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.btn-menu{
|
||||
.right-menu {
|
||||
flex: 1;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btn-menu {
|
||||
color: rgba( $app-text-white, .4 );
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.q-item {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-submenu {
|
||||
font-weight: 700;
|
||||
align-items: flex-start;
|
||||
}
|
||||
</style>
|
||||
|
|
71
webui/src/components/_commons/SidePanel.vue
Normal file
71
webui/src/components/_commons/SidePanel.vue
Normal file
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<div class="panel">
|
||||
<div
|
||||
class="panel-backdrop"
|
||||
@click="close"
|
||||
v-if="isOpen"
|
||||
></div>
|
||||
<transition name="slide">
|
||||
<div v-if="isOpen" class="panel-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: ['isOpen'],
|
||||
methods: {
|
||||
close () {
|
||||
this.$emit('onClose')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../css/sass/mixins";
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.slide-enter,
|
||||
.slide-leave-to {
|
||||
transform: translateX(100%);
|
||||
transition: all 150ms ease-in 0s;
|
||||
}
|
||||
|
||||
.panel-backdrop {
|
||||
z-index: 3000;
|
||||
background-color: rgba(255, 255, 255, 0.47);
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
z-index: 9999;
|
||||
overflow-y: auto;
|
||||
background-color: white;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
width: 100vw;
|
||||
border-top-left-radius: 20px;
|
||||
border-bottom-left-radius: 20px;
|
||||
box-shadow: 2px 0 6px 0 #000;
|
||||
|
||||
@include respond-to(md) {
|
||||
width: 80vw;
|
||||
max-width: 1500px;
|
||||
}
|
||||
}
|
||||
</style>
|
20
webui/src/components/platform/PlatformActionButton.vue
Normal file
20
webui/src/components/platform/PlatformActionButton.vue
Normal file
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<q-btn color="accent" class="btn" @click="$emit('click', $event)" :bind="$attrs">{{label}}</q-btn>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'ConnectButton',
|
||||
props: ['label']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
font-family: 'Nunito', 'Roboto', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
text-transform: inherit;
|
||||
}
|
||||
</style>
|
70
webui/src/components/platform/PlatformAuthState.vue
Normal file
70
webui/src/components/platform/PlatformAuthState.vue
Normal file
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<div class="iframe-wrapper" v-if="isOnline">
|
||||
<iframe
|
||||
v-if="renderIrame"
|
||||
id="platform-auth-state"
|
||||
:src="AuthStateIFrameUrl"
|
||||
@load="onAuthStateIFrameLoad"
|
||||
height="64px"
|
||||
frameBorder="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import iFrameResize from 'iframe-resizer/js/iframeResizer'
|
||||
import '../../_directives/resize'
|
||||
|
||||
export default {
|
||||
name: 'PlatformPanel',
|
||||
data () {
|
||||
return {
|
||||
renderIrame: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('platform', { isPlatformOpen: 'isOpen' }),
|
||||
isOnline () {
|
||||
return window.navigator.onLine
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('platform', { openPlatform: 'open' }, { closePlatform: 'close' }),
|
||||
onAuthStateIFrameLoad () {
|
||||
iFrameResize({
|
||||
log: false,
|
||||
resize: true,
|
||||
onMessage: ({ iframe, message }) => {
|
||||
if (typeof message === 'string') {
|
||||
if (message === 'open:profile') {
|
||||
this.openPlatform()
|
||||
}
|
||||
|
||||
if (message === 'logout') {
|
||||
this.closePlatform()
|
||||
}
|
||||
}
|
||||
}
|
||||
}, '#platform-auth-state')
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const authRedirectUrl = `${window.location.href.split('?')[0]}?platform=on`
|
||||
const queryParams = `?authRedirectUrl=${encodeURIComponent(authRedirectUrl)}`
|
||||
|
||||
this.AuthStateIFrameUrl = `${this.platformUrl}/partials/auth-state${queryParams}`
|
||||
},
|
||||
watch: {
|
||||
isPlatformOpen (isOpen, wasOpen) {
|
||||
if (!isOpen && wasOpen) {
|
||||
this.renderIrame = false
|
||||
this.$nextTick().then(() => {
|
||||
this.renderIrame = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
90
webui/src/components/platform/PlatformNotification.vue
Normal file
90
webui/src/components/platform/PlatformNotification.vue
Normal file
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<transition name="slide" v-if="!instanceIsRegistered && isOnline">
|
||||
<section class="app-section">
|
||||
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-lg">
|
||||
<div class="platform-notification">
|
||||
<p>
|
||||
<q-avatar color="accent" text-color="white" class="icon">
|
||||
<q-icon name="eva-alert-circle" />
|
||||
</q-avatar>
|
||||
This Traefik Instance is not registered in your Traefik Pilot account yet.
|
||||
</p>
|
||||
<platform-action-button label="Register Traefik instance" @click="openPlatform" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import PlatformActionButton from './PlatformActionButton'
|
||||
|
||||
export default {
|
||||
name: 'PlatformNotification',
|
||||
components: { PlatformActionButton },
|
||||
created () {
|
||||
this.getInstanceInfos()
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('platform', { isPlatformOpen: 'isOpen' }),
|
||||
...mapGetters('core', { instanceInfos: 'version' }),
|
||||
instanceIsRegistered () {
|
||||
return !!(this.instanceInfos && this.instanceInfos.uuid && this.instanceInfos.uuid.length > 0)
|
||||
},
|
||||
isOnline () {
|
||||
return window.navigator.onLine
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('platform', { openPlatform: 'open' }),
|
||||
...mapActions('core', { getInstanceInfos: 'getVersion' })
|
||||
},
|
||||
watch: {
|
||||
isPlatformOpen (newValue, oldValue) {
|
||||
const isClosed = !newValue && oldValue
|
||||
if (isClosed) {
|
||||
this.getInstanceInfos()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../css/sass/variables";
|
||||
|
||||
.platform-notification {
|
||||
min-height: 100px;
|
||||
padding: 40px 36px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
color: $accent;
|
||||
background-color: rgba($accent, 0.1);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 20px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.slide-enter,
|
||||
.slide-leave-to {
|
||||
transform: translateX(100%);
|
||||
transition: all 150ms ease-in 0s;
|
||||
}
|
||||
</style>
|
106
webui/src/components/platform/PlatformPanel.vue
Normal file
106
webui/src/components/platform/PlatformPanel.vue
Normal file
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<side-panel :isOpen="isPlatformOpen" @onClose="closePlatform()" v-if="isOnline">
|
||||
<div class="iframe-wrapper">
|
||||
<iframe
|
||||
id="platform-iframe"
|
||||
:src="iFrameUrl"
|
||||
style="position: relative; height: 100%; width: 100%;"
|
||||
frameBorder="0"
|
||||
scrolling="yes"
|
||||
@load="onIFrameLoad"
|
||||
/>
|
||||
</div>
|
||||
</side-panel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import iFrameResize from 'iframe-resizer/js/iframeResizer'
|
||||
import SidePanel from '../_commons/SidePanel'
|
||||
import Helps from '../../_helpers/Helps'
|
||||
import '../../_directives/resize'
|
||||
|
||||
export default {
|
||||
name: 'PlatformPanel',
|
||||
components: {
|
||||
SidePanel
|
||||
},
|
||||
async created () {
|
||||
this.getInstanceInfos()
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('platform', { isPlatformOpen: 'isOpen' }),
|
||||
...mapGetters('core', { instanceInfos: 'version' }),
|
||||
iFrameUrl () {
|
||||
const instanceInfos = JSON.stringify(this.instanceInfos)
|
||||
const authRedirectUrl = `${window.location.href.split('?')[0]}?platform=on`
|
||||
const queryParams = `?authRedirectUrl=${encodeURIComponent(authRedirectUrl)}&instanceInfos=${encodeURIComponent(instanceInfos)}`
|
||||
|
||||
return `${this.platformUrl}/instances${queryParams}`
|
||||
},
|
||||
isOnline () {
|
||||
return window.navigator.onLine
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('platform', { openPlatform: 'open', closePlatform: 'close' }),
|
||||
...mapActions('core', { getInstanceInfos: 'getVersion' }),
|
||||
onIFrameLoad () {
|
||||
iFrameResize({
|
||||
log: false,
|
||||
resize: false,
|
||||
scrolling: true,
|
||||
onMessage: ({ iframe, message }) => {
|
||||
if (typeof message === 'string') {
|
||||
switch (message) {
|
||||
case 'open:profile':
|
||||
this.openPlatform()
|
||||
break
|
||||
case 'logout':
|
||||
this.closePlatform()
|
||||
break
|
||||
}
|
||||
} else if (message.type) {
|
||||
switch (message.type) {
|
||||
case 'copy-to-clipboard':
|
||||
navigator.clipboard.writeText(message.value)
|
||||
break
|
||||
}
|
||||
} else if (message.isAuthenticated) {
|
||||
this.isAuthenticated = message.isAuthenticated
|
||||
}
|
||||
}
|
||||
}, '#platform-iframe')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route (to, from) {
|
||||
const wasOpen = from.query && from.query.platform === 'on'
|
||||
const isOpen = to.query && to.query.platform === 'on'
|
||||
|
||||
if (!wasOpen && isOpen) {
|
||||
this.openPlatform()
|
||||
}
|
||||
},
|
||||
isPlatformOpen (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
document.querySelector('body').style.overflow = newValue ? 'hidden' : 'visible'
|
||||
|
||||
this.$router.push({
|
||||
path: this.$route.path,
|
||||
query: Helps.removeEmptyObjects({
|
||||
...this.$route.query,
|
||||
platform: newValue ? 'on' : undefined
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.iframe-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<page-default>
|
||||
<platform-notification />
|
||||
|
||||
<section class="app-section">
|
||||
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-lg">
|
||||
|
@ -150,6 +151,7 @@ import PanelEntry from '../../components/dashboard/PanelEntry'
|
|||
import PanelChart from '../../components/dashboard/PanelChart'
|
||||
import PanelFeature from '../../components/dashboard/PanelFeature'
|
||||
import PanelProvider from '../../components/dashboard/PanelProvider'
|
||||
import PlatformNotification from '../../components/platform/PlatformNotification'
|
||||
|
||||
export default {
|
||||
name: 'PageDashboardIndex',
|
||||
|
@ -159,7 +161,8 @@ export default {
|
|||
PanelEntry,
|
||||
PanelChart,
|
||||
PanelFeature,
|
||||
PanelProvider
|
||||
PanelProvider,
|
||||
PlatformNotification
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
|
|
@ -6,6 +6,7 @@ import entrypoints from './entrypoints'
|
|||
import http from './http'
|
||||
import tcp from './tcp'
|
||||
import udp from './udp'
|
||||
import platform from './platform'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
|
@ -21,7 +22,8 @@ export default function (/* { ssrContext } */) {
|
|||
entrypoints,
|
||||
http,
|
||||
tcp,
|
||||
udp
|
||||
udp,
|
||||
platform
|
||||
},
|
||||
|
||||
// enable strict mode (adds overhead!)
|
||||
|
|
27
webui/src/store/platform/index.js
Normal file
27
webui/src/store/platform/index.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
export default {
|
||||
namespaced: true,
|
||||
getters: {
|
||||
isOpen (state) {
|
||||
return state.isOpen
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
toggle (state, isOpen) {
|
||||
state.isOpen = isOpen || !state.isOpen
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
toggle ({ commit }) {
|
||||
commit('toggle')
|
||||
},
|
||||
open ({ commit }) {
|
||||
commit('toggle', true)
|
||||
},
|
||||
close ({ commit }) {
|
||||
commit('toggle', false)
|
||||
}
|
||||
},
|
||||
state: {
|
||||
isOpen: false
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue