This commit is contained in:
Donald Zou 2025-06-19 16:51:59 +08:00
parent 85d4b8c487
commit d80eb03707
8 changed files with 150 additions and 38 deletions

View File

@ -101,4 +101,9 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
def ClientAPI_Configurations(): def ClientAPI_Configurations():
return ResponseObject(True, data=DashboardClients.GetClientAssignedPeers(session['ClientID'])) return ResponseObject(True, data=DashboardClients.GetClientAssignedPeers(session['ClientID']))
@client.get(f'{prefix}/api/settings/getClientProfile')
@login_required
def ClientAPI_Settings_GetClientProfile():
return ResponseObject(data=DashboardClients.GetClientProfile(session['ClientID']))
return client return client

View File

@ -63,7 +63,9 @@ class DashboardClients:
def GetClientProfile(self, ClientID): def GetClientProfile(self, ClientID):
with self.engine.connect() as conn: with self.engine.connect() as conn:
return dict(conn.execute( return dict(conn.execute(
self.dashboardClientsInfoTable.select().where( db.select(
*[c for c in self.dashboardClientsInfoTable.c if c.name != 'ClientID']
).where(
self.dashboardClientsInfoTable.c.ClientID == ClientID self.dashboardClientsInfoTable.c.ClientID == ClientID
) )
).mappings().fetchone()) ).mappings().fetchone())

View File

@ -0,0 +1,72 @@
<script setup>
import {clientStore} from "@/stores/clientStore.js";
import {reactive} from "vue";
const store = clientStore()
const ProfileLabels = {
Firstname: "First Name",
Lastname: "Last Name"
}
const Password = reactive({
CurrentPassword: "",
NewPassword: "",
RepeatNewPassword: ""
})
const ResetPasswordFields = () => {
Password.CurrentPassword = ""
Password.NewPassword = ""
Password.RepeatNewPassword = ""
}
</script>
<template>
<div class="d-flex flex-column gap-4 p-3">
<div>
<h5>
Profile
</h5>
<div class="row g-2">
<div class="col-sm-6" v-for="(val, key) in store.clientProfile.Profile">
<label :for="key" class="text-muted form-label">
<small>{{ ProfileLabels[key] }}</small>
</label>
<input :id="key" class="form-control rounded-3" v-model="store.clientProfile.Profile[key]">
</div>
</div>
</div>
<form @submit="undefined" @reset="ResetPasswordFields()">
<h5>
Update Password
</h5>
<div class="row g-2 mb-3">
<div class="col-sm-12">
<label class="text-muted form-label" for="CurrentPassword">
<small>Current Password</small>
</label>
<input class="form-control rounded-3" type="password" autocomplete="current-password" id="CurrentPassword" v-model="Password.CurrentPassword">
</div>
<div class="col-sm-6">
<label class="text-muted form-label" for="NewPassword">
<small>New Password</small>
</label>
<input class="form-control rounded-3" type="password" id="NewPassword" autocomplete="new-password" v-model="Password.NewPassword">
</div>
<div class="col-sm-6">
<label class="text-muted form-label" for="RepeatNewPassword">
<small>Repeat New Password</small>
</label>
<input class="form-control rounded-3" type="password" id="RepeatNewPassword" autocomplete="new-password" v-model="Password.RepeatNewPassword">
</div>
</div>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-secondary rounded-3 ms-auto" type="reset">Clear</button>
<button class="btn btn-sm btn-danger rounded-3" type="submit">Update</button>
</div>
</form>
</div>
</template>
<style scoped>
</style>

View File

@ -56,7 +56,7 @@ const verify = async (e) => {
loading.value = false loading.value = false
if (data){ if (data){
if (data.status){ if (data.status){
console.log(data.data) store.clientProfile = data.data;
router.push('/') router.push('/')
}else{ }else{
store.newNotification(data.message, "danger") store.newNotification(data.message, "danger")

View File

@ -5,6 +5,7 @@ import SignUp from "@/views/signup.vue";
import axios from "axios"; import axios from "axios";
import {axiosGet, requestURl} from "@/utilities/request.js"; import {axiosGet, requestURl} from "@/utilities/request.js";
import {clientStore} from "@/stores/clientStore.js"; import {clientStore} from "@/stores/clientStore.js";
import Settings from "@/views/settings.vue";
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
@ -17,6 +18,14 @@ const router = createRouter({
}, },
name: "Home" name: "Home"
}, },
{
path: '/settings',
component: Settings,
meta: {
auth: true
},
name: "Settings"
},
{ {
path: '/signin', path: '/signin',
component: SignIn, component: SignIn,
@ -35,10 +44,7 @@ const router = createRouter({
}) })
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
const store = clientStore() const store = clientStore()
if (to.path === '/signout'){ if (to.path === '/signout'){
await axios.get(requestURl('/api/signout')).then(() => { await axios.get(requestURl('/api/signout')).then(() => {
next('/signin') next('/signin')
}).catch(() => { }).catch(() => {
@ -58,8 +64,6 @@ router.beforeEach(async (to, from, next) => {
next() next()
} }
} }
}) })
router.afterEach((to, from, next) => { router.afterEach((to, from, next) => {

View File

@ -4,35 +4,40 @@ import {v4} from "uuid"
import dayjs from "dayjs"; import dayjs from "dayjs";
import {axiosGet} from "@/utilities/request.js"; import {axiosGet} from "@/utilities/request.js";
export const clientStore = defineStore('clientStore', {
export const clientStore = defineStore('clientStore', () => { state: () => ({
const notifications = ref([]) notifications: [],
const configurations = ref([]) configurations: [],
const clientProfile = reactive({ clientProfile: {
Email: "", Email: "",
Profile: {} Profile: {}
}) }
}),
actions: {
function newNotification(content, status) { newNotification(content, status){
notifications.value.push({ this.notifications.push({
id: v4().toString(), id: v4().toString(),
status: status, status: status,
content: content, content: content,
time: dayjs(), time: dayjs(),
show: true show: true
}) })
},
async getClientProfile(){
const data = await axiosGet('/api/settings/getClientProfile')
if (data){
this.clientProfile.Profile = data.data
}else{
this.newNotification("Failed to fetch client profile", "danger")
} }
},
async function getConfigurations(){ async getConfigurations(){
const data = await axiosGet("/api/configurations") const data = await axiosGet("/api/configurations")
if (data){ if (data){
configurations.value = data.data this.configurations = data.data
}else{ }else{
newNotification("Failed to fetch configurations", "danger") this.newNotification("Failed to fetch configurations", "danger")
} }
} }
return {
notifications, newNotification, getConfigurations, configurations, clientProfile
} }
}) })

View File

@ -29,10 +29,10 @@ onMounted(async () => {
<strong>WGDashboard Client</strong> <strong>WGDashboard Client</strong>
</a> </a>
<div class="ms-auto px-3 d-flex gap-2 nav-links"> <div class="ms-auto px-3 d-flex gap-2 nav-links">
<a class=" text-body btn btn-outline-body rounded-3 ms-auto btn-sm" aria-current="page" href="#"> <RouterLink to="/settings" class=" text-body btn btn-outline-body rounded-3 ms-auto btn-sm" aria-current="page" href="#">
<i class="bi bi-gear-fill me-sm-2"></i> <i class="bi bi-gear-fill me-sm-2"></i>
<span>Settings</span> <span>Settings</span>
</a> </RouterLink>
<RouterLink to="/signout" class="btn btn-outline-danger rounded-3 btn-sm" aria-current="page"> <RouterLink to="/signout" class="btn btn-outline-danger rounded-3 btn-sm" aria-current="page">
<i class="bi bi-box-arrow-left me-sm-2"></i> <i class="bi bi-box-arrow-left me-sm-2"></i>
<span>Sign Out</span> <span>Sign Out</span>

View File

@ -0,0 +1,24 @@
<script setup async>
import {clientStore} from "@/stores/clientStore.js";
import Profile from "@/components/Settings/profile.vue";
const store = clientStore()
await store.getClientProfile();
</script>
<template>
<div class="p-sm-3">
<div class="w-100 d-flex align-items-center p-3">
<RouterLink to="/" class=" text-body btn btn-outline-body rounded-3 btn-sm" aria-current="page" href="#">
<i class="bi bi-chevron-left me-sm-2"></i>
<span>Back</span>
</RouterLink>
<strong class="ms-auto">Settings</strong>
</div>
<Profile></Profile>
</div>
</template>
<style scoped>
</style>