mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-08-28 07:51:13 +00:00
I am giving up on using ORM...
Lets go back to the good old sql query days ;)
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<script>
|
||||
import {wgdashboardStore} from "@/stores/wgdashboardStore.js";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import ConfigurationCard from "@/components/configurationListComponents/configurationCard.vue";
|
||||
|
||||
export default {
|
||||
name: "configurationList",
|
||||
components: {ConfigurationCard},
|
||||
async setup(){
|
||||
const wireguardConfigurationsStore = WireguardConfigurationsStore();
|
||||
await wireguardConfigurationsStore.getConfigurations();
|
||||
@@ -25,24 +27,7 @@ export default {
|
||||
<p class="text-muted" v-if="this.wireguardConfigurationsStore.Configurations.length === 0">You don't have any WireGuard configurations yet. Please check the configuration folder or change it in "Settings". By default the folder is "/etc/wireguard".</p>
|
||||
|
||||
<div class="d-flex gap-3 flex-column" v-else >
|
||||
<RouterLink :to="'/configuration/' + c.Name"
|
||||
class="card conf_card rounded-3 shadow text-decoration-none" v-for="c in this.wireguardConfigurationsStore.Configurations" :key="c.Name">
|
||||
<div class="card-body d-flex align-items-center gap-3 flex-wrap">
|
||||
<h6 class="mb-0"><span class="dot" :class="{active: c.Status}"></span></h6>
|
||||
<h6 class="card-title mb-0"><samp>{{c.Name}}</samp></h6>
|
||||
<h6 class="mb-0 ms-auto">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<small class="me-2 text-muted">
|
||||
<strong>PUBLIC KEY</strong>
|
||||
</small>
|
||||
<small class="mb-0 d-block d-lg-inline-block ">
|
||||
<samp style="line-break: anywhere">{{c.PublicKey}}</samp>
|
||||
</small>
|
||||
</div>
|
||||
</RouterLink>
|
||||
<ConfigurationCard v-for="c in this.wireguardConfigurationsStore.Configurations" :key="c.Name" :c="c"></ConfigurationCard>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -0,0 +1,71 @@
|
||||
<script>
|
||||
import {fetchGet} from "@/utilities/fetch.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
|
||||
export default {
|
||||
name: "configurationCard",
|
||||
props: {
|
||||
c: {
|
||||
Name: String,
|
||||
Status: Boolean,
|
||||
PublicKey: String,
|
||||
PrivateKey: String
|
||||
|
||||
}
|
||||
},
|
||||
setup(){
|
||||
const dashboardConfigurationStore = DashboardConfigurationStore();
|
||||
return {dashboardConfigurationStore}
|
||||
},
|
||||
methods: {
|
||||
toggle(){
|
||||
fetchGet("/api/toggleWireguardConfiguration/", {
|
||||
configurationName: this.c.Name
|
||||
}, (res) => {
|
||||
if (res.status){
|
||||
this.dashboardConfigurationStore.newMessage("Server",
|
||||
`${this.c.Name} is ${res.data ? 'is on':'is off'}`)
|
||||
}else{
|
||||
this.dashboardConfigurationStore.newMessage("Server",
|
||||
res.message, 'danger')
|
||||
}
|
||||
this.c.Status = res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card conf_card rounded-3 shadow text-decoration-none">
|
||||
<RouterLink :to="'/configuration/' + c.Name" class="card-body d-flex align-items-center gap-3 flex-wrap text-decoration-none">
|
||||
<h6 class="mb-0"><span class="dot" :class="{active: c.Status}"></span></h6>
|
||||
<h6 class="card-title mb-0"><samp>{{c.Name}}</samp></h6>
|
||||
<h6 class="mb-0 ms-auto">
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</h6>
|
||||
</RouterLink>
|
||||
<div class="card-footer d-flex align-items-center">
|
||||
<small class="me-2 text-muted">
|
||||
<strong>PUBLIC KEY</strong>
|
||||
</small>
|
||||
<small class="mb-0 d-block d-lg-inline-block ">
|
||||
<samp style="line-break: anywhere">{{c.PublicKey}}</samp>
|
||||
</small>
|
||||
<div class="form-check form-switch ms-auto">
|
||||
<label class="form-check-label" :for="'switch' + c.PrivateKey">
|
||||
{{c.Status ? "On":"Off"}}
|
||||
</label>
|
||||
<input class="form-check-input"
|
||||
type="checkbox" role="switch" :id="'switch' + c.PrivateKey"
|
||||
@change="this.toggle()"
|
||||
v-model="c.Status"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -0,0 +1,32 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "message",
|
||||
props: {
|
||||
message: Object
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
this.message.show = false
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card shadow rounded-3 position-relative mb-2"
|
||||
:class="{
|
||||
'text-bg-danger': this.message.type === 'danger',
|
||||
'text-bg-success': this.message.type === 'success',
|
||||
'text-bg-warning': this.message.type === 'warning'}"
|
||||
:id="this.message.id"
|
||||
style="width: 400px">
|
||||
<div class="card-body">
|
||||
<small class="fw-bold d-block" style="text-transform: uppercase">FROM {{this.message.from}}</small>
|
||||
{{this.message.content}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -10,6 +10,7 @@ import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStor
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import Setup from "@/views/setup.vue";
|
||||
import NewConfiguration from "@/views/newConfiguration.vue";
|
||||
import Configuration from "@/views/configuration.vue";
|
||||
|
||||
const checkAuth = async () => {
|
||||
let result = false
|
||||
@@ -44,6 +45,11 @@ const router = createRouter({
|
||||
name: "New Configuration",
|
||||
path: '/new_configuration',
|
||||
component: NewConfiguration
|
||||
},
|
||||
{
|
||||
name: "Configuration",
|
||||
path: '/configuration/:id',
|
||||
component: Configuration
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import {defineStore} from "pinia";
|
||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
import {cookie} from "@/utilities/cookie.js";
|
||||
import {v4} from "uuid";
|
||||
|
||||
export const DashboardConfigurationStore = defineStore('DashboardConfigurationStore', {
|
||||
state: () => ({
|
||||
Configuration: undefined
|
||||
Configuration: undefined,
|
||||
Messages: []
|
||||
}),
|
||||
actions: {
|
||||
async getConfiguration(){
|
||||
@@ -23,6 +24,15 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
|
||||
await fetchGet("/api/signout", {}, (res) => {
|
||||
this.$router.go('/signin')
|
||||
});
|
||||
},
|
||||
newMessage(from, content, type){
|
||||
this.Messages.push({
|
||||
id: v4(),
|
||||
from: from,
|
||||
content: content,
|
||||
type: type,
|
||||
show: true
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
@@ -7,9 +7,7 @@ export const fetchGet = async (url, params=undefined, callback=undefined) => {
|
||||
})
|
||||
.then(x => x.json())
|
||||
.then(x => callback ? callback(x) : undefined)
|
||||
.catch(() => {
|
||||
alert("Error occurred! Check console")
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export const fetchPost = async (url, body, callback) => {
|
||||
|
15
src/static/app/src/views/configuration.vue
Normal file
15
src/static/app/src/views/configuration.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "configuration"
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-body">
|
||||
hiiii
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@@ -3,13 +3,19 @@ import Navbar from "@/components/navbar.vue";
|
||||
import {wgdashboardStore} from "@/stores/wgdashboardStore.js";
|
||||
import {WireguardConfigurations} from "@/models/WireguardConfigurations.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import Message from "@/components/messageCentreComponent/message.vue";
|
||||
|
||||
export default {
|
||||
name: "index",
|
||||
components: {Navbar},
|
||||
components: {Message, Navbar},
|
||||
async setup(){
|
||||
const dashboardConfigurationStore = DashboardConfigurationStore()
|
||||
return {dashboardConfigurationStore}
|
||||
},
|
||||
computed: {
|
||||
getMessages(){
|
||||
return this.dashboardConfigurationStore.Messages.filter(x => x.show)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -26,11 +32,20 @@ export default {
|
||||
</Transition>
|
||||
</RouterView>
|
||||
</Suspense>
|
||||
<div class="messageCentre text-body position-fixed">
|
||||
<TransitionGroup name="message" tag="div" class="position-relative">
|
||||
<Message v-for="m in getMessages.slice().reverse()"
|
||||
:message="m" :key="m.id"></Message>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.messageCentre{
|
||||
top: calc(50px + 1rem);
|
||||
right: 1rem;
|
||||
}
|
||||
</style>
|
@@ -2,6 +2,7 @@
|
||||
import {parse} from "cidr-tools";
|
||||
import '@/utilities/wireguard.js'
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
|
||||
export default {
|
||||
name: "newConfiguration",
|
||||
@@ -21,10 +22,13 @@ export default {
|
||||
PreUp: "",
|
||||
PreDown: "",
|
||||
PostUp: "",
|
||||
PostDown: "",
|
||||
UsePreSharedKey: false
|
||||
PostDown: ""
|
||||
},
|
||||
numberOfAvailableIPs: "0"
|
||||
numberOfAvailableIPs: "0",
|
||||
error: false,
|
||||
errorMessage: "",
|
||||
success: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -36,14 +40,32 @@ export default {
|
||||
this.newConfiguration.PrivateKey = wg.privateKey;
|
||||
this.newConfiguration.PublicKey = wg.publicKey;
|
||||
this.newConfiguration.PresharedKey = wg.presharedKey;
|
||||
}
|
||||
},
|
||||
async saveNewConfiguration(){
|
||||
if (this.goodToSubmit){
|
||||
this.loading = true;
|
||||
await fetchPost("/api/addWireguardConfiguration", this.newConfiguration, async (res) => {
|
||||
if (res.status){
|
||||
this.success = true
|
||||
await this.store.getConfigurations()
|
||||
setTimeout(() => {
|
||||
this.$router.push('/')
|
||||
}, 1000)
|
||||
}else{
|
||||
this.error = true;
|
||||
this.errorMessage = res.message;
|
||||
document.querySelector(`#${res.data}`).classList.remove("is-valid")
|
||||
document.querySelector(`#${res.data}`).classList.add("is-invalid")
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
goodToSubmit(){
|
||||
let requirements = ["ConfigurationName", "Address", "ListenPort", "PrivateKey"]
|
||||
let elements = [...document.querySelectorAll("input[required]")];
|
||||
|
||||
|
||||
return requirements.find(x => {
|
||||
return this.newConfiguration[x].length === 0
|
||||
}) === undefined && elements.find(x => {
|
||||
@@ -115,19 +137,26 @@ export default {
|
||||
<h3 class="text-body mb-0">New Configuration</h3>
|
||||
</div>
|
||||
|
||||
<form class="text-body d-flex flex-column gap-3">
|
||||
<form class="text-body d-flex flex-column gap-3"
|
||||
@submit="(e) => {e.preventDefault(); this.saveNewConfiguration();}"
|
||||
>
|
||||
<div class="card rounded-3 shadow">
|
||||
<div class="card-header">Configuration Name</div>
|
||||
<div class="card-body">
|
||||
<input type="text" class="form-control" placeholder="ex. wg1" id="ConfigurationName"
|
||||
v-model="this.newConfiguration.ConfigurationName"
|
||||
:disabled="this.loading"
|
||||
required>
|
||||
<div class="invalid-feedback">
|
||||
Configuration name is invalid. Possible reasons:
|
||||
<ul class="mb-0">
|
||||
<li>Configuration name already exist.</li>
|
||||
<li>Configuration name can only contain 15 lower/uppercase alphabet, numbers, "_"(underscore), "="(equal), "+"(plus), "."(period/dot), "-"(dash/hyphen)</li>
|
||||
</ul>
|
||||
<div v-if="this.error">{{this.errorMessage}}</div>
|
||||
<div v-else>
|
||||
Configuration name is invalid. Possible reasons:
|
||||
<ul class="mb-0">
|
||||
<li>Configuration name already exist.</li>
|
||||
<li>Configuration name can only contain 15 lower/uppercase alphabet, numbers, "_"(underscore), "="(equal), "+"(plus), "."(period/dot), "-"(dash/hyphen)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,6 +167,7 @@ export default {
|
||||
<label class="text-muted fw-bold mb-1"><small>PRIVATE KEY</small></label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="PrivateKey" required
|
||||
:disabled="this.loading"
|
||||
v-model="this.newConfiguration.PrivateKey" disabled
|
||||
>
|
||||
<button class="btn btn-outline-primary" type="button"
|
||||
@@ -148,24 +178,13 @@ export default {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<label class="text-muted fw-bold mb-1"><small>PUBLIC KEY</small></label>
|
||||
<input type="text" class="form-control" id="PublicKey"
|
||||
v-model="this.newConfiguration.PublicKey" disabled
|
||||
>
|
||||
</div>
|
||||
<div class="col-sm" v-if="this.newConfiguration.UsePreSharedKey">
|
||||
<label class="text-muted fw-bold mb-1"><small>PRE-SHARED KEY</small></label>
|
||||
<input type="text" class="form-control" id="PresharedKey"
|
||||
v-model="this.newConfiguration.PresharedKey" disabled
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mt-2">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="UsePreSharedKey" v-model="this.newConfiguration.UsePreSharedKey">
|
||||
<label class="form-check-label" for="UsePreSharedKey"><small>Use Pre-Shared Key?</small></label>
|
||||
<div>
|
||||
<label class="text-muted fw-bold mb-1"><small>PUBLIC KEY</small></label>
|
||||
<input type="text" class="form-control" id="PublicKey"
|
||||
v-model="this.newConfiguration.PublicKey" disabled
|
||||
>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -176,7 +195,14 @@ export default {
|
||||
min="1"
|
||||
max="65353"
|
||||
v-model="this.newConfiguration.ListenPort"
|
||||
:disabled="this.loading"
|
||||
required>
|
||||
<div class="invalid-feedback">
|
||||
<div v-if="this.error">{{this.errorMessage}}</div>
|
||||
<div v-else>
|
||||
Invalid port
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-3 shadow">
|
||||
@@ -188,9 +214,14 @@ export default {
|
||||
<input type="text" class="form-control"
|
||||
placeholder="Ex: 10.0.0.1/24" id="Address"
|
||||
v-model="this.newConfiguration.Address"
|
||||
:disabled="this.loading"
|
||||
required>
|
||||
<div class="invalid-feedback">
|
||||
IP address & range is invalid.
|
||||
<div v-if="this.error">{{this.errorMessage}}</div>
|
||||
<div v-else>
|
||||
IP address & range is invalid.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -237,10 +268,21 @@ export default {
|
||||
<!-- <i class="bi bi-save me-2"></i>-->
|
||||
<!-- Save-->
|
||||
<!-- </RouterLink>-->
|
||||
<button class="btn btn-dark btn-brand rounded-3 px-3 py-2 shadow ms-auto" :disabled="!this.goodToSubmit">
|
||||
<button class="btn btn-dark btn-brand rounded-3 px-3 py-2 shadow ms-auto"
|
||||
:disabled="!this.goodToSubmit">
|
||||
<span v-if="this.success" class="d-flex w-100">
|
||||
Success! <i class="bi bi-check-circle-fill ms-2"></i>
|
||||
</span>
|
||||
<span v-else-if="!this.loading" class="d-flex w-100">
|
||||
Save Configuration <i class="bi bi-save-fill ms-2"></i>
|
||||
</span>
|
||||
<span v-else class="d-flex w-100 align-items-center">
|
||||
Saving...
|
||||
<span class="ms-2 spinner-border spinner-border-sm" role="status">
|
||||
<!-- <span class="visually-hidden">Loading...</span>-->
|
||||
</span>
|
||||
</span>
|
||||
|
||||
Save Configuration
|
||||
<i class="bi bi-save-fill ms-2"></i>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
|
@@ -603,7 +603,6 @@ main {
|
||||
|
||||
.conf_card:hover {
|
||||
border-color: #007bff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.info_loading {
|
||||
@@ -1051,4 +1050,31 @@ pre.index-alert {
|
||||
|
||||
.totp{
|
||||
font-family: var(--bs-font-monospace);
|
||||
}
|
||||
|
||||
.message-move, /* apply transition to moving elements */
|
||||
.message-enter-active,
|
||||
.message-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.message-enter-from,
|
||||
.message-leave-to {
|
||||
filter: blur(2px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.message-enter-from{
|
||||
transform: translateY(-30px) scale(0.7);
|
||||
}
|
||||
|
||||
.message-leave-to{
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
|
||||
/* ensure leaving items are taken out of layout flow so that moving
|
||||
animations can be calculated correctly. */
|
||||
.message-leave-active {
|
||||
position: absolute;
|
||||
}
|
Reference in New Issue
Block a user