Refactoring configuration list

This commit is contained in:
Donald Zou 2024-12-30 20:30:09 +08:00
parent ccfc1ad166
commit cd9d17ab18
10 changed files with 630 additions and 24 deletions

View File

@ -563,7 +563,6 @@ class WireguardConfiguration:
return False, err
return True, None
def __parseConfigurationFile(self):
with open(self.configPath, 'r') as f:
original = [l.rstrip("\n") for l in f.readlines()]
@ -1094,8 +1093,7 @@ class WireguardConfiguration:
"filename": f'{self.Name}_{time}.conf',
"backupDate": datetime.now().strftime("%Y%m%d%H%M%S")
}
def getBackups(self, databaseContent: bool = False) -> list[dict[str: str, str: str, str: str]]:
backups = []
@ -1171,9 +1169,6 @@ class WireguardConfiguration:
return True, zip
def updateConfigurationSettings(self, newData: dict) -> tuple[bool, str]:
if self.Status:
self.toggleConfiguration()
@ -1208,6 +1203,11 @@ class WireguardConfiguration:
status, msg = self.toggleConfiguration()
if not status:
return False, msg
for i in allowEdit:
if isinstance(getattr(self, i), bool):
setattr(self, i, _strToBool(newData[i]))
else:
setattr(self, i, str(newData[i]))
return True, ""
def deleteConfiguration(self):
@ -1274,6 +1274,29 @@ class WireguardConfiguration:
break
return True, availableAddress
def getRealtimeTrafficUsage(self):
stats = psutil.net_io_counters(pernic=True, nowrap=True)
if self.Name in stats.keys():
stat = stats[self.Name]
recv1 = stat.bytes_recv
sent1 = stat.bytes_sent
time.sleep(1)
stats = psutil.net_io_counters(pernic=True, nowrap=True)
if self.Name in stats.keys():
stat = stats[self.Name]
recv2 = stat.bytes_recv
sent2 = stat.bytes_sent
net_in = round((recv2 - recv1) / 1024 / 1024, 3)
net_out = round((sent2 - sent1) / 1024 / 1024, 3)
return {
"sent": net_out,
"recv": net_in
}
else:
return { "sent": 0, "recv": 0 }
else:
return { "sent": 0, "recv": 0 }
"""
AmneziaWG Configuration
"""
@ -2039,7 +2062,6 @@ def sqlSelect(statement: str, paramters: tuple = ()) -> sqlite3.Cursor:
result = []
with sqldb:
try:
print("[WGDashboard] SQLite Select" + " | Statement: " + statement)
cursor = sqldb.cursor()
result = cursor.execute(statement, paramters)
except Exception as error:
@ -2327,6 +2349,13 @@ def API_renameWireguardConfiguration():
WireguardConfigurations[data.get("NewConfigurationName")] = WireguardConfiguration(data.get("NewConfigurationName"))
return ResponseObject(status, message)
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRealtimeTraffic')
def API_getWireguardConfigurationRealtimeTraffic():
configurationName = request.args.get('configurationName')
if configurationName is None or configurationName not in WireguardConfigurations.keys():
return ResponseObject(False, "Configuration does not exist")
return ResponseObject(data=WireguardConfigurations[configurationName].getRealtimeTrafficUsage())
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
def API_getWireguardConfigurationBackup():
configurationName = request.args.get('configurationName')

View File

@ -24,12 +24,7 @@ onBeforeUnmount(() =>{
</script>
<template>
<div class="text-center">
<small v-if="peerListLength < showPeersCount" class="text-muted">
That's it, no more peers
</small>
<div style="margin-bottom: 20px; height: 1px" id="loadMore"></div>
</div>
<div style="margin-bottom: 20px; height: 1px" id="loadMore"></div>
</template>
<style scoped>

View File

@ -79,7 +79,7 @@ export default {
Line,
Bar
},
setup(){
async setup(){
const dashboardConfigurationStore = DashboardConfigurationStore();
const wireguardConfigurationStore = WireguardConfigurationsStore();
const interval = ref(undefined)
@ -284,9 +284,6 @@ export default {
this.getPeers()
}, parseInt(this.dashboardConfigurationStore.Configuration.Server.dashboard_refresh_interval))
},
disableMutationObserver(){
}
},
computed: {
configurationSummary(){

View File

@ -0,0 +1,263 @@
<script setup>
import {computed, defineComponent, onBeforeUnmount, onMounted, reactive, ref, useTemplateRef, watch} from "vue";
import {fetchGet} from "@/utilities/fetch.js";
import { Line, Bar } from 'vue-chartjs'
import {
Chart,
LineElement,
BarElement,
BarController,
LineController,
LinearScale,
Legend,
Title,
Tooltip,
CategoryScale,
PointElement
} from 'chart.js';
Chart.register(
LineElement,
BarElement,
BarController,
LineController,
LinearScale,
Legend,
Title,
Tooltip,
CategoryScale,
PointElement
);
import LocaleText from "@/components/text/localeText.vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import dayjs from "dayjs";
const props = defineProps({
configurationPeers: Array,
configurationInfo: Object
})
const historySentData = ref({
timestamp: [],
data: []
})
const historyReceivedData = ref({
timestamp: [],
data: []
})
const dashboardStore = DashboardConfigurationStore()
const fetchRealtimeTrafficInterval = ref(undefined)
const fetchRealtimeTraffic = async () => {
await fetchGet("/api/getWireguardConfigurationRealtimeTraffic", {
configurationName: "wg1"
}, (res) => {
let timestamp = dayjs().format("hh:mm:ss A")
historySentData.value.timestamp.push(timestamp)
historySentData.value.data.push(res.data.sent)
historyReceivedData.value.timestamp.push(timestamp)
historyReceivedData.value.data.push(res.data.recv)
})
}
const toggleFetchRealtimeTraffic = () => {
clearInterval(fetchRealtimeTrafficInterval.value)
fetchRealtimeTrafficInterval.value = undefined;
if (props.configurationInfo.Status){
fetchRealtimeTrafficInterval.value = setInterval(() => {
fetchRealtimeTraffic()
}, parseInt(dashboardStore.Configuration.Server.dashboard_refresh_interval))
}
}
onMounted(() => {
toggleFetchRealtimeTraffic()
})
watch(() => props.configurationInfo.Status, () => {
toggleFetchRealtimeTraffic()
})
watch(() => dashboardStore.Configuration.Server.dashboard_refresh_interval, () => {
toggleFetchRealtimeTraffic()
})
onBeforeUnmount(() => {
clearInterval(fetchRealtimeTrafficInterval.value)
fetchRealtimeTrafficInterval.value = undefined;
})
const peersDataUsageChartData = computed(() => {
return {
labels: props.configurationPeers.map(x => {
if (x.name) return x.name
return `Untitled Peer - ${x.id}`
}),
datasets: [{
label: 'Total Data Usage',
data: props.configurationPeers.map(x => x.cumu_data + x.total_data),
backgroundColor: props.configurationPeers.map(x => `#ffc107`),
barThickness: 50,
tooltip: {
callbacks: {
label: (tooltipItem) => {
return `${tooltipItem.formattedValue} GB`
}
}
}
}]
}
})
const peersRealtimeSentData = computed(() => {
return {
labels: [...historySentData.value.timestamp],
datasets: [
{
label: 'Data Sent',
data: [...historySentData.value.data],
fill: false,
borderColor: '#198754',
backgroundColor: '#198754',
tension: 0
},
],
}
})
const peersRealtimeReceivedData = computed(() => {
return {
labels: [...historyReceivedData.value.timestamp],
datasets: [
{
label: 'Data Received',
data: [...historyReceivedData.value.data],
fill: false,
borderColor: '#0d6efd',
tension: 0
},
],
}
})
const peersDataUsageChartOption = computed(() => {
return {
responsive: true,
plugins: {
legend: {
display: false
}
},
scales: {
x: {
ticks: {
display: false,
},
grid: {
display: false
},
},
y:{
ticks: {
callback: (val, index) => {
return `${val} GB`
}
},
grid: {
display: false
},
}
}
}
})
const realtimePeersChartOption = computed(() => {
return {
responsive: true,
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: (tooltipItem) => {
return `${tooltipItem.formattedValue} MB/s`
}
}
}
},
scales: {
x: {
ticks: {
display: false,
},
grid: {
display: false
},
},
y:{
ticks: {
callback: (val, index) => {
return `${Math.round((val + Number.EPSILON) * 1000) / 1000
} MB/s`
}
},
grid: {
display: false
},
}
}
}
})
</script>
<template>
<div class="row gx-2 gy-2 mb-3">
<div class="col-12">
<div class="card rounded-3 bg-transparent " style="height: 270px">
<div class="card-header bg-transparent border-0">
<small class="text-muted">
<LocaleText t="Peers Data Usage"></LocaleText>
</small></div>
<div class="card-body pt-1">
<Bar
:data="peersDataUsageChartData"
:options="peersDataUsageChartOption"
style="width: 100%; height: 200px; max-height: 200px"></Bar>
</div>
</div>
</div>
<div class="col-sm col-lg-6">
<div class="card rounded-3 bg-transparent " style="height: 270px">
<div class="card-header bg-transparent border-0"><small class="text-muted">
<LocaleText t="Real Time Received Data Usage"></LocaleText>
</small></div>
<div class="card-body pt-1">
<Line
:options="realtimePeersChartOption"
:data="peersRealtimeReceivedData"
style="width: 100%; height: 200px; max-height: 200px"
></Line>
</div>
</div>
</div>
<div class="col-sm col-lg-6">
<div class="card rounded-3 bg-transparent " style="height: 270px">
<div class="card-header bg-transparent border-0"><small class="text-muted">
<LocaleText t="Real Time Sent Data Usage"></LocaleText>
</small></div>
<div class="card-body pt-1">
<Line
:options="realtimePeersChartOption"
:data="peersRealtimeSentData"
style="width: 100%; height: 200px; max-height: 200px"
></Line>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<script setup>
import {defineAsyncComponent} from "vue";
import LocaleText from "@/components/text/localeText.vue";
const DeleteConfigurationModal = defineAsyncComponent(() => import("@/components/configurationComponents/deleteConfiguration.vue"))
const ConfigurationBackupRestoreModal = defineAsyncComponent(() => import("@/components/configurationComponents/configurationBackupRestore.vue"))
const SelectPeersModal = defineAsyncComponent(() => import("@/components/configurationComponents/selectPeers.vue"))
const EditConfigurationModal = defineAsyncComponent(() => import("@/components/configurationComponents/editConfiguration.vue"))
const PeerShareLinkModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerShareLinkModal.vue"))
const PeerJobsLogsModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerJobsLogsModal.vue"))
const PeerJobsAllModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerJobsAllModal.vue"))
const PeerJobsModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerJobs.vue"))
const PeerQRCodeModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerQRCode.vue"))
const PeerConfigurationFileModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerConfigurationFile.vue"))
const PeerSettingsModal = defineAsyncComponent(() => import("@/components/configurationComponents/peerSettings.vue"))
</script>
<template>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,287 @@
<script setup async>
import {computed, onBeforeUnmount, ref, watch} from "vue";
import {useRoute} from "vue-router";
import {fetchGet} from "@/utilities/fetch.js";
import ProtocolBadge from "@/components/protocolBadge.vue";
import LocaleText from "@/components/text/localeText.vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
import PeerDataUsageCharts from "@/components/configurationComponents/peerListComponents/peerDataUsageCharts.vue";
const dashboardStore = DashboardConfigurationStore()
const wireguardConfigurationStore = WireguardConfigurationsStore()
const route = useRoute()
const configurationInfo = ref({})
const configurationPeers = ref([])
const configurationToggling = ref(false)
const configurationModals = ref({
peerSetting: {
modalOpen: false,
},
peerScheduleJobs:{
modalOpen: false,
},
peerQRCode: {
modalOpen: false,
},
peerConfigurationFile: {
modalOpen: false,
},
peerCreate: {
modalOpen: false
},
peerScheduleJobsAll: {
modalOpen: false
},
peerScheduleJobsLogs: {
modalOpen: false
},
peerShare:{
modalOpen: false,
},
editConfiguration: {
modalOpen: false
},
selectPeers: {
modalOpen: false
},
backupRestore: {
modalOpen: false
},
deleteConfiguration: {
modalOpen: false
},
editRawConfigurationFile: {
modalOpen: false
}
})
// Fetch Peer =====================================
const fetchPeerList = async () => {
await fetchGet("/api/getWireguardConfigurationInfo", {
configurationName: route.params.id
}, (res) => {
if (res.status){
configurationInfo.value = res.data.configurationInfo;
configurationPeers.value = res.data.configurationPeers;
configurationPeers.value.forEach(p => {
p.restricted = false
})
res.data.configurationRestrictedPeers.forEach(x => {
x.restricted = true;
configurationPeers.value.push(x)
})
}
})
}
await fetchPeerList()
// Fetch Peer Interval =====================================
const fetchPeerListInterval = ref(undefined)
const setFetchPeerListInterval = () => {
clearInterval(fetchPeerListInterval.value)
fetchPeerListInterval.value = setInterval(async () => {
await fetchPeerList()
}, parseInt(dashboardStore.Configuration.Server.dashboard_refresh_interval))
}
setFetchPeerListInterval()
onBeforeUnmount(() => {
clearInterval(fetchPeerListInterval.value);
fetchPeerListInterval.value = undefined;
})
watch(() => {
return dashboardStore.Configuration.Server.dashboard_refresh_interval
}, () => {
setFetchPeerListInterval()
})
// Toggle Configuration Method =====================================
const toggleConfiguration = async () => {
configurationToggling.value = true;
await fetchGet("/api/toggleWireguardConfiguration/", {
configurationName: configurationInfo.value.Name
}, (res) => {
if (res.status){
dashboardStore.newMessage("Server",
`${configurationInfo.value.Name} ${res.data ? 'is on':'is off'}`, "success")
}else{
dashboardStore.newMessage("Server", res.message, 'danger')
}
wireguardConfigurationStore.Configurations
.find(x => x.Name === configurationInfo.value.Name).Status = res.data
configurationInfo.value.Status = res.data
configurationToggling.value = false;
})
}
// Configuration Summary =====================================
const configurationSummary = computed(() => {
return {
connectedPeers: configurationPeers.value.filter(x => x.status === "running").length,
totalUsage: configurationPeers.value.length > 0 ?
configurationPeers.value.filter(x => !x.restricted)
.map(x => x.total_data + x.cumu_data).reduce((a, b) => a + b, 0).toFixed(4) : 0,
totalReceive: configurationPeers.value.length > 0 ?
configurationPeers.value.filter(x => !x.restricted)
.map(x => x.total_receive + x.cumu_receive).reduce((a, b) => a + b, 0).toFixed(4) : 0,
totalSent: configurationPeers.value.length > 0 ?
configurationPeers.value.filter(x => !x.restricted)
.map(x => x.total_sent + x.cumu_sent).reduce((a, b) => a + b, 0).toFixed(4) : 0
}
})
</script>
<template>
<div class="container-md" >
<div class="d-flex align-items-sm-center flex-column flex-sm-row gap-3">
<div>
<div class="text-muted d-flex align-items-center gap-2">
<h5 class="mb-0">
<ProtocolBadge :protocol="configurationInfo.Protocol"></ProtocolBadge>
</h5>
</div>
<div class="d-flex align-items-center gap-3">
<h1 class="mb-0 display-4"><samp>{{configurationInfo.Name}}</samp></h1>
</div>
</div>
<div class="ms-sm-auto d-flex gap-2 flex-column">
<div class="card rounded-3 bg-transparent ">
<div class="card-body py-2 d-flex align-items-center">
<small class="text-muted">
<LocaleText t="Status"></LocaleText>
</small>
<div class="dot ms-2" :class="{active: configurationInfo.Status}"></div>
<div class="form-check form-switch mb-0 ms-auto pe-0 me-0">
<label class="form-check-label" style="cursor: pointer" :for="'switch' + configurationInfo.id">
<LocaleText t="On" v-if="configurationInfo.Status && !configurationToggling"></LocaleText>
<LocaleText t="Off" v-else-if="!configurationInfo.Status && !configurationToggling"></LocaleText>
<span v-if="configurationToggling"
class="spinner-border spinner-border-sm ms-2" aria-hidden="true">
</span>
</label>
<input class="form-check-input"
style="cursor: pointer"
:disabled="configurationToggling"
type="checkbox" role="switch" :id="'switch' + configurationInfo.id"
@change="toggleConfiguration()"
v-model="configurationInfo.Status">
</div>
</div>
</div>
<div class="d-flex gap-2">
<RouterLink
to="create"
class="titleBtn py-2 text-decoration-none btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle ">
<i class="bi bi-plus-lg me-2"></i>
<LocaleText t="Peer"></LocaleText>
</RouterLink>
<button class="titleBtn py-2 text-decoration-none btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle "
@click="configurationModals.editConfiguration.modalOpen = true"
type="button" aria-expanded="false">
<i class="bi bi-gear-fill me-2"></i>
<LocaleText t="Configuration Settings"></LocaleText>
</button>
</div>
</div>
</div>
<hr>
<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="Address"></LocaleText>
</small></p>
{{configurationInfo.Address}}
</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="Listen Port"></LocaleText>
</small></p>
{{configurationInfo.ListenPort}}
</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>{{configurationInfo.PublicKey}}</samp>
</div>
</div>
</div>
</div>
<div class="row gx-2 gy-2 mb-2">
<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="Connected Peers"></LocaleText>
</small></p>
<strong class="h4">
{{configurationSummary.connectedPeers}} / {{configurationPeers.length}}
</strong>
</div>
<i class="bi bi-ethernet 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">{{configurationSummary.totalUsage}} 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">{{configurationSummary.totalReceive}} 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">{{configurationSummary.totalSent}} GB</strong>
</div>
<i class="bi bi-arrow-up ms-auto h2 text-muted"></i>
</div>
</div>
</div>
</div>
<PeerDataUsageCharts
:configurationPeers="configurationPeers"
:configurationInfo="configurationInfo"
></PeerDataUsageCharts>
</div>
</template>
<style scoped>
</style>

View File

@ -1,11 +1,14 @@
<script setup>
import {onBeforeUnmount, onMounted, ref} from "vue";
import {computed, onBeforeUnmount, onMounted, ref} from "vue";
import {fetchGet} from "@/utilities/fetch.js";
import LocaleText from "@/components/text/localeText.vue";
import CpuCore from "@/components/systemStatusComponents/cpuCore.vue";
import StorageMount from "@/components/systemStatusComponents/storageMount.vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
const data = ref(undefined)
const dashboardStore = DashboardConfigurationStore()
// const data = ref(undefined)
let interval = null;
onMounted(() => {
@ -21,9 +24,13 @@ onBeforeUnmount(() => {
const getData = () => {
fetchGet("/api/systemStatus", {}, (res) => {
data.value = res.data
dashboardStore.SystemStatus = res.data
})
}
const data = computed(() => {
return dashboardStore.SystemStatus
})
</script>
<template>

View File

@ -91,7 +91,7 @@ const router = createRouter({
{
name: "Peers List",
path: 'peers',
component: () => import('@/components/configurationComponents/peerList.vue')
component: () => import('@/components/configurationComponents/peerListNew.vue')
},
{
name: "Peers Create",

View File

@ -16,6 +16,7 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
Enable: false,
ServerList: {}
},
SystemStatus: undefined,
ActiveServerConfiguration: undefined,
IsElectronApp: false,
ShowNavBar: false,

View File

@ -1,12 +1,16 @@
<script setup>
import {onBeforeUnmount, onMounted, ref} from "vue";
import {computed, onBeforeUnmount, onMounted, ref} from "vue";
import {fetchGet} from "@/utilities/fetch.js";
import LocaleText from "@/components/text/localeText.vue";
import CpuCore from "@/components/systemStatusComponents/cpuCore.vue";
import StorageMount from "@/components/systemStatusComponents/storageMount.vue";
import Process from "@/components/systemStatusComponents/process.vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
const data = ref(undefined)
const dashboardStore = DashboardConfigurationStore()
const data = computed(() => {
return dashboardStore.SystemStatus
})
let interval = null;
onMounted(() => {
@ -22,7 +26,7 @@ onBeforeUnmount(() => {
const getData = () => {
fetchGet("/api/systemStatus", {}, (res) => {
data.value = res.data
dashboardStore.SystemStatus = res.data
})
}
</script>