Finished Index

This commit is contained in:
Donald Zou 2025-06-03 17:18:18 +08:00
parent c35d22a82f
commit e9730f24a0
9 changed files with 114 additions and 51 deletions

View File

@ -53,6 +53,13 @@ def createClientBlueprint(wireguardConfigurations: dict[WireguardConfiguration],
session['totpVerified'] = False session['totpVerified'] = False
return ResponseObject(status, msg) return ResponseObject(status, msg)
@client.get(f'{prefix}/api/signout')
def ClientAPI_SignOut():
session.pop('username')
session.pop('role')
session.pop('totpVerified')
return ResponseObject(True)
@client.get(f'{prefix}/api/signin/totp') @client.get(f'{prefix}/api/signin/totp')
def ClientAPI_SignIn_TOTP(): def ClientAPI_SignIn_TOTP():
token = request.args.get('Token', None) token = request.args.get('Token', None)

View File

@ -6,10 +6,11 @@ import NotificationList from "@/components/notification/notificationList.vue";
<template> <template>
<div data-bs-theme="dark" class="text-body bg-body w-100 h-100"> <div data-bs-theme="dark" class="text-body bg-body w-100 h-100">
<div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll"> <div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll">
<div class="m-auto p-5 bg-body-tertiary rounded-4 shadow-lg border" style="width: 700px"> <div class="mx-auto my-sm-auto bg-body-tertiary rounded-4 shadow-lg border position-relative"
style="width: 700px">
<Suspense> <Suspense>
<RouterView v-slot="{ Component }"> <RouterView v-slot="{ Component }">
<Transition name="app" mode="out-in" type="transition" appear> <Transition name="app" type="transition" mode="out-in">
<Component :is="Component"></Component> <Component :is="Component"></Component>
</Transition> </Transition>
</RouterView> </RouterView>

View File

@ -76,11 +76,19 @@
.app-enter-active, .app-enter-active,
.app-leave-active { .app-leave-active {
transition: all 0.3s cubic-bezier(0.82, 0.58, 0.17, 1); transition: all 0.3s cubic-bezier(0.82, 0.58, 0.17, 1);
} }
.app-enter-from, .app-enter-from,
.app-leave-to{ .app-leave-to{
opacity: 0; opacity: 0;
filter: blur(5px); filter: blur(5px);
}
.app-enter-from{
transform: scale(1.03);
}
.app-leave-to{
transform: scale(0.97); transform: scale(0.97);
} }

View File

@ -82,15 +82,12 @@ if (route.query.Email){
<button <button
:disabled="!formFilled || loading" :disabled="!formFilled || loading"
class="btn btn-primary rounded-3 btn-brand px-3 py-2"> class="btn btn-primary rounded-3 btn-brand px-3 py-2">
<Transition name="slide-right" mode="out-in">
<span v-if="!loading" class="d-block"> <span v-if="!loading" class="d-block">
Continue <i class="ms-2 bi bi-arrow-right"></i> Continue <i class="ms-2 bi bi-arrow-right"></i>
</span> </span>
<span v-else class="d-block"> <span v-else class="d-block">
Loading... Loading...<i class="ms-2 spinner-border spinner-border-sm"></i>
<i class="spinner-border spinner-border-sm"></i>
</span> </span>
</Transition>
</button> </button>
</form> </form>
<div> <div>

View File

@ -14,7 +14,7 @@ const formData = reactive({
TOTP: "" TOTP: ""
}) })
const loading = ref(false) const loading = ref(true)
const replace = () => { const replace = () => {
formData.TOTP = formData.TOTP.replace(/\D/i, "") formData.TOTP = formData.TOTP.replace(/\D/i, "")
} }
@ -25,12 +25,14 @@ const formFilled = computed(() => {
const store = clientStore() const store = clientStore()
const router = useRouter() const router = useRouter()
await axios.get(requestURl('/api/signin/totp'), { onMounted(() => {
axios.get(requestURl('/api/signin/totp'), {
params: { params: {
Token: props.totpToken Token: props.totpToken
} }
}).then(res => { }).then(res => {
let data = res.data let data = res.data
loading.value = false
if (data.status){ if (data.status){
if (data.message){ if (data.message){
totpKey.value = data.message totpKey.value = data.message
@ -40,6 +42,7 @@ await axios.get(requestURl('/api/signin/totp'), {
router.push('/signin') router.push('/signin')
} }
}) })
})
const emits = defineEmits(['clearToken']) const emits = defineEmits(['clearToken'])
@ -117,14 +120,13 @@ const verify = async (e) => {
<button <button
:disabled="!formFilled || loading" :disabled="!formFilled || loading"
class="btn btn-primary rounded-3 btn-brand px-3 py-2"> class="btn btn-primary rounded-3 btn-brand px-3 py-2">
<Transition name="slide-right" mode="out-in">
<span v-if="!loading" class="d-block"> <span v-if="!loading" class="d-block">
Continue <i class="ms-2 bi bi-arrow-right"></i> Continue <i class="ms-2 bi bi-arrow-right"></i>
</span> </span>
<span v-else class="d-block"> <span v-else class="d-block">
<i class="spinner-border spinner-border-sm"></i> Loading...
<i class="ms-2 spinner-border spinner-border-sm"></i>
</span> </span>
</Transition>
</button> </button>
</div> </div>

View File

@ -26,22 +26,38 @@ const router = createRouter({
path: '/signup', path: '/signup',
component: SignUp, component: SignUp,
name: "Sign Up" name: "Sign Up"
},
{
path: '/signout',
name: "Sign Out"
} }
] ]
}) })
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
const store = clientStore()
if (to.path === '/signout'){
await axios.get(requestURl('/api/signout')).then(() => {
next('/signin')
}).catch(() => {
next('/signin')
});
store.newNotification("Sign in session ended, please sign in again", "warning")
}else{
if (to.meta.auth){ if (to.meta.auth){
await axios.get(requestURl('/api/validateAuthentication')).then(res => { await axios.get(requestURl('/api/validateAuthentication')).then(res => {
next() next()
}).catch(() => { }).catch(() => {
const store = clientStore()
store.newNotification("Sign in session ended, please sign in again", "warning") store.newNotification("Sign in session ended, please sign in again", "warning")
next('/signin') next('/signin')
}) })
}else{ }else{
next() next()
} }
}
}) })
router.afterEach((to, from, next) => { router.afterEach((to, from, next) => {

View File

@ -3,11 +3,45 @@
</script> </script>
<template> <template>
<h1> <div class="">
Index PAge <ul class="nav gap-0 border-bottom">
</h1> <li class="nav-item">
<a class="nav-link text-body border-start-0" aria-current="page" href="#">
<strong>WGDashboard Client</strong>
</a>
</li>
<li class="nav-item ms-auto">
<a class="nav-link text-body" aria-current="page" href="#">
<i class="bi bi-gear-fill me-sm-2"></i>
<span>Settings</span>
</a>
</li>
<li class="nav-item">
<RouterLink to="/signout" class="nav-link text-danger" aria-current="page">
<i class="bi bi-box-arrow-left me-sm-2"></i>
<span>Sign Out</span>
</RouterLink>
</li>
</ul>
<div class="d-flex flex-column gap-3">
<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>
</div>
<div></div>
</div>
</div>
</template> </template>
<style scoped> <style scoped>
.nav-link{
padding: 1rem 1rem;
border-left: 1px solid var(--bs-border-color)
}
@media screen and (max-width: 576px) {
.nav-link span{
display: none;
}
}
</style> </style>

View File

@ -8,7 +8,7 @@ const checkTotp = ref("")
</script> </script>
<template> <template>
<div> <div class="p-3 p-sm-5">
<Transition name="app" mode="out-in"> <Transition name="app" mode="out-in">
<SignInForm <SignInForm
@totpToken="token => { checkTotp = token }" @totpToken="token => { checkTotp = token }"

View File

@ -68,7 +68,7 @@ onMounted(() => {
</script> </script>
<template> <template>
<div> <div class="p-3 p-sm-5">
<h1>Sign Up</h1> <h1>Sign Up</h1>
<p>to use WGDashboard Client</p> <p>to use WGDashboard Client</p>
<form class="mt-4 d-flex flex-column gap-3" @submit="e => signUp(e)"> <form class="mt-4 d-flex flex-column gap-3" @submit="e => signUp(e)">
@ -122,7 +122,6 @@ onMounted(() => {
<button <button
:disabled="!formFilled || !validatePassword || loading" :disabled="!formFilled || !validatePassword || loading"
class=" btn btn-primary rounded-3 btn-brand px-3 py-2"> class=" btn btn-primary rounded-3 btn-brand px-3 py-2">
<Transition name="slide-right" mode="out-in">
<span v-if="!loading" class="d-block"> <span v-if="!loading" class="d-block">
Continue <i class="ms-2 bi bi-arrow-right"></i> Continue <i class="ms-2 bi bi-arrow-right"></i>
</span> </span>
@ -130,7 +129,6 @@ onMounted(() => {
Loading... Loading...
<i class="spinner-border spinner-border-sm"></i> <i class="spinner-border spinner-border-sm"></i>
</span> </span>
</Transition>
</button> </button>
</form> </form>
<div> <div>