This commit is contained in:
Donald Zou 2025-06-06 15:49:55 +08:00
parent 6cb30bcd7f
commit c6af129960
12 changed files with 196 additions and 35 deletions

View File

@ -81,7 +81,10 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
if session.get('username') is None:
return ResponseObject(False, "Sign in status is invalid", status_code=401)
session['totpVerified'] = True
return ResponseObject(True)
return ResponseObject(True, data={
"Email": session.get('username'),
"Profile": DashboardClients.GetClientProfile(session.get("ClientID"))
})
return ResponseObject(status, msg)
@client.get(prefix)

View File

@ -60,6 +60,13 @@ class DashboardClients:
self.dashboardClientsTable.c.DeletedDate is None)
).mappings().fetchall()
def GetClientProfile(self, ClientID):
with self.engine.connect() as conn:
return dict(conn.execute(
self.dashboardClientsInfoTable.select().where(
self.dashboardClientsInfoTable.c.ClientID == ClientID
)
).mappings().fetchone())
def SignIn(self, Email, Password) -> tuple[bool, str]:
if not all([Email, Password]):

View File

@ -74,10 +74,8 @@ class DashboardClientsPeerAssignment:
def GetAssignedPeers(self, ClientID):
peers = []
assigned = list(
filter(lambda e:
assigned = filter(lambda e:
e['ClientID'] == ClientID, self.assignments)
)
for a in assigned:
peer = filter(lambda e : e.id == a['PeerID'],
@ -97,6 +95,4 @@ class DashboardClientsPeerAssignment:
'configuration_name': a['ConfigurationName'],
'peer_configuration_data': p.downloadPeer()
})
print(peers)
return peers

View File

@ -1,6 +1,6 @@
<script setup>
import './assets/main.css'
import NotificationList from "@/components/notification/notificationList.vue";
import NotificationList from "@/components/Notification/notificationList.vue";
</script>
<template>

View File

@ -0,0 +1,82 @@
<script setup>
import {ref} from "vue";
import ConfigurationQRCode from "@/components/Configuration/configurationQRCode.vue";
const props = defineProps([
'config'
])
const showQRCode = ref(false)
</script>
<template>
<div class="card shadow rounded-3">
<div class="card-header d-flex align-items-center">
<div>
<small v-if="props.config.status === 'stopped'">
<i class="bi bi-lightbulb text-secondary me-2"></i>
</small>
<small v-else>
<i class="bi bi-lightbulb-fill text-success me-2"></i>
</small>
<small style="word-break: break-all">
{{ props.config.name }}
</small>
</div>
<div class="ms-auto d-flex gap-2 button-group">
<a role="button" class="px-2 py-1 text-white rounded-3" aria-label="Download Configuration">
<i class="bi bi-download"></i>
</a>
<a role="button"
@click="showQRCode = true"
class="px-2 py-1 text-white rounded-3" aria-label="Display QR Code">
<i class="bi bi-qr-code"></i>
</a>
<Transition name="app">
<ConfigurationQRCode
v-if="showQRCode"
@back="showQRCode = false"
:qrcode-data="config.peer_configuration_data.file"></ConfigurationQRCode>
</Transition>
</div>
</div>
<div class="card-body">
<div>
<small class="d-block text-muted" style="font-size: 0.8rem">Public Key</small>
<small>
<samp style="word-break: break-word">{{ props.config.id }}</samp>
</small>
</div>
<hr>
<div>
<h6 class="text-center">Data Usage</h6>
<div class="row text-center">
<div class="col-4">
<small class="d-block text-muted">
Total
</small>
<small>3.20 GB / 4GB</small>
</div>
<div class="col-4">
<small class="d-block text-muted">
Received
</small>
<small>3.20 GB</small>
</div>
<div class="col-4">
<small class="d-block text-muted">
Sent
</small>
<small>3.20 GB</small>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.button-group a:hover{
background-color: #ffffff20;
}
</style>

View File

@ -0,0 +1,33 @@
<script setup>
import * as uuid from "uuid";
import {onMounted} from "vue";
import QRCode from "qrcode";
import Qrcode from "@/components/SignIn/qrcode.vue";
const props = defineProps([
'qrcodeData'
])
const emits = defineEmits([
'back'
])
</script>
<template>
<div class="p-2 position-fixed top-0 start-0 vw-100 vh-100 d-flex qrcodeContainer">
<div class="m-auto d-flex gap-3 flex-column">
<a role="button" @click="emits('back')">
<i class="me-2 bi bi-chevron-left"></i> Back
</a>
<Qrcode :content="props.qrcodeData" ></Qrcode>
</div>
</div>
</template>
<style scoped>
.qrcodeContainer{
background-color: #00000050;
backdrop-filter: blur(8px);
}
</style>

View File

@ -1,6 +1,6 @@
<script setup>
import {clientStore} from "@/stores/clientStore.js";
import Notification from "@/components/notification/notification.vue";
import Notification from "@/components/Notification/notification.vue";
import {computed, onMounted} from "vue";
const store = clientStore()
const notifications = computed(() => {

View File

@ -1,19 +1,21 @@
<script setup>
import {onMounted} from "vue";
import QRCode from "qrcode";
import * as uuid from "uuid";
const props = defineProps([
"content"
])
const id = uuid.v4().toString()
onMounted(() => {
QRCode.toCanvas(document.getElementById('qrcode'), props.content, function (error) {})
QRCode.toCanvas(document.getElementById(`qrcode_${id}`), props.content, function (error) {})
})
</script>
<template>
<div>
<canvas id="qrcode" class="rounded-3 shadow"></canvas>
<canvas :id="'qrcode_' + id" class="rounded-3 shadow"></canvas>
</div>
</template>

View File

@ -1,5 +1,5 @@
<script setup async>
import {computed, onMounted, reactive, ref} from "vue";
import {computed, onMounted, reactive, ref, watch} from "vue";
import axios from "axios";
import {axiosPost, requestURl} from "@/utilities/request.js";
import {useRouter} from "vue-router";
@ -45,13 +45,8 @@ onMounted(() => {
})
const emits = defineEmits(['clearToken'])
onMounted(() => {
})
const verify = async (e) => {
e.preventDefault()
if (e) e.preventDefault()
if (formFilled){
loading.value = true
const data = await axiosPost('/api/signin/totp', {
@ -61,6 +56,7 @@ const verify = async (e) => {
loading.value = false
if (data){
if (data.status){
store.clientProfile = data.data
router.push('/')
}else{
store.newNotification(data.message, "danger")
@ -71,6 +67,10 @@ const verify = async (e) => {
}
}
}
watch(formFilled, () => {
verify()
})
</script>
<template>

View File

@ -1,11 +1,19 @@
import {defineStore} from "pinia";
import {ref} from "vue";
import {onMounted, reactive, ref} from "vue";
import {v4} from "uuid"
import dayjs from "dayjs";
import {axiosGet} from "@/utilities/request.js";
export const clientStore = defineStore('clientStore', () => {
const notifications = ref([])
const configurations = ref([])
const clientProfile = reactive({
Email: "",
Profile: {}
})
function newNotification(content, status) {
notifications.value.push({
id: v4().toString(),
@ -16,7 +24,15 @@ export const clientStore = defineStore('clientStore', () => {
})
}
async function getConfigurations(){
const data = await axiosGet("/api/configurations")
if (data){
configurations.value = data.data
}else{
newNotification("Failed to fetch configurations", "danger")
}
}
return {
notifications, newNotification
notifications, newNotification, getConfigurations, configurations, clientProfile
}
})

View File

@ -1,14 +1,26 @@
<script setup>
import {onMounted} from "vue";
<script setup async>
import {computed, onMounted, ref} from "vue";
import {axiosGet} from "@/utilities/request.js";
import {clientStore} from "@/stores/clientStore.js";
import Configuration from "@/components/Configuration/configuration.vue";
const store = clientStore()
const loading = ref(true)
const loadConfigurations = async () => {
await store.getConfigurations()
}
const configurations = computed(() => {
return store.configurations
});
onMounted(async () => {
const data = await axiosGet("/api/configurations")
if (data){
console.log(data)
}else{
// loading.value = true;
await loadConfigurations();
loading.value = false;
}
})
</script>
@ -33,13 +45,23 @@ onMounted(async () => {
</RouterLink>
</li>
</ul>
<div class="d-flex flex-column gap-3">
<Transition name="app" mode="out-in">
<div class="d-flex flex-column gap-3" v-if="!loading">
<div class="px-3 border-bottom py-4">
<h6>Hi donaldzou@live.hk!</h6>
<h5 class="mb-0">You have <strong>3</strong> configurations available</h5>
<h5 class="mb-0">You have <strong>
{{ configurations.length }}
</strong> configuration{{ configurations.length > 1 ? 's':''}} available</h5>
</div>
<div class="px-3">
<Configuration v-for="config in configurations" :config="config"></Configuration>
</div>
<div></div>
</div>
<div v-else class="d-flex py-4">
<div class="spinner-border m-auto"></div>
</div>
</Transition>
</div>
</template>