mirror of
https://github.com/WeeJeWel/wg-easy.git
synced 2024-12-22 17:19:23 +08:00
add charts
This commit is contained in:
parent
9a4b0bff2e
commit
7cd677f7ef
@ -5,5 +5,5 @@ services:
|
|||||||
command: npm run serve
|
command: npm run serve
|
||||||
volumes:
|
volumes:
|
||||||
- ./src/:/app/
|
- ./src/:/app/
|
||||||
environment:
|
# environment:
|
||||||
- PASSWORD=p
|
# - PASSWORD=p
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"consistent-return": "off",
|
"consistent-return": "off",
|
||||||
"no-shadow": "off"
|
"no-shadow": "off",
|
||||||
|
"max-len": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>WireGuard</title>
|
<title>WireGuard</title>
|
||||||
<link href="css/vendor/tailwind.min.css" rel="stylesheet">
|
<link href="/css/vendor/tailwind.min.css" rel="stylesheet">
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<link rel="icon" type="image/png" href="img/favicon.png">
|
<link rel="icon" type="image/png" href="img/favicon.png">
|
||||||
<link rel="apple-touch-icon" href="img/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="img/apple-touch-icon.png">
|
||||||
@ -67,67 +67,54 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
<!-- Client -->
|
||||||
<div v-if="clients && clients.length > 0" v-for="client in clients" :key="client.id"
|
<div v-if="clients && clients.length > 0" v-for="client in clients" :key="client.id"
|
||||||
class="p-5 flex flex-row">
|
class="relative overflow-hidden border-b border-gray-100 border-solid">
|
||||||
<div class="h-10 w-10 mr-5 rounded-full bg-gray-50 relative">
|
|
||||||
<svg class="w-6 m-2 text-gray-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
<img v-if="client.avatar" :src="client.avatar" class="w-10 rounded-full absolute top-0 left-0" />
|
|
||||||
|
|
||||||
<div
|
<!-- Chart -->
|
||||||
v-if="client.latestHandshakeAt && ((new Date() - new Date(client.latestHandshakeAt) < 1000 * 60 * 10))">
|
<div class="absolute z-0 bottom-0 left-0 right-0" style="top: 60%;">
|
||||||
<div class="animate-ping w-4 h-4 p-1 bg-red-100 rounded-full absolute -bottom-1 -right-1"></div>
|
<apexchart width="100%" height="100%" :options="client.chartOptions" :series="client.transferTxSeries">
|
||||||
<div class="w-2 h-2 bg-red-800 rounded-full absolute bottom-0 right-0"></div>
|
</apexchart>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute z-0 top-0 left-0 right-0" style="bottom: 60%;">
|
||||||
|
<apexchart width="100%" height="100%" :options="client.chartOptions" :series="client.transferRxSeries"
|
||||||
|
style="transform: scaleY(-1);">
|
||||||
|
</apexchart>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-grow">
|
<div class="relative p-5 z-10 flex flex-row">
|
||||||
<!-- Name -->
|
<div class="h-10 w-10 mr-5 rounded-full bg-gray-50 relative">
|
||||||
<div class="text-gray-700 group" :title="'Created at ' + dateTime(new Date(client.createdAt))">
|
<svg class="w-6 m-2 text-gray-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||||
|
fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<img v-if="client.avatar" :src="client.avatar" class="w-10 rounded-full absolute top-0 left-0" />
|
||||||
|
|
||||||
<!-- Show -->
|
<div
|
||||||
<input v-show="clientEditNameId === client.id" v-model="clientEditName"
|
v-if="client.latestHandshakeAt && ((new Date() - new Date(client.latestHandshakeAt) < 1000 * 60 * 10))">
|
||||||
v-on:keyup.enter="updateClientName(client, clientEditName); clientEditName = null; clientEditNameId = null;"
|
<div class="animate-ping w-4 h-4 p-1 bg-red-100 rounded-full absolute -bottom-1 -right-1"></div>
|
||||||
v-on:keyup.escape="clientEditName = null; clientEditNameId = null;"
|
<div class="w-2 h-2 bg-red-800 rounded-full absolute bottom-0 right-0"></div>
|
||||||
:ref="'client-' + client.id + '-name'"
|
</div>
|
||||||
class="rounded px-1 border-2 border-gray-100 focus:border-gray-200 outline-none w-30" />
|
|
||||||
<span v-show="clientEditNameId !== client.id"
|
|
||||||
class="inline-block border-t-2 border-b-2 border-white">{{client.name}}</span>
|
|
||||||
|
|
||||||
<!-- Edit -->
|
|
||||||
<span v-show="clientEditNameId !== client.id"
|
|
||||||
@click="clientEditName = client.name; clientEditNameId = client.id; setTimeout(() => $refs['client-' + client.id + '-name'][0].select(), 1);"
|
|
||||||
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Info -->
|
<div class="flex-grow">
|
||||||
<div class="text-gray-300 text-xs">
|
|
||||||
|
|
||||||
<!-- Address -->
|
<!-- Name -->
|
||||||
<span class="group">
|
<div class="text-gray-700 group" :title="'Created at ' + dateTime(new Date(client.createdAt))">
|
||||||
|
|
||||||
<!-- Show -->
|
<!-- Show -->
|
||||||
<input v-show="clientEditAddressId === client.id" v-model="clientEditAddress"
|
<input v-show="clientEditNameId === client.id" v-model="clientEditName"
|
||||||
v-on:keyup.enter="updateClientAddress(client, clientEditAddress); clientEditAddress = null; clientEditAddressId = null;"
|
v-on:keyup.enter="updateClientName(client, clientEditName); clientEditName = null; clientEditNameId = null;"
|
||||||
v-on:keyup.escape="clientEditAddress = null; clientEditAddressId = null;"
|
v-on:keyup.escape="clientEditName = null; clientEditNameId = null;"
|
||||||
:ref="'client-' + client.id + '-address'"
|
:ref="'client-' + client.id + '-name'"
|
||||||
class="rounded border-2 border-gray-100 focus:border-gray-200 outline-none w-20 text-black" />
|
class="rounded px-1 border-2 border-gray-100 focus:border-gray-200 outline-none w-30" />
|
||||||
<span v-show="clientEditAddressId !== client.id"
|
<span v-show="clientEditNameId !== client.id"
|
||||||
class="inline-block border-t-2 border-b-2 border-white">{{client.address}}</span>
|
class="inline-block border-t-2 border-b-2 border-transparent">{{client.name}}</span>
|
||||||
|
|
||||||
<!-- Edit -->
|
<!-- Edit -->
|
||||||
<span v-show="clientEditAddressId !== client.id"
|
<span v-show="clientEditNameId !== client.id"
|
||||||
@click="clientEditAddress = client.address; clientEditAddressId = client.id; setTimeout(() => $refs['client-' + client.id + '-address'][0].select(), 1);"
|
@click="clientEditName = client.name; clientEditNameId = client.id; setTimeout(() => $refs['client-' + client.id + '-name'][0].select(), 1);"
|
||||||
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
|
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" fill="none" viewBox="0 0 24 24"
|
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" fill="none" viewBox="0 0 24 24"
|
||||||
@ -136,84 +123,114 @@
|
|||||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- Transfer TX -->
|
|
||||||
<span v-if="client.transferTx" title="Download">
|
|
||||||
·
|
|
||||||
<svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path fill-rule="evenodd"
|
|
||||||
d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
{{client.transferTx | bytes}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- Transfer RX -->
|
|
||||||
<span v-if="client.transferRx" title="Upload">
|
|
||||||
·
|
|
||||||
<svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path fill-rule="evenodd"
|
|
||||||
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
{{client.transferRx | bytes}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- Last seen -->
|
|
||||||
<span v-if="client.latestHandshakeAt"
|
|
||||||
:title="'Last seen at ' + dateTime(new Date(client.latestHandshakeAt))">
|
|
||||||
· {{new Date(client.latestHandshakeAt) | timeago}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-right">
|
|
||||||
<div class="text-gray-400">
|
|
||||||
|
|
||||||
<!-- Enable/Disable -->
|
|
||||||
<div @click="disableClient(client)" v-if="client.enabled === true"
|
|
||||||
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-red-800 cursor-pointer hover:bg-red-700 transition-all">
|
|
||||||
<div class="rounded-full w-4 h-4 m-1 ml-5 bg-white"></div>
|
|
||||||
</div>
|
|
||||||
<div @click="enableClient(client)" v-if="client.enabled === false"
|
|
||||||
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-gray-200 cursor-pointer hover:bg-gray-300 transition-all">
|
|
||||||
<div class="rounded-full w-4 h-4 m-1 bg-white"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Show QR-->
|
<!-- Info -->
|
||||||
<button class="align-middle bg-gray-100 hover:bg-red-800 hover:text-white p-2 rounded transition"
|
<div class="text-gray-400 text-xs">
|
||||||
title="Show QR Code" @click="qrcode = `/api/wireguard/client/${client.id}/qrcode.svg`">
|
|
||||||
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Download Config -->
|
<!-- Address -->
|
||||||
<a :href="'/api/wireguard/client/' + client.id + '/configuration'" download
|
<span class="group">
|
||||||
class="align-middle inline-block bg-gray-100 hover:bg-red-800 hover:text-white p-2 rounded transition"
|
|
||||||
title="Download Configuration">
|
|
||||||
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
||||||
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<!-- Delete -->
|
<!-- Show -->
|
||||||
<button class="align-middle bg-gray-100 hover:bg-red-800 hover:text-white p-2 rounded transition"
|
<input v-show="clientEditAddressId === client.id" v-model="clientEditAddress"
|
||||||
title="Delete Client" @click="clientDelete = client">
|
v-on:keyup.enter="updateClientAddress(client, clientEditAddress); clientEditAddress = null; clientEditAddressId = null;"
|
||||||
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
v-on:keyup.escape="clientEditAddress = null; clientEditAddressId = null;"
|
||||||
<path fill-rule="evenodd"
|
:ref="'client-' + client.id + '-address'"
|
||||||
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
class="rounded border-2 border-gray-100 focus:border-gray-200 outline-none w-20 text-black" />
|
||||||
clip-rule="evenodd" />
|
<span v-show="clientEditAddressId !== client.id"
|
||||||
</svg>
|
class="inline-block border-t-2 border-b-2 border-transparent">{{client.address}}</span>
|
||||||
</button>
|
|
||||||
|
<!-- Edit -->
|
||||||
|
<span v-show="clientEditAddressId !== client.id"
|
||||||
|
@click="clientEditAddress = client.address; clientEditAddressId = client.id; setTimeout(() => $refs['client-' + client.id + '-address'][0].select(), 1);"
|
||||||
|
class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4 inline align-middle opacity-25 hover:opacity-100" fill="none"
|
||||||
|
viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Transfer TX -->
|
||||||
|
<span v-if="client.transferTx" title="Download">
|
||||||
|
·
|
||||||
|
<svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||||
|
fill="currentColor">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
{{client.transferTxCurrent | bytes}}/s
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Transfer RX -->
|
||||||
|
<span v-if="client.transferRx" title="Upload">
|
||||||
|
·
|
||||||
|
<svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
|
||||||
|
fill="currentColor">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
{{client.transferRxCurrent | bytes}}/s
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- Last seen -->
|
||||||
|
<span v-if="client.latestHandshakeAt"
|
||||||
|
:title="'Last seen at ' + dateTime(new Date(client.latestHandshakeAt))">
|
||||||
|
· {{new Date(client.latestHandshakeAt) | timeago}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="text-right">
|
||||||
|
<div class="text-gray-400">
|
||||||
|
|
||||||
|
<!-- Enable/Disable -->
|
||||||
|
<div @click="disableClient(client)" v-if="client.enabled === true" title="Disable Client"
|
||||||
|
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-red-800 cursor-pointer hover:bg-red-700 transition-all">
|
||||||
|
<div class="rounded-full w-4 h-4 m-1 ml-5 bg-white"></div>
|
||||||
|
</div>
|
||||||
|
<div @click="enableClient(client)" v-if="client.enabled === false" title="Enable Client"
|
||||||
|
class="inline-block align-middle rounded-full w-10 h-6 mr-1 bg-gray-200 cursor-pointer hover:bg-gray-300 transition-all">
|
||||||
|
<div class="rounded-full w-4 h-4 m-1 bg-white"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Show QR-->
|
||||||
|
<button class="align-middle bg-gray-100 hover:bg-red-800 hover:text-white p-2 rounded transition"
|
||||||
|
title="Show QR Code" @click="qrcode = `/api/wireguard/client/${client.id}/qrcode.svg`">
|
||||||
|
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Download Config -->
|
||||||
|
<a :href="'/api/wireguard/client/' + client.id + '/configuration'" download
|
||||||
|
class="align-middle inline-block bg-gray-100 hover:bg-red-800 hover:text-white p-2 rounded transition"
|
||||||
|
title="Download Configuration">
|
||||||
|
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Delete -->
|
||||||
|
<button class="align-middle bg-gray-100 hover:bg-red-800 hover:text-white p-2 rounded transition"
|
||||||
|
title="Delete Client" @click="clientDelete = client">
|
||||||
|
<svg class="w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -244,7 +261,7 @@
|
|||||||
|
|
||||||
<!-- QR Code-->
|
<!-- QR Code-->
|
||||||
<div v-if="qrcode">
|
<div v-if="qrcode">
|
||||||
<div class="bg-black bg-opacity-50 fixed top-0 right-0 left-0 bottom-0 flex items-center justify-center">
|
<div class="bg-black bg-opacity-50 fixed top-0 right-0 left-0 bottom-0 flex items-center justify-center z-20">
|
||||||
<div class="bg-white rounded-md shadow-lg relative p-8">
|
<div class="bg-white rounded-md shadow-lg relative p-8">
|
||||||
<button @click="qrcode = null" class="absolute right-4 top-4 text-gray-600 hover:text-gray-800">
|
<button @click="qrcode = null" class="absolute right-4 top-4 text-gray-600 hover:text-gray-800">
|
||||||
<svg class="w-8" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
<svg class="w-8" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
@ -457,6 +474,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/js/vendor/vue.min.js"></script>
|
<script src="/js/vendor/vue.min.js"></script>
|
||||||
|
<script src="/js/vendor/apexcharts.min.js"></script>
|
||||||
|
<script src="/js/vendor/vue-apexcharts.min.js"></script>
|
||||||
<script src="/js/vendor/md5.min.js"></script>
|
<script src="/js/vendor/md5.min.js"></script>
|
||||||
<script src="/js/vendor/timeago.min.js"></script>
|
<script src="/js/vendor/timeago.min.js"></script>
|
||||||
<script src="/js/api.js"></script>
|
<script src="/js/api.js"></script>
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
|
components: {
|
||||||
|
apexchart: VueApexCharts,
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
authenticated: null,
|
authenticated: null,
|
||||||
authenticating: false,
|
authenticating: false,
|
||||||
@ -14,6 +17,7 @@ new Vue({
|
|||||||
requiresPassword: null,
|
requiresPassword: null,
|
||||||
|
|
||||||
clients: null,
|
clients: null,
|
||||||
|
clientsPersist: {},
|
||||||
clientDelete: null,
|
clientDelete: null,
|
||||||
clientCreate: null,
|
clientCreate: null,
|
||||||
clientCreateName: '',
|
clientCreateName: '',
|
||||||
@ -25,6 +29,67 @@ new Vue({
|
|||||||
|
|
||||||
currentRelease: null,
|
currentRelease: null,
|
||||||
latestRelease: null,
|
latestRelease: null,
|
||||||
|
|
||||||
|
chartOptions: {
|
||||||
|
chart: {
|
||||||
|
background: 'transparent',
|
||||||
|
type: 'area',
|
||||||
|
toolbar: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: 'gradient',
|
||||||
|
},
|
||||||
|
colors: ['#CCCCCC'],
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
curve: 'smooth',
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
labels: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisBorder: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
min: 0,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
show: false,
|
||||||
|
padding: {
|
||||||
|
left: -10,
|
||||||
|
right: 0,
|
||||||
|
bottom: -15,
|
||||||
|
top: -15,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
lines: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
dateTime: value => {
|
dateTime: value => {
|
||||||
@ -45,6 +110,49 @@ new Vue({
|
|||||||
client.avatar = `https://www.gravatar.com/avatar/${md5(client.name)}?d=blank`;
|
client.avatar = `https://www.gravatar.com/avatar/${md5(client.name)}?d=blank`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.clientsPersist[client.id]) {
|
||||||
|
this.clientsPersist[client.id] = {};
|
||||||
|
this.clientsPersist[client.id].transferRxHistory = Array(20).fill(0);
|
||||||
|
this.clientsPersist[client.id].transferRxPrevious = client.transferRx;
|
||||||
|
this.clientsPersist[client.id].transferTxHistory = Array(20).fill(0);
|
||||||
|
this.clientsPersist[client.id].transferTxPrevious = client.transferTx;
|
||||||
|
|
||||||
|
this.clientsPersist[client.id].chartOptions = {
|
||||||
|
...this.chartOptions,
|
||||||
|
yaxis: {
|
||||||
|
...this.chartOptions.yaxis,
|
||||||
|
max: () => this.clientsPersist[client.id].chartMax,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientsPersist[client.id].transferRxCurrent = client.transferRx - this.clientsPersist[client.id].transferRxPrevious;
|
||||||
|
this.clientsPersist[client.id].transferRxPrevious = client.transferRx;
|
||||||
|
this.clientsPersist[client.id].transferTxCurrent = client.transferTx - this.clientsPersist[client.id].transferTxPrevious;
|
||||||
|
this.clientsPersist[client.id].transferTxPrevious = client.transferTx;
|
||||||
|
|
||||||
|
this.clientsPersist[client.id].transferRxHistory.push(this.clientsPersist[client.id].transferRxCurrent);
|
||||||
|
this.clientsPersist[client.id].transferRxHistory.shift();
|
||||||
|
|
||||||
|
this.clientsPersist[client.id].transferTxHistory.push(this.clientsPersist[client.id].transferTxCurrent);
|
||||||
|
this.clientsPersist[client.id].transferTxHistory.shift();
|
||||||
|
|
||||||
|
client.transferTxCurrent = this.clientsPersist[client.id].transferTxCurrent;
|
||||||
|
client.transferTxSeries = [{
|
||||||
|
name: 'tx',
|
||||||
|
data: this.clientsPersist[client.id].transferTxHistory,
|
||||||
|
}];
|
||||||
|
|
||||||
|
client.transferRxCurrent = this.clientsPersist[client.id].transferRxCurrent;
|
||||||
|
client.transferRxSeries = [{
|
||||||
|
name: 'rx',
|
||||||
|
data: this.clientsPersist[client.id].transferRxHistory,
|
||||||
|
}];
|
||||||
|
|
||||||
|
this.clientsPersist[client.id].chartMax = Math.max(...this.clientsPersist[client.id].transferTxHistory, ...this.clientsPersist[client.id].transferRxHistory);
|
||||||
|
|
||||||
|
client.chartOptions = this.clientsPersist[client.id].chartOptions;
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -124,13 +232,13 @@ new Vue({
|
|||||||
},
|
},
|
||||||
bytes: (bytes, decimals, kib, maxunit) => {
|
bytes: (bytes, decimals, kib, maxunit) => {
|
||||||
kib = kib || false;
|
kib = kib || false;
|
||||||
if (bytes === 0) return '0 Bytes';
|
if (bytes === 0) return '0 B';
|
||||||
if (Number.isNaN(parseFloat(bytes)) && !Number.isFinite(bytes)) return 'Not an number';
|
if (Number.isNaN(parseFloat(bytes)) && !Number.isFinite(bytes)) return 'NaN';
|
||||||
const k = kib ? 1024 : 1000;
|
const k = kib ? 1024 : 1000;
|
||||||
const dm = decimals != null && !Number.isNaN(decimals) && decimals >= 0 ? decimals : 2;
|
const dm = decimals != null && !Number.isNaN(decimals) && decimals >= 0 ? decimals : 2;
|
||||||
const sizes = kib
|
const sizes = kib
|
||||||
? ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB', 'BiB']
|
? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB', 'BiB']
|
||||||
: ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
|
: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
|
||||||
let i = Math.floor(Math.log(bytes) / Math.log(k));
|
let i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
if (maxunit !== undefined) {
|
if (maxunit !== undefined) {
|
||||||
const index = sizes.indexOf(maxunit);
|
const index = sizes.indexOf(maxunit);
|
||||||
|
14
src/www/js/vendor/apexcharts.min.js
vendored
Normal file
14
src/www/js/vendor/apexcharts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
src/www/js/vendor/vue-apexcharts.min.js
vendored
Normal file
7
src/www/js/vendor/vue-apexcharts.min.js
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Minified by jsDelivr using Terser v5.7.1.
|
||||||
|
* Original file: /npm/vue-apexcharts@1.6.2/dist/vue-apexcharts.js
|
||||||
|
*
|
||||||
|
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||||
|
*/
|
||||||
|
!function (t, e) { "object" == typeof exports && "undefined" != typeof module ? module.exports = e(require("apexcharts/dist/apexcharts.min")) : "function" == typeof define && define.amd ? define(["apexcharts/dist/apexcharts.min"], e) : t.VueApexCharts = e(t.ApexCharts) }(this, (function (t) { "use strict"; function e(t) { return (e = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t })(t) } function n(t, e, n) { return e in t ? Object.defineProperty(t, e, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : t[e] = n, t } t = t && t.hasOwnProperty("default") ? t.default : t; var i = { props: { options: { type: Object }, type: { type: String }, series: { type: Array, required: !0, default: function () { return [] } }, width: { default: "100%" }, height: { default: "auto" } }, data: function () { return { chart: null } }, beforeMount: function () { window.ApexCharts = t }, mounted: function () { this.init() }, created: function () { var t = this; this.$watch("options", (function (e) { !t.chart && e ? t.init() : t.chart.updateOptions(t.options) })), this.$watch("series", (function (e) { !t.chart && e ? t.init() : t.chart.updateSeries(t.series) }));["type", "width", "height"].forEach((function (e) { t.$watch(e, (function () { t.refresh() })) })) }, beforeDestroy: function () { this.chart && this.destroy() }, render: function (t) { return t("div") }, methods: { init: function () { var e = this, n = { chart: { type: this.type || this.options.chart.type || "line", height: this.height, width: this.width, events: {} }, series: this.series }; Object.keys(this.$listeners).forEach((function (t) { n.chart.events[t] = e.$listeners[t] })); var i = this.extend(this.options, n); return this.chart = new t(this.$el, i), this.chart.render() }, isObject: function (t) { return t && "object" === e(t) && !Array.isArray(t) && null != t }, extend: function (t, e) { var i = this; "function" != typeof Object.assign && (Object.assign = function (t) { if (null == t) throw new TypeError("Cannot convert undefined or null to object"); for (var e = Object(t), n = 1; n < arguments.length; n++) { var i = arguments[n]; if (null != i) for (var r in i) i.hasOwnProperty(r) && (e[r] = i[r]) } return e }); var r = Object.assign({}, t); return this.isObject(t) && this.isObject(e) && Object.keys(e).forEach((function (o) { i.isObject(e[o]) && o in t ? r[o] = i.extend(t[o], e[o]) : Object.assign(r, n({}, o, e[o])) })), r }, refresh: function () { return this.destroy(), this.init() }, destroy: function () { this.chart.destroy() }, updateSeries: function (t, e) { return this.chart.updateSeries(t, e) }, updateOptions: function (t, e, n, i) { return this.chart.updateOptions(t, e, n, i) }, toggleSeries: function (t) { return this.chart.toggleSeries(t) }, showSeries: function (t) { this.chart.showSeries(t) }, hideSeries: function (t) { this.chart.hideSeries(t) }, appendSeries: function (t, e) { return this.chart.appendSeries(t, e) }, resetSeries: function () { this.chart.resetSeries() }, zoomX: function (t, e) { this.chart.zoomX(t, e) }, toggleDataPointSelection: function (t, e) { this.chart.toggleDataPointSelection(t, e) }, appendData: function (t) { return this.chart.appendData(t) }, addText: function (t) { this.chart.addText(t) }, addImage: function (t) { this.chart.addImage(t) }, addShape: function (t) { this.chart.addShape(t) }, dataURI: function () { return this.chart.dataURI() }, setLocale: function (t) { return this.chart.setLocale(t) }, addXaxisAnnotation: function (t, e) { this.chart.addXaxisAnnotation(t, e) }, addYaxisAnnotation: function (t, e) { this.chart.addYaxisAnnotation(t, e) }, addPointAnnotation: function (t, e) { this.chart.addPointAnnotation(t, e) }, removeAnnotation: function (t, e) { this.chart.removeAnnotation(t, e) }, clearAnnotations: function () { this.chart.clearAnnotations() } } }; return window.ApexCharts = t, i.install = function (e) { e.ApexCharts = t, window.ApexCharts = t, Object.defineProperty(e.prototype, "$apexcharts", { get: function () { return t } }) }, i }));
|
Loading…
Reference in New Issue
Block a user