mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-03 07:46:18 +00:00
Peer history
This commit is contained in:
@@ -908,6 +908,37 @@ def API_GetPeerSessions():
|
|||||||
return ResponseObject(data=p.getSessions(startDate, endDate))
|
return ResponseObject(data=p.getSessions(startDate, endDate))
|
||||||
return ResponseObject(False, "Peer does not exist")
|
return ResponseObject(False, "Peer does not exist")
|
||||||
|
|
||||||
|
@app.get(f'{APP_PREFIX}/api/getPeerTraffics')
|
||||||
|
def API_GetPeerTraffics():
|
||||||
|
configurationName = request.args.get("configurationName")
|
||||||
|
id = request.args.get('id')
|
||||||
|
try:
|
||||||
|
interval = request.args.get('interval', 30)
|
||||||
|
startDate = request.args.get('startDate', None)
|
||||||
|
endDate = request.args.get('endDate', None)
|
||||||
|
|
||||||
|
if type(interval) is str:
|
||||||
|
if not interval.isdigit():
|
||||||
|
return ResponseObject(False, "Interval must be integers in minutes")
|
||||||
|
interval = int(interval)
|
||||||
|
|
||||||
|
if startDate is None:
|
||||||
|
endDate = None
|
||||||
|
else:
|
||||||
|
startDate = datetime.strptime(startDate, "%Y-%m-%d")
|
||||||
|
if endDate:
|
||||||
|
endDate = datetime.strptime(endDate, "%Y-%m-%d")
|
||||||
|
if startDate > endDate:
|
||||||
|
return ResponseObject(False, "startDate must be smaller than endDate")
|
||||||
|
except Exception as e:
|
||||||
|
return ResponseObject(False, "Dates are invalid" + e)
|
||||||
|
if not configurationName or not id:
|
||||||
|
return ResponseObject(False, "Please provide configurationName and id")
|
||||||
|
fp, p = WireguardConfigurations.get(configurationName).searchPeer(id)
|
||||||
|
if fp:
|
||||||
|
return ResponseObject(data=p.getTraffics(interval, startDate, endDate))
|
||||||
|
return ResponseObject(False, "Peer does not exist")
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
|
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
|
||||||
def API_getDashboardTheme():
|
def API_getDashboardTheme():
|
||||||
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
||||||
|
@@ -236,17 +236,48 @@ class Peer:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def getTraffics(self, interval: int = 30, startDate: datetime.datetime = None, endDate: datetime.datetime = None):
|
||||||
|
if startDate is None and endDate is None:
|
||||||
|
endDate = datetime.datetime.now()
|
||||||
|
startDate = endDate - timedelta(minutes=interval)
|
||||||
|
else:
|
||||||
|
endDate = endDate.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||||
|
startDate = startDate.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
|
with self.configuration.engine.connect() as conn:
|
||||||
|
result = conn.execute(
|
||||||
|
db.select(
|
||||||
|
self.configuration.peersTransferTable.c.cumu_data,
|
||||||
|
self.configuration.peersTransferTable.c.total_data,
|
||||||
|
self.configuration.peersTransferTable.c.cumu_receive,
|
||||||
|
self.configuration.peersTransferTable.c.total_receive,
|
||||||
|
self.configuration.peersTransferTable.c.cumu_sent,
|
||||||
|
self.configuration.peersTransferTable.c.total_sent,
|
||||||
|
self.configuration.peersTransferTable.c.time
|
||||||
|
).where(
|
||||||
|
db.and_(
|
||||||
|
self.configuration.peersTransferTable.c.id == self.id,
|
||||||
|
self.configuration.peersTransferTable.c.time <= endDate,
|
||||||
|
self.configuration.peersTransferTable.c.time >= startDate,
|
||||||
|
)
|
||||||
|
).order_by(
|
||||||
|
self.configuration.peersTransferTable.c.time
|
||||||
|
)
|
||||||
|
).mappings().fetchall()
|
||||||
|
return list(result)
|
||||||
|
|
||||||
|
|
||||||
def getSessions(self, startDate: datetime.datetime = None, endDate: datetime.datetime = None):
|
def getSessions(self, startDate: datetime.datetime = None, endDate: datetime.datetime = None):
|
||||||
if endDate is None:
|
if endDate is None:
|
||||||
endDate = datetime.datetime.now()
|
endDate = datetime.datetime.now()
|
||||||
|
|
||||||
if startDate is None:
|
if startDate is None:
|
||||||
startDate = (endDate - datetime.timedelta(days=1))
|
startDate = endDate
|
||||||
|
|
||||||
endDate = endDate.replace(hour=23, minute=59, second=59, microsecond=999999)
|
endDate = endDate.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||||
startDate = startDate.replace(hour=0, minute=0, second=0, microsecond=0)
|
startDate = startDate.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
|
|
||||||
with self.configuration.engine.connect() as conn:
|
with self.configuration.engine.connect() as conn:
|
||||||
result = conn.execute(
|
result = conn.execute(
|
||||||
db.select(
|
db.select(
|
||||||
@@ -261,23 +292,27 @@ class Peer:
|
|||||||
self.configuration.peersTransferTable.c.time
|
self.configuration.peersTransferTable.c.time
|
||||||
)
|
)
|
||||||
).fetchall()
|
).fetchall()
|
||||||
|
time = list(map(lambda x : x[0], result))
|
||||||
|
return time
|
||||||
# sessions = []
|
# sessions = []
|
||||||
# current_session = [time[0]]
|
# if len(time) > 1:
|
||||||
|
# current_session = [time[0]]
|
||||||
#
|
#
|
||||||
# for ts in time[1:]:
|
# for ts in time[1:]:
|
||||||
# if ts - current_session[-1] <= datetime.timedelta(minutes=3):
|
# if ts - current_session[-1] <= datetime.timedelta(minutes=3):
|
||||||
# current_session.append(ts)
|
# current_session.append(ts)
|
||||||
# else:
|
# else:
|
||||||
# sessions.append({
|
# sessions.append({
|
||||||
# "duration": self.__duration(current_session[-1], current_session[0]),
|
# "duration": self.__duration(current_session[-1], current_session[0]),
|
||||||
# "timestamps": current_session
|
# "timestamps": current_session
|
||||||
# })
|
# })
|
||||||
# current_session = [ts]
|
# current_session = [ts]
|
||||||
# sessions.append({
|
# sessions.append({
|
||||||
# "duration": self.__duration(current_session[-1], current_session[0]),
|
# "duration": self.__duration(current_session[-1], current_session[0]),
|
||||||
# "timestamps": current_session
|
# "timestamps": current_session
|
||||||
# })
|
# })
|
||||||
return list(map(lambda x : x[0], result))
|
# print(sessions)
|
||||||
|
# return sessions
|
||||||
|
|
||||||
def __duration(self, t1: datetime.datetime, t2: datetime.datetime):
|
def __duration(self, t1: datetime.datetime, t2: datetime.datetime):
|
||||||
delta = t1 - t2
|
delta = t1 - t2
|
||||||
|
@@ -16,9 +16,6 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
Peer: Object, ConfigurationInfo: Object, order: Number, searchPeersLength: Number
|
Peer: Object, ConfigurationInfo: Object, order: Number, searchPeersLength: Number
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
setup(){
|
setup(){
|
||||||
const target = ref(null);
|
const target = ref(null);
|
||||||
@@ -136,7 +133,12 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer" role="button" @click="$emit('details')">
|
||||||
|
<small class="d-flex align-items-center">
|
||||||
|
<LocaleText t="Details"></LocaleText>
|
||||||
|
<i class="bi bi-chevron-right ms-auto"></i>
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -0,0 +1,178 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import {computed, ref, watch} from "vue";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import {GetLocale} from "@/utilities/locale.js"
|
||||||
|
import {
|
||||||
|
Chart,
|
||||||
|
LineElement,
|
||||||
|
BarElement,
|
||||||
|
BarController,
|
||||||
|
LineController,
|
||||||
|
LinearScale,
|
||||||
|
Legend,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
CategoryScale,
|
||||||
|
PointElement,
|
||||||
|
Filler
|
||||||
|
} from 'chart.js';
|
||||||
|
Chart.register(
|
||||||
|
LineElement,
|
||||||
|
BarElement,
|
||||||
|
BarController,
|
||||||
|
LineController,
|
||||||
|
LinearScale,
|
||||||
|
Legend,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
CategoryScale,
|
||||||
|
PointElement,
|
||||||
|
Filler
|
||||||
|
);
|
||||||
|
import {Line} from "vue-chartjs";
|
||||||
|
import PeerSessions from "@/components/peerDetailsModalComponents/peerSessions.vue";
|
||||||
|
import PeerTraffics from "@/components/peerDetailsModalComponents/peerTraffics.vue";
|
||||||
|
const props = defineProps(['selectedPeer'])
|
||||||
|
const selectedDate = ref(undefined)
|
||||||
|
defineEmits(['close'])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll">
|
||||||
|
<div class=" d-flex h-100 w-100">
|
||||||
|
<div class="w-100 p-2">
|
||||||
|
<div class="card rounded-3 shadow" style="width: 100%">
|
||||||
|
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2">
|
||||||
|
<h4 class="mb-0 fw-normal">
|
||||||
|
<LocaleText t="Peer Details"></LocaleText>
|
||||||
|
</h4>
|
||||||
|
<button type="button" class="btn-close ms-auto" @click="$emit('close')"></button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body px-4">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Peer"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
<h2>
|
||||||
|
{{ selectedPeer.name }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-3 gy-2 gx-2 mb-2">
|
||||||
|
<div class="col-12 col-lg-3">
|
||||||
|
<div class="card rounded-3 bg-transparent h-100">
|
||||||
|
<div class="card-body py-2 d-flex flex-column justify-content-center">
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Status"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="dot ms-0 me-2" :class="{active: selectedPeer.status === 'running'}"></span>
|
||||||
|
<LocaleText t="Connected" v-if="selectedPeer.status === 'running'"></LocaleText>
|
||||||
|
<LocaleText t="Disconnected" v-else></LocaleText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-3">
|
||||||
|
<div class="card rounded-3 bg-transparent h-100">
|
||||||
|
<div class="card-body py-2 d-flex flex-column justify-content-center">
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Allowed IPs"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
{{selectedPeer.allowed_ip}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="word-break: break-all" class="col-12 col-lg-6">
|
||||||
|
<div class="card rounded-3 bg-transparent h-100">
|
||||||
|
<div class="card-body py-2 d-flex flex-column justify-content-center">
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Public Key"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
<samp>{{selectedPeer.id}}</samp>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-lg-3">
|
||||||
|
<div class="card rounded-3 bg-transparent h-100">
|
||||||
|
<div class="card-body d-flex">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Latest Handshake Time"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
<strong class="h4">
|
||||||
|
<LocaleText :t="selectedPeer.latest_handshake + ' ago'"></LocaleText>
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
<i class="bi bi-person-raised-hand ms-auto h2 text-muted"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-3">
|
||||||
|
<div class="card rounded-3 bg-transparent h-100">
|
||||||
|
<div class="card-body d-flex">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Total Usage"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
<strong class="h4 text-warning">
|
||||||
|
{{ (selectedPeer.total_data + selectedPeer.cumu_data).toFixed(4) }} GB
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
<i class="bi bi-arrow-down-up ms-auto h2 text-muted"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-3">
|
||||||
|
<div class="card rounded-3 bg-transparent h-100">
|
||||||
|
<div class="card-body d-flex">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Total Received"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
<strong class="h4 text-primary">{{(selectedPeer.total_receive + selectedPeer.cumu_receive).toFixed(4)}} GB</strong>
|
||||||
|
</div>
|
||||||
|
<i class="bi bi-arrow-down ms-auto h2 text-muted"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-3">
|
||||||
|
<div class="card rounded-3 bg-transparent h-100">
|
||||||
|
<div class="card-body d-flex">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 text-muted"><small>
|
||||||
|
<LocaleText t="Total Sent"></LocaleText>
|
||||||
|
</small></p>
|
||||||
|
<strong class="h4 text-success">{{(selectedPeer.total_sent + selectedPeer.cumu_sent).toFixed(4)}} GB</strong>
|
||||||
|
</div>
|
||||||
|
<i class="bi bi-arrow-up ms-auto h2 text-muted"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<PeerSessions
|
||||||
|
:selectedDate="selectedDate"
|
||||||
|
@selectDate="args => selectedDate = args"
|
||||||
|
:selectedPeer="selectedPeer"></PeerSessions>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<PeerTraffics
|
||||||
|
:selectedDate="selectedDate"
|
||||||
|
:selectedPeer="selectedPeer"></PeerTraffics>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@@ -1,5 +1,5 @@
|
|||||||
<script setup async>
|
<script setup async>
|
||||||
import {computed, defineAsyncComponent, onBeforeUnmount, ref, watch} from "vue";
|
import {computed, defineAsyncComponent, onBeforeUnmount, onMounted, ref, watch} from "vue";
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import {fetchGet} from "@/utilities/fetch.js";
|
import {fetchGet} from "@/utilities/fetch.js";
|
||||||
import ProtocolBadge from "@/components/protocolBadge.vue";
|
import ProtocolBadge from "@/components/protocolBadge.vue";
|
||||||
@@ -12,6 +12,7 @@ import Peer from "@/components/configurationComponents/peer.vue";
|
|||||||
import PeerListModals from "@/components/configurationComponents/peerListComponents/peerListModals.vue";
|
import PeerListModals from "@/components/configurationComponents/peerListComponents/peerListModals.vue";
|
||||||
import PeerIntersectionObserver from "@/components/configurationComponents/peerIntersectionObserver.vue";
|
import PeerIntersectionObserver from "@/components/configurationComponents/peerIntersectionObserver.vue";
|
||||||
import ConfigurationDescription from "@/components/configurationComponents/configurationDescription.vue";
|
import ConfigurationDescription from "@/components/configurationComponents/configurationDescription.vue";
|
||||||
|
import PeerDetailsModal from "@/components/configurationComponents/peerDetailsModal.vue";
|
||||||
|
|
||||||
// Async Components
|
// Async Components
|
||||||
const PeerSearchBar = defineAsyncComponent(() => import("@/components/configurationComponents/peerSearchBar.vue"))
|
const PeerSearchBar = defineAsyncComponent(() => import("@/components/configurationComponents/peerSearchBar.vue"))
|
||||||
@@ -73,6 +74,9 @@ const configurationModals = ref({
|
|||||||
},
|
},
|
||||||
assignPeer: {
|
assignPeer: {
|
||||||
modalOpen: false
|
modalOpen: false
|
||||||
|
},
|
||||||
|
peerDetails: {
|
||||||
|
modalOpen: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const peerSearchBar = ref(false)
|
const peerSearchBar = ref(false)
|
||||||
@@ -205,10 +209,6 @@ const searchPeers = computed(() => {
|
|||||||
}).slice(0, showPeersCount.value)
|
}).slice(0, showPeersCount.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const dropup = (index) => {
|
|
||||||
return searchPeers.value.length - (index + 1) <= 3
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(() => route.query.id, (newValue) => {
|
watch(() => route.query.id, (newValue) => {
|
||||||
if (newValue){
|
if (newValue){
|
||||||
wireguardConfigurationStore.searchString = newValue
|
wireguardConfigurationStore.searchString = newValue
|
||||||
@@ -220,7 +220,9 @@ watch(() => route.query.id, (newValue) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// onMounted(() => {
|
||||||
|
// configurationModalSelectedPeer.value = searchPeers.value[0]
|
||||||
|
// })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -393,8 +395,10 @@ watch(() => route.query.id, (newValue) => {
|
|||||||
:searchPeersLength="searchPeers.length"
|
:searchPeersLength="searchPeers.length"
|
||||||
:order="order"
|
:order="order"
|
||||||
:ConfigurationInfo="configurationInfo"
|
:ConfigurationInfo="configurationInfo"
|
||||||
|
@details="configurationModals.peerDetails.modalOpen = true; configurationModalSelectedPeer = peer"
|
||||||
@share="configurationModals.peerShare.modalOpen = true; configurationModalSelectedPeer = peer"
|
@share="configurationModals.peerShare.modalOpen = true; configurationModalSelectedPeer = peer"
|
||||||
@refresh="fetchPeerList()"
|
@refresh="fetchPeerList()"
|
||||||
|
|
||||||
@jobs="configurationModals.peerScheduleJobs.modalOpen = true; configurationModalSelectedPeer = peer"
|
@jobs="configurationModals.peerScheduleJobs.modalOpen = true; configurationModalSelectedPeer = peer"
|
||||||
@setting="configurationModals.peerSetting.modalOpen = true; configurationModalSelectedPeer = peer"
|
@setting="configurationModals.peerSetting.modalOpen = true; configurationModalSelectedPeer = peer"
|
||||||
@qrcode="configurationModalSelectedPeer = peer; configurationModals.peerQRCode.modalOpen = true;"
|
@qrcode="configurationModalSelectedPeer = peer; configurationModals.peerQRCode.modalOpen = true;"
|
||||||
@@ -456,6 +460,13 @@ watch(() => route.query.id, (newValue) => {
|
|||||||
:configurationPeers="configurationPeers"
|
:configurationPeers="configurationPeers"
|
||||||
@close="configurationModals.selectPeers.modalOpen = false"
|
@close="configurationModals.selectPeers.modalOpen = false"
|
||||||
></SelectPeersModal>
|
></SelectPeersModal>
|
||||||
|
<PeerDetailsModal
|
||||||
|
key="PeerDetailsModal"
|
||||||
|
v-if="configurationModals.peerDetails.modalOpen"
|
||||||
|
:selectedPeer="searchPeers.find(x => x.id === configurationModalSelectedPeer.id)"
|
||||||
|
@close="configurationModals.peerDetails.modalOpen = false"
|
||||||
|
>
|
||||||
|
</PeerDetailsModal>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
<PeerIntersectionObserver
|
<PeerIntersectionObserver
|
||||||
:showPeersCount="showPeersCount"
|
:showPeersCount="showPeersCount"
|
||||||
|
@@ -13,7 +13,8 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
CategoryScale,
|
CategoryScale,
|
||||||
PointElement
|
PointElement,
|
||||||
|
Filler
|
||||||
} from 'chart.js';
|
} from 'chart.js';
|
||||||
Chart.register(
|
Chart.register(
|
||||||
LineElement,
|
LineElement,
|
||||||
@@ -25,13 +26,15 @@ Chart.register(
|
|||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
CategoryScale,
|
CategoryScale,
|
||||||
PointElement
|
PointElement,
|
||||||
|
Filler
|
||||||
);
|
);
|
||||||
|
|
||||||
import LocaleText from "@/components/text/localeText.vue";
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
import {GetLocale} from "@/utilities/locale.js";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
configurationPeers: Array,
|
configurationPeers: Array,
|
||||||
configurationInfo: Object
|
configurationInfo: Object
|
||||||
@@ -124,12 +127,14 @@ const peersRealtimeSentData = computed(() => {
|
|||||||
labels: [...historySentData.value.timestamp],
|
labels: [...historySentData.value.timestamp],
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Data Sent',
|
label: GetLocale('Data Sent'),
|
||||||
data: [...historySentData.value.data],
|
data: [...historySentData.value.data],
|
||||||
fill: false,
|
fill: 'start',
|
||||||
borderColor: '#198754',
|
borderColor: '#198754',
|
||||||
backgroundColor: '#198754',
|
backgroundColor: '#19875490',
|
||||||
tension: 0
|
tension: 0,
|
||||||
|
pointRadius: 2,
|
||||||
|
borderWidth: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -139,12 +144,14 @@ const peersRealtimeReceivedData = computed(() => {
|
|||||||
labels: [...historyReceivedData.value.timestamp],
|
labels: [...historyReceivedData.value.timestamp],
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Data Received',
|
label: GetLocale('Data Received'),
|
||||||
data: [...historyReceivedData.value.data],
|
data: [...historyReceivedData.value.data],
|
||||||
fill: false,
|
fill: 'start',
|
||||||
borderColor: '#0d6efd',
|
borderColor: '#0d6efd',
|
||||||
backgroundColor: '#0d6efd',
|
backgroundColor: '#0d6efd90',
|
||||||
tension: 0
|
tension: 0,
|
||||||
|
pointRadius: 2,
|
||||||
|
borderWidth: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,8 @@ const PeerJobsModal = defineAsyncComponent(() => import("@/components/configurat
|
|||||||
const PeerQRCodeModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerQRCode.vue"))
|
const PeerQRCodeModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerQRCode.vue"))
|
||||||
const PeerConfigurationFileModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerConfigurationFile.vue"))
|
const PeerConfigurationFileModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerConfigurationFile.vue"))
|
||||||
const PeerSettingsModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerSettings.vue"))
|
const PeerSettingsModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerSettings.vue"))
|
||||||
|
const PeerDetailsModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerDetailsModal.vue"))
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -43,14 +45,17 @@ const PeerSettingsModal = defineAsyncComponent(() => import("@/components/config
|
|||||||
:selectedPeer="configurationModalSelectedPeer">
|
:selectedPeer="configurationModalSelectedPeer">
|
||||||
</PeerShareLinkModal>
|
</PeerShareLinkModal>
|
||||||
<PeerConfigurationFileModal
|
<PeerConfigurationFileModal
|
||||||
|
key="PeerConfigurationFileModal"
|
||||||
@close="configurationModals.peerConfigurationFile.modalOpen = false"
|
@close="configurationModals.peerConfigurationFile.modalOpen = false"
|
||||||
v-if="configurationModals.peerConfigurationFile.modalOpen"
|
v-if="configurationModals.peerConfigurationFile.modalOpen"
|
||||||
:selectedPeer="configurationModalSelectedPeer"
|
:selectedPeer="configurationModalSelectedPeer"
|
||||||
></PeerConfigurationFileModal>
|
></PeerConfigurationFileModal>
|
||||||
<PeerAssignModal
|
<PeerAssignModal
|
||||||
|
key="PeerAssignModal"
|
||||||
:selectedPeer="configurationModalSelectedPeer"
|
:selectedPeer="configurationModalSelectedPeer"
|
||||||
@close="configurationModals.assignPeer.modalOpen = false"
|
@close="configurationModals.assignPeer.modalOpen = false"
|
||||||
v-if="configurationModals.assignPeer.modalOpen"></PeerAssignModal>
|
v-if="configurationModals.assignPeer.modalOpen"></PeerAssignModal>
|
||||||
|
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
|
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
const props = defineProps(['session'])
|
||||||
|
|
||||||
|
const heightPerSecond = 1224 / 24 / 60 / 60;
|
||||||
|
const start = dayjs(props.session.timestamps[0])
|
||||||
|
const offsetTop = (start.hour() * 60 * 60 + start.minute() * 60 + start.second()) * heightPerSecond
|
||||||
|
|
||||||
|
const duration = dayjs(props.session.duration, "HH:mm:ss")
|
||||||
|
const height = (duration.hour() * 60 * 60 + duration.minute() * 60 + duration.second()) * heightPerSecond
|
||||||
|
|
||||||
|
console.log()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="position-absolute"
|
||||||
|
:style="{top: offsetTop + 'px'}"
|
||||||
|
style="width: 100%; padding-left: calc(18px + 2.5rem)">
|
||||||
|
<div class="rounded-3"
|
||||||
|
style="background: rgba(25,135,84,0.7); min-height: 5px !important;"
|
||||||
|
:style="{height: height + 'px'}">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@@ -0,0 +1,80 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {computed} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps(['sessions', 'day'])
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
||||||
|
import duration from "dayjs/plugin/duration"
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
dayjs.extend(isSameOrBefore)
|
||||||
|
dayjs.extend(duration)
|
||||||
|
|
||||||
|
const sessionsOfToday = computed(() => {
|
||||||
|
let sessions = props.sessions.map(x => dayjs(x)).filter(x => x.isSame(props.day, 'D')).reverse()
|
||||||
|
let result = []
|
||||||
|
if (sessions.length > 1){
|
||||||
|
let cur = [sessions[0]]
|
||||||
|
for (let s of sessions.slice(1)){
|
||||||
|
if (s.isSameOrBefore(cur[cur.length - 1].add(3, 'minute'))){
|
||||||
|
cur.push(s)
|
||||||
|
}else{
|
||||||
|
result.push({
|
||||||
|
timestamps: cur,
|
||||||
|
duration: dayjs.duration(
|
||||||
|
cur[cur.length - 1].diff(cur[0])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
cur = [s]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
result.push({
|
||||||
|
timestamps: cur,
|
||||||
|
duration: dayjs.duration(
|
||||||
|
cur[cur.length - 1].diff(cur[0])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
defineEmits(['openDetails'])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex gap-1 flex-column" @click="$emit('openDetails', sessionsOfToday)">
|
||||||
|
<small v-if="sessionsOfToday.length > 0" class="sessions-label">
|
||||||
|
<LocaleText :t="sessionsOfToday.length + ' Session' + (sessionsOfToday.length > 1 ? 's':'')"></LocaleText>
|
||||||
|
</small>
|
||||||
|
<div class="d-flex flex-wrap gap-1 session-dot">
|
||||||
|
<div class="bg-warning"
|
||||||
|
style="height: 5px; width: 5px; border-radius: 100%; vertical-align: top" v-for="_ in sessionsOfToday.length"></div>
|
||||||
|
</div>
|
||||||
|
<div class="p-1 badge text-bg-warning text-start session-list" v-for="s in sessionsOfToday">
|
||||||
|
<div>
|
||||||
|
<i class="bi bi-stopwatch me-1"></i>{{ s.timestamps[0].format("HH:mm:ss") }}<i class="bi bi-arrow-right mx-1"></i>{{ s.timestamps[s.timestamps.length - 1].format("HH:mm:ss") }}
|
||||||
|
</div>
|
||||||
|
<div class="mt-1">
|
||||||
|
<LocaleText t="Duration:"></LocaleText> {{ s.duration.format('HH:mm:ss')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@media screen and (max-width: 992px) {
|
||||||
|
.calendar-day .session-list{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sessions-label{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 992px) {
|
||||||
|
.session-dot{
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
||||||
|
dayjs.extend(isSameOrBefore)
|
||||||
|
const props = defineProps(['sessions'])
|
||||||
|
const offsetMonth = ref(0)
|
||||||
|
const today = ref(dayjs().subtract(offsetMonth.value, 'month'))
|
||||||
|
const firstDay = ref(today.value.startOf('month'))
|
||||||
|
const lastDay = ref(today.value.endOf('month'))
|
||||||
|
|
||||||
|
const startOfWeek = ref(firstDay.value.startOf('week'))
|
||||||
|
const endOfWeek = ref(lastDay.value.endOf('week'))
|
||||||
|
|
||||||
|
const calendarDays = computed(() => {
|
||||||
|
let dates = []
|
||||||
|
let cur = startOfWeek.value;
|
||||||
|
while (cur.isSameOrBefore(endOfWeek.value, 'day')){
|
||||||
|
dates.push(cur)
|
||||||
|
cur = cur.add(1, 'day')
|
||||||
|
}
|
||||||
|
return dates
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="calendar-grid">
|
||||||
|
<div class="calendar-day p-2"
|
||||||
|
:class="{
|
||||||
|
'bg-body-secondary': day.isSame(today, 'D'),
|
||||||
|
'border-end': day.day() < 6,
|
||||||
|
'border-bottom': index < 28}"
|
||||||
|
v-for="(day, index) in calendarDays">
|
||||||
|
<h5>
|
||||||
|
{{ day.isSame(day.startOf('month')) ? day.format("MMM") : '' }} {{ day.format('D')}}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.calendar-grid{
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "sun mon tue wed thu fri sat";
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day.day-6{
|
||||||
|
border-right: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.calendar-day{
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.calendar-day.active{
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,169 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import { fetchGet } from "@/utilities/fetch.js"
|
||||||
|
import {computed, onBeforeUnmount, ref, watch} from "vue";
|
||||||
|
|
||||||
|
const props = defineProps(['selectedPeer', 'selectedDate'])
|
||||||
|
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js"
|
||||||
|
const store = DashboardConfigurationStore()
|
||||||
|
const sessions = ref([])
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
||||||
|
import PeerSessionCalendarDay from "@/components/peerDetailsModalComponents/peerSessionCalendarDay.vue";
|
||||||
|
dayjs.extend(isSameOrBefore)
|
||||||
|
const interval = ref(undefined)
|
||||||
|
const offsetMonth = ref(0)
|
||||||
|
const todayNoOffset = ref(dayjs())
|
||||||
|
const today = computed(() => dayjs().add(offsetMonth.value, 'month'))
|
||||||
|
const firstDay = computed(() => today.value.startOf('month'))
|
||||||
|
const lastDay = computed(() => today.value.endOf('month'))
|
||||||
|
|
||||||
|
const startOfWeek = computed(() => firstDay.value.startOf('week'))
|
||||||
|
const endOfWeek = computed(() => lastDay.value.endOf('week'))
|
||||||
|
|
||||||
|
const calendarDays = computed(() => {
|
||||||
|
let dates = []
|
||||||
|
let cur = startOfWeek.value;
|
||||||
|
while (cur.isSameOrBefore(endOfWeek.value, 'day')){
|
||||||
|
dates.push(cur)
|
||||||
|
cur = cur.add(1, 'day')
|
||||||
|
}
|
||||||
|
return dates
|
||||||
|
})
|
||||||
|
|
||||||
|
const getSessions = async () => {
|
||||||
|
await fetchGet("/api/getPeerSessions", {
|
||||||
|
configurationName: props.selectedPeer.configuration.Name,
|
||||||
|
id: props.selectedPeer.id,
|
||||||
|
startDate: startOfWeek.value.format("YYYY-MM-DD"),
|
||||||
|
endDate: endOfWeek.value.format("YYYY-MM-DD")
|
||||||
|
}, (res) => {
|
||||||
|
sessions.value = res.data.reverse()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await getSessions()
|
||||||
|
interval.value = setInterval(async () => {
|
||||||
|
await getSessions()
|
||||||
|
}, 60000)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => today.value, () => getSessions())
|
||||||
|
|
||||||
|
const dayDetails = ref(false)
|
||||||
|
const dayDetailsData = ref(undefined)
|
||||||
|
const emits = defineEmits(['selectDate'])
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<div class="card rounded-3 bg-transparent">
|
||||||
|
<div class="card-header d-flex align-items-center">
|
||||||
|
<button class="btn btn-sm rounded-3" @click="offsetMonth -= 1">
|
||||||
|
<i class="bi bi-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm rounded-3" v-if="offsetMonth !== 0" @click="offsetMonth = 0; $emit('selectDate', day)">
|
||||||
|
<LocaleText t="Today"></LocaleText>
|
||||||
|
</button>
|
||||||
|
<h5 class="mx-auto mb-0">
|
||||||
|
{{ today.format('YYYY-MM')}}
|
||||||
|
</h5>
|
||||||
|
<button class="btn btn-sm rounded-3" v-if="offsetMonth !== 0" @click="offsetMonth = 0; $emit('selectDate', day)">
|
||||||
|
<LocaleText t="Today"></LocaleText>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm rounded-3" @click="offsetMonth += 1">
|
||||||
|
<i class="bi bi-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0 position-relative">
|
||||||
|
<div class="calendar-grid">
|
||||||
|
<div class="calendar-day p-2 d-flex flex-column"
|
||||||
|
:key="day"
|
||||||
|
@click="$emit('selectDate', day)"
|
||||||
|
style="cursor: pointer"
|
||||||
|
:class="{
|
||||||
|
'bg-body-secondary': day.isSame(todayNoOffset, 'D'),
|
||||||
|
'border-end': day.day() < 6,
|
||||||
|
'border-bottom': index < calendarDays.length - 7,
|
||||||
|
'extra-day': !day.isSame(today, 'month')}"
|
||||||
|
v-for="(day, index) in calendarDays">
|
||||||
|
<h5 class="d-flex">
|
||||||
|
{{ day.format('D')}}
|
||||||
|
<i class="bi bi-check-circle-fill ms-auto" v-if="selectedDate && selectedDate.isSame(day, 'D')"></i>
|
||||||
|
</h5>
|
||||||
|
<PeerSessionCalendarDay
|
||||||
|
class="flex-grow-1"
|
||||||
|
@openDetails="args => {dayDetailsData = {day: day, details: args}; dayDetails = true}"
|
||||||
|
:sessions="sessions"
|
||||||
|
:day="day" :key="day"></PeerSessionCalendarDay>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Transition name="zoom">
|
||||||
|
<div class="position-absolute rounded-bottom-3 dayDetail p-3"
|
||||||
|
v-if="dayDetails"
|
||||||
|
style="bottom: 0; height: 100%; width: 100%; z-index: 9999; background: #00000050; backdrop-filter: blur(8px); overflow: scroll">
|
||||||
|
<div class="d-flex mb-3">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
{{ dayDetailsData.day.format("YYYY-MM-DD") }}
|
||||||
|
</h5>
|
||||||
|
<a role="button" class="ms-auto text-white" @click="dayDetails = false">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<i class="bi bi-x-lg"></i>
|
||||||
|
</h5>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-column gap-2">
|
||||||
|
<div class="p-1 badge text-bg-warning text-start session-list d-flex align-items-center" v-for="s in dayDetailsData.details">
|
||||||
|
<div>
|
||||||
|
<i class="bi bi-stopwatch me-1"></i>{{ s.timestamps[0].format("HH:mm:ss") }}<i class="bi bi-arrow-right mx-1"></i>{{ s.timestamps[s.timestamps.length - 1].format("HH:mm:ss") }}
|
||||||
|
</div>
|
||||||
|
<div class="ms-auto">
|
||||||
|
<LocaleText t="Duration:"></LocaleText> {{ s.duration.format('HH:mm:ss')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.calendar-grid{
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "sun mon tue wed thu fri sat";
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day.day-6{
|
||||||
|
border-right: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day{
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 992px) {
|
||||||
|
.calendar-day{
|
||||||
|
min-height: 100px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 992px) {
|
||||||
|
.dayDetail{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-day h5{
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,178 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import {Line} from "vue-chartjs";
|
||||||
|
import {GetLocale} from "@/utilities/locale.js"
|
||||||
|
import { fetchGet } from "@/utilities/fetch.js"
|
||||||
|
import {computed, onBeforeUnmount, ref, watch} from "vue";
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js"
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
const props = defineProps(['selectedDate', 'selectedPeer'])
|
||||||
|
const store = DashboardConfigurationStore()
|
||||||
|
|
||||||
|
const date = computed(() => props.selectedDate ? props.selectedDate : dayjs())
|
||||||
|
const traffics = ref([])
|
||||||
|
const getTraffic = async () => {
|
||||||
|
await fetchGet("/api/getPeerTraffics", {
|
||||||
|
configurationName: props.selectedPeer.configuration.Name,
|
||||||
|
id: props.selectedPeer.id,
|
||||||
|
startDate: date.value.format("YYYY-MM-DD"),
|
||||||
|
endDate: date.value.format("YYYY-MM-DD")
|
||||||
|
}, (res) => {
|
||||||
|
traffics.value = res.data
|
||||||
|
console.log(traffics.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const interval = ref(undefined)
|
||||||
|
await getTraffic()
|
||||||
|
interval.value = setInterval(async () => {
|
||||||
|
await getTraffic()
|
||||||
|
}, 60000)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval.value)
|
||||||
|
})
|
||||||
|
watch(() => date.value, () => {getTraffic()})
|
||||||
|
|
||||||
|
|
||||||
|
const dataUsageChartOption = computed(() => {
|
||||||
|
return {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: (tooltipItem) => {
|
||||||
|
return `${tooltipItem.formattedValue} MB`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y:{
|
||||||
|
ticks: {
|
||||||
|
callback: (val) => {
|
||||||
|
return `${val.toFixed(4)} MB`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
display: true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const historicalSent = computed(() => {
|
||||||
|
let h = traffics.value.map(x => x.cumu_sent + x.total_sent)
|
||||||
|
let r = [0]
|
||||||
|
if (h.length > 1){
|
||||||
|
for (let i = 1; i < h.length; i++){
|
||||||
|
if (h[i] >= h[i - 1]){
|
||||||
|
r.push((h[i] - h[i - 1]) * 1024)
|
||||||
|
}else{
|
||||||
|
r.push(h[i] * 1024)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
|
||||||
|
const historicalReceive = computed(() => {
|
||||||
|
let h = traffics.value.map(x => x.cumu_receive + x.total_receive)
|
||||||
|
let r = [0]
|
||||||
|
if (h.length > 1){
|
||||||
|
for (let i = 1; i < h.length; i++){
|
||||||
|
if (h[i] >= h[i - 1]){
|
||||||
|
r.push((h[i] - h[i - 1]) * 1024)
|
||||||
|
}else{
|
||||||
|
r.push(h[i] * 1024)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
|
||||||
|
const historicalSentData = computed(() => {
|
||||||
|
return {
|
||||||
|
labels: traffics.value.map(x => x.time),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: GetLocale('Data Sent'),
|
||||||
|
data: historicalSent.value,
|
||||||
|
fill: 'start',
|
||||||
|
borderColor: '#198754',
|
||||||
|
backgroundColor: '#19875490',
|
||||||
|
tension: 0,
|
||||||
|
pointRadius: 2,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const historicalReceivedData = computed(() => {
|
||||||
|
return {
|
||||||
|
labels: traffics.value.map(x => x.time),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: GetLocale('Data Received'),
|
||||||
|
data: historicalReceive.value,
|
||||||
|
fill: 'start',
|
||||||
|
borderColor: '#0d6efd',
|
||||||
|
backgroundColor: '#0d6efd90',
|
||||||
|
tension: 0.3,
|
||||||
|
pointRadius: 2,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card rounded-3 bg-transparent">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="text-muted">
|
||||||
|
<LocaleText :t="'Peer Historical Data Usage of ' + date.format('YYYY-MM-DD')"></LocaleText>
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column gap-3">
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<LocaleText t="Data Received"></LocaleText>
|
||||||
|
</p>
|
||||||
|
<Line
|
||||||
|
:options="dataUsageChartOption"
|
||||||
|
:data="historicalReceivedData"
|
||||||
|
style="width: 100%; height: 300px; max-height: 300px"
|
||||||
|
></Line>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<LocaleText t="Data Sent"></LocaleText>
|
||||||
|
</p>
|
||||||
|
<Line
|
||||||
|
:options="dataUsageChartOption"
|
||||||
|
:data="historicalSentData"
|
||||||
|
style="width: 100%; height: 300px; max-height: 300px"
|
||||||
|
></Line>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@@ -56,7 +56,7 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="position-absolute w-100 h-100 top-0 start-0 rounded-bottom-3 p-3 d-flex"
|
<div class="position-absolute w-100 h-100 top-0 start-0 rounded-bottom-3 p-3 d-flex"
|
||||||
style="background-color: #00000060; backdrop-filter: blur(3px)">
|
style="background-color: #00000060; backdrop-filter: blur(3px); z-index: 9999">
|
||||||
<div class="card m-auto rounded-3 mt-5">
|
<div class="card m-auto rounded-3 mt-5">
|
||||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0">
|
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0">
|
||||||
<h6 class="mb-0">
|
<h6 class="mb-0">
|
||||||
@@ -70,6 +70,7 @@ export default {
|
|||||||
</small>
|
</small>
|
||||||
<div class="d-flex align-items-center gap-2">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<VueDatePicker
|
<VueDatePicker
|
||||||
|
style="z-index: 9999"
|
||||||
:is24="true"
|
:is24="true"
|
||||||
:min-date="new Date()"
|
:min-date="new Date()"
|
||||||
:model-value="this.newKeyData.ExpiredAt"
|
:model-value="this.newKeyData.ExpiredAt"
|
||||||
|
Reference in New Issue
Block a user