|
@@ -18,6 +18,7 @@
|
|
|
<title>EdgeVPN</title>
|
|
|
<meta name="description" content="Edgevpn dashboard">
|
|
|
<meta name="keywords" content="edgevpn,dashboard">
|
|
|
+ <script src="/js/apexcharts.min.js"></script>
|
|
|
<script src="/js/alpine-magic-helpers.min.js" defer></script>
|
|
|
<script src="/js/alpine.min.js" defer></script>
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
@@ -1630,7 +1631,7 @@
|
|
|
|
|
|
<div class="w-full px-4 md:px-0 md:mt-8 mb-16 text-gray-800 leading-normal"
|
|
|
x-data="summary()"
|
|
|
- x-init="$interval(updateItems, 1500)"
|
|
|
+ x-init="$interval(updateItems, 1500); initChart()"
|
|
|
>
|
|
|
<!--Summary Content-->
|
|
|
|
|
@@ -1772,7 +1773,81 @@
|
|
|
<!--/Metric Card-->
|
|
|
</div>
|
|
|
|
|
|
- </div>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ <div class="w-full md:w-1/2 xl:w-1/3 p-3">
|
|
|
+ <!--Metric Card-->
|
|
|
+ <div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
|
|
|
+ <div class="flex flex-row items-center">
|
|
|
+ <div class="flex-shrink pr-4">
|
|
|
+ <div class="rounded p-3 bg-cyan-600"><i class="fas fa-download fa-2x fa-fw fa-inverse"></i></div>
|
|
|
+ </div>
|
|
|
+ <div class="flex-1 text-right md:text-center">
|
|
|
+ <h5 class="font-bold uppercase text-gray-400">Total downloaded</h5>
|
|
|
+ <h3 class="font-bold text-3xl text-gray-600" x-text="bytesToSize(metrics.TotalIn)"></h3>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!--/Metric Card-->
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+ <div class="w-full md:w-1/2 xl:w-1/3 p-3 text-gray-800 ">
|
|
|
+ <div class="dark:bg-gray-900 rounded shadow dark:border-gray-600 border-b-4 ">
|
|
|
+ <div class="bg-white-200 dark:bg-gray-900 p-3">
|
|
|
+ <h5 class="font-bold float-left uppercase text-gray-400">
|
|
|
+ <span class="rounded p-1 bg-teal-600"><i class="fa-duotone fa-right-left fa-fw fa-inverse"></i></span>
|
|
|
+ Bandwidth
|
|
|
+ </h5>
|
|
|
+ <h5 class="font-bold uppercase float-right text-gray-600">
|
|
|
+ <span class="rounded p-1 bg-cyan-600"><i class="fas fa-arrow-down fa-fw fa-inverse"></i></span>
|
|
|
+ <span x-text="bytesToSize(metrics.RateIn)"></span>
|
|
|
+ <span class="rounded p-1 bg-amber-600"><i class="fas fa-arrow-up fa-fw fa-inverse"></i></span>
|
|
|
+ <span x-text="bytesToSize(metrics.RateOut)"></span>
|
|
|
+ </h5>
|
|
|
+ </div>
|
|
|
+ <br>
|
|
|
+ <div class=" relative mt-1 ">
|
|
|
+ <!-- Network stat Card-->
|
|
|
+ <div class="dark:bg-gray-900 bg-white-100 rounded shadow p-2">
|
|
|
+ <div class="flex flex-row items-center">
|
|
|
+ <div class="flex-1 text-right md:text-center">
|
|
|
+ <div x-ref="chart"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!--/Network stat Card-->
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ <div class="w-full md:w-1/2 xl:w-1/3 p-3">
|
|
|
+ <!--Metric Card-->
|
|
|
+ <div class="dark:bg-gray-900 bg-white-100 dark:border-gray-800 dark:border-gray-600 border-b-4 rounded shadow p-2">
|
|
|
+ <div class="flex flex-row items-center">
|
|
|
+ <div class="flex-shrink pr-4">
|
|
|
+ <div class="rounded p-3 bg-amber-600"><i class="fas fa-upload fa-2x fa-fw fa-inverse"></i></div>
|
|
|
+ </div>
|
|
|
+ <div class="flex-1 text-right md:text-center">
|
|
|
+ <h5 class="font-bold uppercase text-gray-400">Total uploaded</h5>
|
|
|
+ <h3 class="font-bold text-3xl text-gray-600" x-text="bytesToSize(metrics.TotalOut)"></h3>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!--/Metric Card-->
|
|
|
+ </div>
|
|
|
+
|
|
|
|
|
|
<!--Divider-->
|
|
|
<hr class="border-b-2 border-gray-600 my-8 mx-4">
|
|
@@ -2030,6 +2105,34 @@
|
|
|
</footer>
|
|
|
|
|
|
<script>
|
|
|
+ function bytesToSize(bytes, decimals = 2) {
|
|
|
+ if (bytes === 0) return '0 Bytes';
|
|
|
+
|
|
|
+ const k = 1024;
|
|
|
+ const dm = decimals < 0 ? 0 : decimals;
|
|
|
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
|
+
|
|
|
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
+
|
|
|
+ var s = sizes[i]
|
|
|
+ if (!sizes[i]) {
|
|
|
+ s = "B"
|
|
|
+ }
|
|
|
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + s;
|
|
|
+ }
|
|
|
+
|
|
|
+ function scaleSize(bytes, scale = 1, decimals = 2) {
|
|
|
+ if (bytes === 0) return '0';
|
|
|
+
|
|
|
+ const k = 1024;
|
|
|
+ const dm = decimals < 0 ? 0 : decimals;
|
|
|
+
|
|
|
+ //const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
+ //console.log(i)
|
|
|
+
|
|
|
+ return parseFloat((bytes / Math.pow(k, scale)).toFixed(dm));
|
|
|
+ }
|
|
|
+
|
|
|
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
|
|
|
function calcPages(n, total, size) {
|
|
|
start = 1;
|
|
@@ -2704,10 +2807,83 @@
|
|
|
function summary(){
|
|
|
return {
|
|
|
summary: {},
|
|
|
+ chart: null,
|
|
|
+ metrics: {},
|
|
|
+ initChart() {
|
|
|
+ this.chart = new ApexCharts(this.$refs.chart, {
|
|
|
+ chart: {
|
|
|
+ type: 'area',
|
|
|
+ height: 80,
|
|
|
+ sparkline: {
|
|
|
+ enabled: true
|
|
|
+ },
|
|
|
+ dropShadow: {
|
|
|
+ enabled: true,
|
|
|
+ top: 1,
|
|
|
+ left: 1,
|
|
|
+ blur: 2,
|
|
|
+ opacity: 0.2,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ dataLabels: {
|
|
|
+ enabled: false,
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: "Download",
|
|
|
+ data: [],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "Upload",
|
|
|
+ data: [],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ stroke: {
|
|
|
+ curve: 'smooth'
|
|
|
+ },
|
|
|
+ markers: {
|
|
|
+ size: 0
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ padding: {
|
|
|
+ top: 20,
|
|
|
+ bottom: 10,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ colors: ['#247BA0', '#FF1654' ],
|
|
|
+
|
|
|
+ noData: {
|
|
|
+ text: "Loading...",
|
|
|
+ },
|
|
|
+ xaxis: {
|
|
|
+ labels: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ x: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ y: {
|
|
|
+ formatter: function(value, { series, seriesIndex, dataPointIndex, w }) {
|
|
|
+ return value + ' KB/s'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.chart.render()
|
|
|
+ },
|
|
|
updateItems() {
|
|
|
fetch('/api/summary')
|
|
|
.then(response => response.json())
|
|
|
.then(data => this.summary = data )
|
|
|
+ fetch('/api/metrics')
|
|
|
+ .then(response => response.json())
|
|
|
+ .then(data => {
|
|
|
+ this.metrics = data;
|
|
|
+ this.chart.appendData([{ data: [ scaleSize(data.RateIn) ] }, { data: [ scaleSize(data.RateOut) ] } ]);
|
|
|
+
|
|
|
+ } )
|
|
|
}
|
|
|
};
|
|
|
}
|