mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-06 16:00:28 +01:00
Minor updates...
This commit is contained in:
parent
75fbdb653a
commit
914a0bf514
@ -729,6 +729,38 @@ def _generatePublicKey(privateKey) -> [bool, str]:
|
|||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
|
def _getWireguardConfigurationAvailableIP(configName) -> [bool, list[str]]:
|
||||||
|
if configName not in WireguardConfigurations.keys():
|
||||||
|
return False, None
|
||||||
|
configuration = WireguardConfigurations[configName]
|
||||||
|
if len(configuration.Address) > 0:
|
||||||
|
address = configuration.Address.split(',')
|
||||||
|
print(address)
|
||||||
|
existedAddress = []
|
||||||
|
availableAddress = []
|
||||||
|
for p in configuration.Peers:
|
||||||
|
if len(p.allowed_ip) > 0:
|
||||||
|
add = p.allowed_ip.split(',')
|
||||||
|
for i in add:
|
||||||
|
a, c = i.split('/')
|
||||||
|
existedAddress.append(ipaddress.ip_address(a.replace(" ", "")))
|
||||||
|
for i in address:
|
||||||
|
addressSplit, cidr = i.split('/')
|
||||||
|
existedAddress.append(ipaddress.ip_address(addressSplit.replace(" ", "")))
|
||||||
|
for i in address:
|
||||||
|
network = ipaddress.ip_network(i.replace(" ", ""), False)
|
||||||
|
count = 0
|
||||||
|
for h in network.hosts():
|
||||||
|
if h not in existedAddress:
|
||||||
|
availableAddress.append(ipaddress.ip_network(h).compressed)
|
||||||
|
count += 1
|
||||||
|
if network.version == 6 and count > 255:
|
||||||
|
break
|
||||||
|
return True, availableAddress
|
||||||
|
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
API Routes
|
API Routes
|
||||||
'''
|
'''
|
||||||
@ -1001,6 +1033,12 @@ PersistentKeepalive = {str(peer.keepalive)}
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/getAvailableIPs/<configName>")
|
||||||
|
def API_getAvailableIPs(configName):
|
||||||
|
status, ips = _getWireguardConfigurationAvailableIP(configName)
|
||||||
|
return ResponseObject(status=status, data=ips)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/getWireguardConfigurationInfo', methods=["GET"])
|
@app.route('/api/getWireguardConfigurationInfo', methods=["GET"])
|
||||||
def API_getConfigurationInfo():
|
def API_getConfigurationInfo():
|
||||||
configurationName = request.args.get("configurationName")
|
configurationName = request.args.get("configurationName")
|
||||||
|
8
src/static/app/dist/assets/index.css
vendored
8
src/static/app/dist/assets/index.css
vendored
File diff suppressed because one or more lines are too long
39
src/static/app/dist/assets/index.js
vendored
39
src/static/app/dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
2602
src/static/app/package-lock.json
generated
2602
src/static/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,11 +11,15 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@vueuse/shared": "^10.9.0",
|
"@vueuse/shared": "^10.9.0",
|
||||||
|
"animate.css": "^4.1.1",
|
||||||
"bootstrap": "^5.3.2",
|
"bootstrap": "^5.3.2",
|
||||||
"bootstrap-icons": "^1.11.2",
|
"bootstrap-icons": "^1.11.2",
|
||||||
"cidr-tools": "^7.0.4",
|
"cidr-tools": "^7.0.4",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
|
"i": "^0.3.7",
|
||||||
|
"is-cidr": "^5.0.3",
|
||||||
|
"npm": "^10.5.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qrcodejs": "^1.0.0",
|
"qrcodejs": "^1.0.0",
|
||||||
|
@ -0,0 +1,135 @@
|
|||||||
|
<script>
|
||||||
|
import {fetchGet} from "@/utilities/fetch.js";
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "allowedIPsInput",
|
||||||
|
props: {
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean,
|
||||||
|
bulk: Boolean,
|
||||||
|
availableIp: undefined,
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
allowedIp: [],
|
||||||
|
|
||||||
|
availableIpSearchString: "",
|
||||||
|
customAvailableIp: "",
|
||||||
|
allowedIpFormatError: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
const store = WireguardConfigurationsStore();
|
||||||
|
const dashboardStore = DashboardConfigurationStore();
|
||||||
|
return {store, dashboardStore}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
searchAvailableIps(){
|
||||||
|
return this.availableIpSearchString ?
|
||||||
|
this.availableIp.filter(x =>
|
||||||
|
x.includes(this.availableIpSearchString) && !this.data.allowed_ip.includes(x)) :
|
||||||
|
this.availableIp.filter(x => !this.data.allowed_ip.includes(x))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addAllowedIp(ip){
|
||||||
|
if(this.store.checkCIDR(ip)){
|
||||||
|
this.data.allowed_ip.push(ip);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
customAvailableIp(){
|
||||||
|
this.allowedIpFormatError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="{inactiveField: this.bulk}">
|
||||||
|
<label for="peer_allowed_ip_textbox" class="form-label">
|
||||||
|
<small class="text-muted">Allowed IPs <code>(Required)</code></small>
|
||||||
|
</label>
|
||||||
|
<div class="d-flex gap-2 flex-wrap" :class="{'mb-2': this.data.allowed_ip.length > 0}">
|
||||||
|
<TransitionGroup name="list">
|
||||||
|
<span class="badge rounded-pill text-bg-success" v-for="(ip, index) in this.data.allowed_ip" :key="ip">
|
||||||
|
{{ip}}
|
||||||
|
<a role="button" @click="this.data.allowed_ip.splice(index, 1)">
|
||||||
|
<i class="bi bi-x-circle-fill ms-1"></i></a>
|
||||||
|
</span>
|
||||||
|
</TransitionGroup>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2 align-items-center">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control form-control-sm rounded-start-3"
|
||||||
|
placeholder="Enter IP Address/CIDR"
|
||||||
|
:class="{'is-invalid': this.allowedIpFormatError}"
|
||||||
|
v-model="customAvailableIp"
|
||||||
|
:disabled="bulk">
|
||||||
|
<button class="btn btn-outline-success btn-sm rounded-end-3"
|
||||||
|
:disabled="bulk || !this.customAvailableIp"
|
||||||
|
@click="this.addAllowedIp(this.customAvailableIp)
|
||||||
|
? this.customAvailableIp = '' :
|
||||||
|
this.allowedIpFormatError = true;
|
||||||
|
this.dashboardStore.newMessage('WGDashboard', 'Allowed IP is invalid', 'danger')"
|
||||||
|
type="button" id="button-addon2">
|
||||||
|
<i class="bi bi-plus-lg"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<small class="text-muted">or</small>
|
||||||
|
<div class="dropdown flex-grow-1">
|
||||||
|
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3 w-100"
|
||||||
|
:disabled="!availableIp || bulk"
|
||||||
|
data-bs-auto-close="outside"
|
||||||
|
type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="bi bi-filter-circle me-2"></i>
|
||||||
|
Pick Available IP
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu mt-2 shadow w-100 dropdown-menu-end rounded-3"
|
||||||
|
v-if="this.availableIp"
|
||||||
|
style="overflow-y: scroll; max-height: 270px; width: 300px !important;">
|
||||||
|
<li>
|
||||||
|
<div class="px-3 pb-2 pt-1">
|
||||||
|
<input class="form-control form-control-sm rounded-3"
|
||||||
|
v-model="this.availableIpSearchString"
|
||||||
|
placeholder="Search...">
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li v-for="ip in this.searchAvailableIps" >
|
||||||
|
<a class="dropdown-item d-flex" role="button" @click="this.addAllowedIp(ip)">
|
||||||
|
<span class="me-auto"><small>{{ip}}</small></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li v-if="this.searchAvailableIps.length === 0">
|
||||||
|
<small class="px-3 text-muted">No available IP containing "{{this.availableIpSearchString}}"</small>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.list-move, /* apply transition to moving elements */
|
||||||
|
.list-enter-active,
|
||||||
|
.list-leave-active {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-enter-from,
|
||||||
|
.list-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ensure leaving items are taken out of layout flow so that moving
|
||||||
|
animations can be calculated correctly. */
|
||||||
|
.list-leave-active {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,41 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "bulkAdd",
|
||||||
|
props: {
|
||||||
|
saving: Boolean,
|
||||||
|
data: Object,
|
||||||
|
availableIp: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="form-check form-switch ">
|
||||||
|
<input class="form-check-input"
|
||||||
|
type="checkbox" role="switch"
|
||||||
|
:disabled="!this.availableIp"
|
||||||
|
id="bulk_add" v-model="this.data.bulkAdd">
|
||||||
|
<label class="form-check-label me-2" for="bulk_add">
|
||||||
|
<small><strong>Bulk Add</strong></small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p :class="{'mb-0': !this.data.bulkAdd}"><small class="text-muted d-block">
|
||||||
|
By adding peers by bulk, each peer's name will be auto generated, and Allowed IP will be assign to the next available IP.
|
||||||
|
</small></p>
|
||||||
|
|
||||||
|
<div class="form-group" v-if="this.data.bulkAdd">
|
||||||
|
<input class="form-control form-control-sm rounded-3 mb-1" type="number" min="1"
|
||||||
|
:max="this.availableIp.length"
|
||||||
|
v-model="this.data.bulkAddAmount"
|
||||||
|
placeholder="How many peers you want to add?">
|
||||||
|
<small class="text-muted">
|
||||||
|
You can add up to <strong>{{this.availableIp.length}}</strong> peers
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,64 @@
|
|||||||
|
<script>
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "dnsInput",
|
||||||
|
props: {
|
||||||
|
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean,
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
error: false,
|
||||||
|
dns: JSON.parse(JSON.stringify(this.data.DNS)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
const store = WireguardConfigurationsStore();
|
||||||
|
const dashboardStore = DashboardConfigurationStore();
|
||||||
|
return {store, dashboardStore}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
checkDNS(){
|
||||||
|
let i = this.dns.split(',').map(x => x.replaceAll(' ', ''));
|
||||||
|
for(let ip in i){
|
||||||
|
if (!this.store.regexCheckIP(i[ip])){
|
||||||
|
if (!this.error){
|
||||||
|
this.dashboardStore.newMessage("WGDashboard", "DNS is invalid", "danger");
|
||||||
|
}
|
||||||
|
this.error = true;
|
||||||
|
this.data.DNS = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.error = false;
|
||||||
|
this.data.DNS = this.dns;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'dns'(){
|
||||||
|
this.checkDNS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label for="peer_DNS_textbox" class="form-label">
|
||||||
|
<small class="text-muted">DNS <code>(Required)</code></small>
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control form-control-sm rounded-3"
|
||||||
|
:class="{'is-invalid': this.error}"
|
||||||
|
:disabled="this.saving"
|
||||||
|
v-model="this.dns"
|
||||||
|
|
||||||
|
id="peer_DNS_textbox">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,63 @@
|
|||||||
|
<script>
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "endpointAllowedIps",
|
||||||
|
props: {
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
const store = WireguardConfigurationsStore();
|
||||||
|
const dashboardStore = DashboardConfigurationStore()
|
||||||
|
return {store, dashboardStore}
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
endpointAllowedIps: JSON.parse(JSON.stringify(this.data.endpoint_allowed_ip)),
|
||||||
|
error: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
checkAllowedIP(){
|
||||||
|
let i = this.endpointAllowedIps.split(",").map(x => x.replaceAll(' ', ''));
|
||||||
|
for (let ip in i){
|
||||||
|
if (!this.store.checkCIDR(i[ip])){
|
||||||
|
if (!this.error){
|
||||||
|
this.dashboardStore.newMessage("WGDashboard", "Endpoint Allowed IP is invalid.", "danger")
|
||||||
|
}
|
||||||
|
this.data.endpoint_allowed_ip = "";
|
||||||
|
this.error = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.error = false;
|
||||||
|
this.data.endpoint_allowed_ip = this.endpointAllowedIps;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'endpointAllowedIps'(){
|
||||||
|
this.checkAllowedIP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label for="peer_endpoint_allowed_ips" class="form-label">
|
||||||
|
<small class="text-muted">Endpoint Allowed IPs <code>(Required)</code></small>
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control form-control-sm rounded-3"
|
||||||
|
:class="{'is-invalid': error}"
|
||||||
|
:disabled="this.saving"
|
||||||
|
v-model="this.endpointAllowedIps"
|
||||||
|
@blur="this.checkAllowedIP()"
|
||||||
|
id="peer_endpoint_allowed_ips">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,23 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "mtuInput",
|
||||||
|
props: {
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label for="peer_mtu" class="form-label"><small class="text-muted">MTU</small></label>
|
||||||
|
<input type="number" class="form-control form-control-sm rounded-3"
|
||||||
|
:disabled="this.saving"
|
||||||
|
v-model="this.data.mtu"
|
||||||
|
id="peer_mtu">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,26 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "nameInput",
|
||||||
|
props: {
|
||||||
|
bulk: Boolean,
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="{inactiveField: this.bulk}">
|
||||||
|
<label for="peer_name_textbox" class="form-label">
|
||||||
|
<small class="text-muted">Name</small>
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control form-control-sm rounded-3"
|
||||||
|
:disabled="this.saving || this.bulk"
|
||||||
|
v-model="this.data.name"
|
||||||
|
id="peer_name_textbox" placeholder="">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,25 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "persistentKeepAliveInput",
|
||||||
|
props: {
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label for="peer_keep_alive" class="form-label">
|
||||||
|
<small class="text-muted">Persistent Keepalive</small>
|
||||||
|
</label>
|
||||||
|
<input type="number" class="form-control form-control-sm rounded-3"
|
||||||
|
:disabled="this.saving"
|
||||||
|
v-model="this.data.keepalive"
|
||||||
|
id="peer_keep_alive">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,25 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "presharedKeyInput",
|
||||||
|
props: {
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label for="peer_preshared_key_textbox" class="form-label">
|
||||||
|
<small class="text-muted">Pre-Shared Key</small>
|
||||||
|
</label>
|
||||||
|
<input type="text" class="form-control form-control-sm rounded-3"
|
||||||
|
:disabled="this.saving"
|
||||||
|
v-model="this.data.preshared_key"
|
||||||
|
id="peer_preshared_key_textbox">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,108 @@
|
|||||||
|
<script>
|
||||||
|
import "@/utilities/wireguard.js"
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
export default {
|
||||||
|
name: "privatePublicKeyInput",
|
||||||
|
props: {
|
||||||
|
data: Object,
|
||||||
|
saving: Boolean,
|
||||||
|
bulk: Boolean
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
const dashboardStore = DashboardConfigurationStore();
|
||||||
|
return {dashboardStore}
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
keypair: {
|
||||||
|
publicKey: "",
|
||||||
|
privateKey: "",
|
||||||
|
presharedKey: ""
|
||||||
|
},
|
||||||
|
editKey: false,
|
||||||
|
error: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
genKeyPair(){
|
||||||
|
this.editKey = false;
|
||||||
|
this.keypair = window.wireguard.generateKeypair();
|
||||||
|
this.data.private_key = this.keypair.privateKey;
|
||||||
|
this.data.public_key = this.keypair.publicKey;
|
||||||
|
},
|
||||||
|
checkMatching(){
|
||||||
|
try{
|
||||||
|
if (window.wireguard.generatePublicKey(this.keypair.privateKey)
|
||||||
|
!== this.keypair.publicKey){
|
||||||
|
this.error = true;
|
||||||
|
this.dashboardStore.newMessage("WGDashboard", "Private Key and Public Key does not match.", "danger");
|
||||||
|
}
|
||||||
|
}catch (e){
|
||||||
|
this.error = true;
|
||||||
|
this.data.private_key = "";
|
||||||
|
this.data.public_key = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.genKeyPair();
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
keypair: {
|
||||||
|
deep: true,
|
||||||
|
handler(){
|
||||||
|
this.error = false;
|
||||||
|
this.checkMatching();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex gap-2 flex-column" :class="{inactiveField: this.bulk}">
|
||||||
|
<div>
|
||||||
|
<label for="peer_private_key_textbox" class="form-label">
|
||||||
|
<small class="text-muted">Private Key <code>(Required for QR Code and Download)</code></small>
|
||||||
|
</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control form-control-sm rounded-start-3"
|
||||||
|
v-model="this.keypair.privateKey"
|
||||||
|
:disabled="!this.editKey || this.bulk"
|
||||||
|
:class="{'is-invalid': this.error}"
|
||||||
|
@blur="this.checkMatching()"
|
||||||
|
id="peer_private_key_textbox">
|
||||||
|
<button class="btn btn-outline-info btn-sm rounded-end-3"
|
||||||
|
@click="this.genKeyPair()"
|
||||||
|
:disabled="this.bulk"
|
||||||
|
type="button" id="button-addon2">
|
||||||
|
<i class="bi bi-arrow-repeat"></i> </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="d-flex">
|
||||||
|
<label for="public_key" class="form-label">
|
||||||
|
<small class="text-muted">Public Key <code>(Required)</code></small>
|
||||||
|
</label>
|
||||||
|
<div class="form-check form-switch ms-auto">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch"
|
||||||
|
:disabled="this.bulk"
|
||||||
|
id="enablePublicKeyEdit" v-model="this.editKey">
|
||||||
|
<label class="form-check-label" for="enablePublicKeyEdit">
|
||||||
|
<small>Edit</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input class="form-control-sm form-control rounded-3"
|
||||||
|
:class="{'is-invalid': this.error}"
|
||||||
|
v-model="this.keypair.publicKey"
|
||||||
|
@blur="this.checkMatching()"
|
||||||
|
:disabled="!this.editKey || this.bulk"
|
||||||
|
type="text" id="public_key">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,151 @@
|
|||||||
|
<script>
|
||||||
|
// import {Popover, Dropdown} from "bootstrap";
|
||||||
|
import {fetchGet} from "@/utilities/fetch.js";
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
|
import NameInput from "@/components/configurationComponents/newPeersComponents/nameInput.vue";
|
||||||
|
import PrivatePublicKeyInput from "@/components/configurationComponents/newPeersComponents/privatePublicKeyInput.vue";
|
||||||
|
import AllowedIPsInput from "@/components/configurationComponents/newPeersComponents/allowedIPsInput.vue";
|
||||||
|
import DnsInput from "@/components/configurationComponents/newPeersComponents/dnsInput.vue";
|
||||||
|
import EndpointAllowedIps from "@/components/configurationComponents/newPeersComponents/endpointAllowedIps.vue";
|
||||||
|
import PresharedKeyInput from "@/components/configurationComponents/newPeersComponents/presharedKeyInput.vue";
|
||||||
|
import MtuInput from "@/components/configurationComponents/newPeersComponents/mtuInput.vue";
|
||||||
|
import PersistentKeepAliveInput
|
||||||
|
from "@/components/configurationComponents/newPeersComponents/persistentKeepAliveInput.vue";
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
import BulkAdd from "@/components/configurationComponents/newPeersComponents/bulkAdd.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "peerCreate",
|
||||||
|
components: {
|
||||||
|
BulkAdd,
|
||||||
|
PersistentKeepAliveInput,
|
||||||
|
MtuInput,
|
||||||
|
PresharedKeyInput, EndpointAllowedIps, DnsInput, AllowedIPsInput, PrivatePublicKeyInput, NameInput},
|
||||||
|
data(){
|
||||||
|
return{
|
||||||
|
|
||||||
|
data: {
|
||||||
|
bulkAdd: false,
|
||||||
|
bulkAddAmount: "",
|
||||||
|
name: "",
|
||||||
|
allowed_ip: [],
|
||||||
|
private_key: "",
|
||||||
|
public_key: "",
|
||||||
|
DNS: this.dashboardStore.Configuration.Peers.peer_global_dns,
|
||||||
|
endpoint_allowed_ip: this.dashboardStore.Configuration.Peers.peer_endpoint_allowed_ip,
|
||||||
|
keepalive: parseInt(this.dashboardStore.Configuration.Peers.peer_keep_alive),
|
||||||
|
mtu: parseInt(this.dashboardStore.Configuration.Peers.peer_mtu),
|
||||||
|
preshared_key: ""
|
||||||
|
},
|
||||||
|
availableIp: undefined,
|
||||||
|
availableIpSearchString: "",
|
||||||
|
saving: false,
|
||||||
|
allowedIpDropdown: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
fetchGet("/api/getAvailableIPs/" + this.$route.params.id, {}, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
this.availableIp = res.data;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
const store = WireguardConfigurationsStore();
|
||||||
|
const dashboardStore = DashboardConfigurationStore();
|
||||||
|
return {store, dashboardStore}
|
||||||
|
},
|
||||||
|
computed:{
|
||||||
|
allRequireFieldsFilled(){
|
||||||
|
let status = true;
|
||||||
|
if (this.data.bulkAdd){
|
||||||
|
if(this.data.bulkAddAmount.length === 0 || this.data.bulkAddAmount > this.availableIp.length){
|
||||||
|
status = false;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
let requireFields =
|
||||||
|
["allowed_ip", "private_key", "public_key", "DNS", "endpoint_allowed_ip", "keepalive", "mtu"]
|
||||||
|
|
||||||
|
requireFields.forEach(x => {
|
||||||
|
if (this.data[x].length === 0) status = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
bulkAdd(newVal){
|
||||||
|
if(!newVal){
|
||||||
|
this.data.bulkAddAmount = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'data.bulkAddAmount'(){
|
||||||
|
if (this.data.bulkAddAmount > this.availableIp.length){
|
||||||
|
this.data.bulkAddAmount = this.availableIp.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="modal fade" id="peerCreateModal" data-bs-backdrop="static">
|
||||||
|
<div class="modal-dialog " style="max-width: 700px !important;">
|
||||||
|
<div class="modal-content rounded-3 border-0 shadow">
|
||||||
|
<div class="modal-header border-0 pb-0">
|
||||||
|
<h1 class="modal-title fs-5" id="staticBackdropLabel">Add Peer</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="d-flex flex-column gap-2">
|
||||||
|
<BulkAdd :saving="saving" :data="this.data" :availableIp="this.availableIp"></BulkAdd>
|
||||||
|
<hr class="mb-0 mt-2">
|
||||||
|
<NameInput :saving="saving" :data="this.data" v-if="!this.data.bulkAdd"></NameInput>
|
||||||
|
<PrivatePublicKeyInput :saving="saving" :data="data" v-if="!this.data.bulkAdd"></PrivatePublicKeyInput>
|
||||||
|
<AllowedIPsInput :availableIp="this.availableIp" :saving="saving" :data="data" v-if="!this.data.bulkAdd"></AllowedIPsInput>
|
||||||
|
<DnsInput :saving="saving" :data="data"></DnsInput>
|
||||||
|
<EndpointAllowedIps :saving="saving" :data="data"></EndpointAllowedIps>
|
||||||
|
<hr class="mb-0 mt-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm" v-if="!this.data.bulkAdd">
|
||||||
|
<PresharedKeyInput :saving="saving" :data="data" :bulk="this.data.bulkAdd"></PresharedKeyInput>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<MtuInput :saving="saving" :data="data"></MtuInput>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<PersistentKeepAliveInput :saving="saving" :data="data"></PersistentKeepAliveInput>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex mt-2">
|
||||||
|
<button class="ms-auto btn btn-dark btn-brand rounded-3 px-3 py-2 shadow"
|
||||||
|
:disabled="!this.allRequireFieldsFilled"
|
||||||
|
>
|
||||||
|
<i class="bi bi-plus-circle-fill me-2"></i>Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.peerSettingContainer {
|
||||||
|
background-color: #00000060;
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
|
|
||||||
|
div{
|
||||||
|
transition: 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inactiveField{
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card{
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
@ -35,6 +35,7 @@ import {
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import PeerSettings from "@/components/configurationComponents/peerSettings.vue";
|
import PeerSettings from "@/components/configurationComponents/peerSettings.vue";
|
||||||
import PeerQRCode from "@/components/configurationComponents/peerQRCode.vue";
|
import PeerQRCode from "@/components/configurationComponents/peerQRCode.vue";
|
||||||
|
import PeerCreate from "@/components/configurationComponents/peerCreate.vue";
|
||||||
|
|
||||||
Chart.register(
|
Chart.register(
|
||||||
ArcElement,
|
ArcElement,
|
||||||
@ -64,7 +65,7 @@ Chart.register(
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "peerList",
|
name: "peerList",
|
||||||
components: {PeerQRCode, PeerSettings, PeerSearch, Peer, Line, Bar},
|
components: {PeerCreate, PeerQRCode, PeerSettings, PeerSearch, Peer, Line, Bar},
|
||||||
setup(){
|
setup(){
|
||||||
const dashboardConfigurationStore = DashboardConfigurationStore();
|
const dashboardConfigurationStore = DashboardConfigurationStore();
|
||||||
const wireguardConfigurationStore = WireguardConfigurationsStore();
|
const wireguardConfigurationStore = WireguardConfigurationsStore();
|
||||||
@ -109,6 +110,9 @@ export default {
|
|||||||
peerQRCode: {
|
peerQRCode: {
|
||||||
modalOpen: false,
|
modalOpen: false,
|
||||||
peerConfigData: undefined
|
peerConfigData: undefined
|
||||||
|
},
|
||||||
|
peerCreate: {
|
||||||
|
modalOpen: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -442,7 +446,10 @@ export default {
|
|||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div class="d-flex align-items-center gap-3 mb-2 ">
|
<div class="d-flex align-items-center gap-3 mb-2 ">
|
||||||
<h3>Peers</h3>
|
<h3>Peers</h3>
|
||||||
<a href="#" class="text-decoration-none ms-auto"><i class="bi bi-plus-circle-fill me-2"></i>Add Peer</a>
|
<a role="button"
|
||||||
|
data-bs-toggle="modal" data-bs-target="#peerCreateModal"
|
||||||
|
class="text-decoration-none ms-auto">
|
||||||
|
<i class="bi bi-plus-circle-fill me-2"></i>Add Peer</a>
|
||||||
</div>
|
</div>
|
||||||
<PeerSearch></PeerSearch>
|
<PeerSearch></PeerSearch>
|
||||||
<TransitionGroup name="list" tag="div" class="row gx-2 gy-2 z-0">
|
<TransitionGroup name="list" tag="div" class="row gx-2 gy-2 z-0">
|
||||||
@ -469,6 +476,10 @@ export default {
|
|||||||
@close="this.peerQRCode.modalOpen = false"
|
@close="this.peerQRCode.modalOpen = false"
|
||||||
v-if="peerQRCode.modalOpen"></PeerQRCode>
|
v-if="peerQRCode.modalOpen"></PeerQRCode>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
<!-- <Transition name="fade">-->
|
||||||
|
<!-- -->
|
||||||
|
<!-- </Transition>-->
|
||||||
|
<PeerCreate></PeerCreate>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -6,10 +6,8 @@ export default {
|
|||||||
peerConfigData: String
|
peerConfigData: String
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// let qrcode = QRCode.create(, );
|
|
||||||
QRCode.toCanvas(document.querySelector("#qrcode"), this.peerConfigData , function (error) {
|
QRCode.toCanvas(document.querySelector("#qrcode"), this.peerConfigData , function (error) {
|
||||||
if (error) console.error(error)
|
if (error) console.error(error)
|
||||||
console.log('success!');
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,17 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
let fadeIn = "animate__fadeInUp";
|
||||||
|
let fadeOut = "animate__fadeOutDown"
|
||||||
|
|
||||||
|
this.$el.querySelectorAll(".dropdown").forEach(x => {
|
||||||
|
x.addEventListener('show.bs.dropdown', (e) => {
|
||||||
|
console.log(e.target.parentNode.children)
|
||||||
|
console.log(e.target.closest("ul.dropdown-menu"))
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -62,7 +73,7 @@ export default {
|
|||||||
<i class="bi bi-filter-circle me-2"></i>
|
<i class="bi bi-filter-circle me-2"></i>
|
||||||
Sort
|
Sort
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu mt-2 shadow-lg">
|
<ul class="dropdown-menu mt-2 shadow">
|
||||||
<li v-for="(value, key) in this.sort">
|
<li v-for="(value, key) in this.sort">
|
||||||
<a class="dropdown-item d-flex" role="button" @click="this.updateSort(key)">
|
<a class="dropdown-item d-flex" role="button" @click="this.updateSort(key)">
|
||||||
<span class="me-auto">{{value}}</span>
|
<span class="me-auto">{{value}}</span>
|
||||||
@ -75,7 +86,7 @@ export default {
|
|||||||
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-arrow-repeat me-2"></i>Refresh Interval
|
<i class="bi bi-arrow-repeat me-2"></i>Refresh Interval
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu shadow mt-2">
|
||||||
<li v-for="(value, key) in this.interval">
|
<li v-for="(value, key) in this.interval">
|
||||||
<a class="dropdown-item d-flex" role="button" @click="updateRefreshInterval(key)">
|
<a class="dropdown-item d-flex" role="button" @click="updateRefreshInterval(key)">
|
||||||
<span class="me-auto">{{value}}</span>
|
<span class="me-auto">{{value}}</span>
|
||||||
|
@ -2,6 +2,7 @@ import '../../css/dashboard.css'
|
|||||||
import 'bootstrap/dist/css/bootstrap.css'
|
import 'bootstrap/dist/css/bootstrap.css'
|
||||||
import 'bootstrap/dist/js/bootstrap.js'
|
import 'bootstrap/dist/js/bootstrap.js'
|
||||||
import 'bootstrap-icons/font/bootstrap-icons.css'
|
import 'bootstrap-icons/font/bootstrap-icons.css'
|
||||||
|
import 'animate.css/animate.compat.css'
|
||||||
|
|
||||||
import {createApp, markRaw} from 'vue'
|
import {createApp, markRaw} from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {defineStore} from "pinia";
|
import {defineStore} from "pinia";
|
||||||
import {fetchGet} from "@/utilities/fetch.js";
|
import {fetchGet} from "@/utilities/fetch.js";
|
||||||
|
import isCidr from "is-cidr";
|
||||||
|
|
||||||
export const WireguardConfigurationsStore = defineStore('WireguardConfigurationsStore', {
|
export const WireguardConfigurationsStore = defineStore('WireguardConfigurationsStore', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@ -11,6 +12,13 @@ export const WireguardConfigurationsStore = defineStore('WireguardConfigurations
|
|||||||
await fetchGet("/api/getWireguardConfigurations", {}, (res) => {
|
await fetchGet("/api/getWireguardConfigurations", {}, (res) => {
|
||||||
if (res.status) this.Configurations = res.data
|
if (res.status) this.Configurations = res.data
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
regexCheckIP(ip){
|
||||||
|
let regex = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/;
|
||||||
|
return regex.test(ip)
|
||||||
|
},
|
||||||
|
checkCIDR(ip){
|
||||||
|
return isCidr(ip) !== 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user