feat(webui/dashboard): init new dashboard
This commit is contained in:
parent
8e97af8dc3
commit
4d8dcdc623
8 changed files with 303 additions and 41 deletions
|
@ -10,6 +10,8 @@
|
|||
"test:unit": "vue-cli-service test:unit"
|
||||
},
|
||||
"dependencies": {
|
||||
"bulma": "^0.7.5",
|
||||
"chart.js": "^2.8.0",
|
||||
"core-js": "^2.6.5",
|
||||
"vue": "^2.6.10",
|
||||
"vue-router": "^3.0.3",
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/dashboard">
|
||||
<img
|
||||
src="./assets/images/traefik_logo@3x.svg"
|
||||
alt="Traefik Webui"
|
||||
width="112"
|
||||
height="28"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
|
19
webui/src/assets/images/traefik_logo@3x.svg
Normal file
19
webui/src/assets/images/traefik_logo@3x.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
|
@ -3,6 +3,8 @@ import App from "./App.vue";
|
|||
import router from "./router";
|
||||
import store from "./store";
|
||||
|
||||
import "bulma/css/bulma.min.css";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from "vue";
|
||||
import Router from "vue-router";
|
||||
import WIP from "./views/WIP.vue";
|
||||
import Home from "./views/Home.vue";
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
|
@ -9,7 +9,7 @@ export default new Router({
|
|||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: WIP
|
||||
component: Home
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
228
webui/src/views/Home.vue
Normal file
228
webui/src/views/Home.vue
Normal file
|
@ -0,0 +1,228 @@
|
|||
<template>
|
||||
<main class="home section">
|
||||
<section class="container panel">
|
||||
<p class="panel-heading ">🚧 Work in progress...</p>
|
||||
<div class="panel-block">
|
||||
<div>
|
||||
<p>
|
||||
In the meantime, you can review your current configuration by using
|
||||
the <a href="/api/rawdata">/api/rawdata</a> endpoint.
|
||||
</p>
|
||||
<p>
|
||||
Also, please keep your <i class="fa fa-eye" /> on our
|
||||
<a href="https://docs.traefik.io/v2.0/operations/dashboard/"
|
||||
>documentation</a
|
||||
>
|
||||
to stay informed
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container panel" v-if="entrypoints.length">
|
||||
<p class="panel-heading ">Entrypoints</p>
|
||||
<div class="panel-block">
|
||||
<nav class="level" :style="{ flex: '1 1' }">
|
||||
<div
|
||||
class="level-item has-text-centered"
|
||||
v-for="entrypoint in entrypoints"
|
||||
:key="entrypoint.name"
|
||||
>
|
||||
<div>
|
||||
<p class="heading">{{ entrypoint.name }}</p>
|
||||
<p class="title">{{ entrypoint.address }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container" v-if="overview.http">
|
||||
<p class="title is-4">HTTP</p>
|
||||
<div class="tile is-child box columns is-height-limited">
|
||||
<div class="column is-4">
|
||||
<canvas id="http-routers" />
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<canvas id="http-middlewares" />
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<canvas id="http-services" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container" v-if="overview.tcp">
|
||||
<p class="title is-4">TCP</p>
|
||||
<div class="tile is-child box columns is-height-limited">
|
||||
<div class="column is-4">
|
||||
<canvas id="tcp-routers" />
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<canvas id="tcp-services" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container panel">
|
||||
<p class="panel-heading">Features</p>
|
||||
<div class="panel-block">
|
||||
<div class="tile is-ancestor">
|
||||
<div
|
||||
class="tile is-parent"
|
||||
v-for="(feature, key) of overview.features"
|
||||
:key="key"
|
||||
>
|
||||
<div
|
||||
class="tile is-child notification"
|
||||
:class="{ 'is-success': feature, 'is-danger': !feature }"
|
||||
>
|
||||
<p class="title">{{ key }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Chart from "chart.js";
|
||||
|
||||
Chart.plugins.register({
|
||||
afterDraw: function(chart) {
|
||||
if (chart.data.datasets[0].data.reduce((acc, it) => acc + it, 0) === 0) {
|
||||
var ctx = chart.chart.ctx;
|
||||
var width = chart.chart.width;
|
||||
var height = chart.chart.height
|
||||
chart.clear();
|
||||
|
||||
ctx.save();
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.font = "16px normal 'Helvetica Nueue'";
|
||||
ctx.fillText(`No ${chart.options.title.text}`, width / 2, height / 2);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "home",
|
||||
data: () => ({
|
||||
entrypoints: [],
|
||||
overview: {
|
||||
features: []
|
||||
},
|
||||
charts: {
|
||||
http: {
|
||||
routers: null,
|
||||
middlewares: null,
|
||||
services: null
|
||||
},
|
||||
tcp: {
|
||||
routers: null,
|
||||
services: null
|
||||
}
|
||||
},
|
||||
interval: null
|
||||
}),
|
||||
methods: {
|
||||
buildDoughnutChart(
|
||||
selector,
|
||||
entity = { errors: 2, warnings: 2, total: 6 },
|
||||
name
|
||||
) {
|
||||
return new Chart(this.$el.querySelector(selector), {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
entity.errors,
|
||||
entity.warnings,
|
||||
entity.total - (entity.errors + entity.warnings)
|
||||
],
|
||||
backgroundColor: [
|
||||
"hsl(348, 100%, 61%)",
|
||||
"hsl(48, 100%, 67%)",
|
||||
"hsl(141, 71%, 48%)"
|
||||
]
|
||||
}
|
||||
],
|
||||
labels: ["errors", "warnings", "success"]
|
||||
},
|
||||
options: {
|
||||
title: {
|
||||
display: true,
|
||||
text: name
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchOverview() {
|
||||
return fetch("/api/overview")
|
||||
.then(response => response.json())
|
||||
.then(response => (this.overview = response))
|
||||
.then(() => {
|
||||
this.charts = {
|
||||
http: {
|
||||
routers: this.buildDoughnutChart(
|
||||
"#http-routers",
|
||||
this.overview.http.routers,
|
||||
"Routers"
|
||||
),
|
||||
middlewares: this.buildDoughnutChart(
|
||||
"#http-middlewares",
|
||||
this.overview.http.middlewares,
|
||||
"Middlewares"
|
||||
),
|
||||
services: this.buildDoughnutChart(
|
||||
"#http-services",
|
||||
this.overview.http.services,
|
||||
"Services"
|
||||
)
|
||||
},
|
||||
tcp: {
|
||||
routers: this.buildDoughnutChart(
|
||||
"#tcp-routers",
|
||||
this.overview.tcp.routers,
|
||||
"Routers"
|
||||
),
|
||||
services: this.buildDoughnutChart(
|
||||
"#tcp-services",
|
||||
this.overview.tcp.services,
|
||||
"Services"
|
||||
)
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
fetchEntrypoints() {
|
||||
return fetch("/api/entrypoints")
|
||||
.then(response => response.json())
|
||||
.then(response => (this.entrypoints = response));
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.fetchOverview();
|
||||
await this.fetchEntrypoints();
|
||||
this.interval = setInterval(() => {
|
||||
this.fetchOverview();
|
||||
this.fetchEntrypoints();
|
||||
}, 60000);
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.home section {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
</style>
|
|
@ -1,39 +0,0 @@
|
|||
<template>
|
||||
<main class="wip">
|
||||
<img src="../assets/images/traefik.logo.svg" alt="logo" />
|
||||
<header>
|
||||
<h1 class="title">
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
Work in progress...
|
||||
</h1>
|
||||
<p>
|
||||
In the meantime, you can review your current configuration by using the
|
||||
<a href="/api/rawdata">/api/rawdata</a> endpoint <br /><br />
|
||||
Also, please keep your <i class="fa fa-eye"></i> on our
|
||||
<a href="https://docs.traefik.io/v2.0/operations/dashboard/"
|
||||
>documentation</a
|
||||
>
|
||||
to stay informed
|
||||
</p>
|
||||
<p></p>
|
||||
</header>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "home"
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.wip
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: center
|
||||
justify-content: center
|
||||
height: 100vh
|
||||
|
||||
.title
|
||||
font-size: 4em
|
||||
</style>
|
|
@ -2058,6 +2058,11 @@ builtin-status-codes@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
|
||||
|
||||
bulma@^0.7.5:
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.7.5.tgz#35066c37f82c088b68f94450be758fc00a967208"
|
||||
integrity sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw==
|
||||
|
||||
bytes@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||
|
@ -2272,6 +2277,29 @@ chardet@^0.7.0:
|
|||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
chart.js@^2.8.0:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.8.0.tgz#b703b10d0f4ec5079eaefdcd6ca32dc8f826e0e9"
|
||||
integrity sha512-Di3wUL4BFvqI5FB5K26aQ+hvWh8wnP9A3DWGvXHVkO13D3DSnaSsdZx29cXlEsYKVkn1E2az+ZYFS4t0zi8x0w==
|
||||
dependencies:
|
||||
chartjs-color "^2.1.0"
|
||||
moment "^2.10.2"
|
||||
|
||||
chartjs-color-string@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
|
||||
integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
|
||||
chartjs-color@^2.1.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.3.0.tgz#0e7e1e8dba37eae8415fd3db38bf572007dd958f"
|
||||
integrity sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g==
|
||||
dependencies:
|
||||
chartjs-color-string "^0.6.0"
|
||||
color-convert "^0.5.3"
|
||||
|
||||
check-types@^8.0.3:
|
||||
version "8.0.3"
|
||||
resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
|
||||
|
@ -2453,6 +2481,11 @@ collection-visit@^1.0.0:
|
|||
map-visit "^1.0.0"
|
||||
object-visit "^1.0.0"
|
||||
|
||||
color-convert@^0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
|
||||
integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=
|
||||
|
||||
color-convert@^1.9.0, color-convert@^1.9.1:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
|
@ -6513,6 +6546,11 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
|
|||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
moment@^2.10.2:
|
||||
version "2.24.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
|
||||
|
||||
move-concurrently@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||
|
|
Loading…
Reference in a new issue