I am giving up on using ORM...

Lets go back to the good old sql query days ;)
This commit is contained in:
Donald Zou
2024-02-11 23:53:51 -05:00
parent 6b6ad05e3a
commit 1e88491ca1
11 changed files with 665 additions and 171 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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
}
]
},

View File

@@ -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
})
}
}
});

View File

@@ -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) => {

View File

@@ -0,0 +1,15 @@
<script>
export default {
name: "configuration"
}
</script>
<template>
<div class="text-body">
hiiii
</div>
</template>
<style scoped>
</style>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}