mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-03 07:46:18 +00:00
Feature for #844
This commit is contained in:
@@ -39,6 +39,7 @@ from logging.config import dictConfig
|
|||||||
from modules.DashboardClients import DashboardClients
|
from modules.DashboardClients import DashboardClients
|
||||||
from modules.DashboardPlugins import DashboardPlugins
|
from modules.DashboardPlugins import DashboardPlugins
|
||||||
from modules.DashboardWebHooks import DashboardWebHooks
|
from modules.DashboardWebHooks import DashboardWebHooks
|
||||||
|
from modules.NewConfigurationTemplates import NewConfigurationTemplates
|
||||||
|
|
||||||
dictConfig({
|
dictConfig({
|
||||||
'version': 1,
|
'version': 1,
|
||||||
@@ -214,12 +215,40 @@ def API_SignOut():
|
|||||||
session.clear()
|
session.clear()
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@app.route(f'{APP_PREFIX}/api/getWireguardConfigurations', methods=["GET"])
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurations')
|
||||||
def API_getWireguardConfigurations():
|
def API_getWireguardConfigurations():
|
||||||
InitWireguardConfigurationsList()
|
InitWireguardConfigurationsList()
|
||||||
return ResponseObject(data=[wc for wc in WireguardConfigurations.values()])
|
return ResponseObject(data=[wc for wc in WireguardConfigurations.values()])
|
||||||
|
|
||||||
@app.route(f'{APP_PREFIX}/api/addWireguardConfiguration', methods=["POST"])
|
@app.get(f'{APP_PREFIX}/api/newConfigurationTemplates')
|
||||||
|
def API_NewConfigurationTemplates():
|
||||||
|
return ResponseObject(data=NewConfigurationTemplates.GetTemplates())
|
||||||
|
|
||||||
|
@app.get(f'{APP_PREFIX}/api/newConfigurationTemplates/createTemplate')
|
||||||
|
def API_NewConfigurationTemplates_CreateTemplate():
|
||||||
|
return ResponseObject(data=NewConfigurationTemplates.CreateTemplate().model_dump())
|
||||||
|
|
||||||
|
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/updateTemplate')
|
||||||
|
def API_NewConfigurationTemplates_UpdateTemplate():
|
||||||
|
data = request.get_json()
|
||||||
|
template = data.get('Template', None)
|
||||||
|
if not template:
|
||||||
|
return ResponseObject(False, "Please provide template")
|
||||||
|
|
||||||
|
status, msg = NewConfigurationTemplates.UpdateTemplate(template)
|
||||||
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/deleteTemplate')
|
||||||
|
def API_NewConfigurationTemplates_DeleteTemplate():
|
||||||
|
data = request.get_json()
|
||||||
|
template = data.get('Template', None)
|
||||||
|
if not template:
|
||||||
|
return ResponseObject(False, "Please provide template")
|
||||||
|
|
||||||
|
status, msg = NewConfigurationTemplates.DeleteTemplate(template)
|
||||||
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
@app.post(f'{APP_PREFIX}/api/addWireguardConfiguration')
|
||||||
def API_addWireguardConfiguration():
|
def API_addWireguardConfiguration():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
requiredKeys = [
|
requiredKeys = [
|
||||||
@@ -1625,6 +1654,7 @@ AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations)
|
|||||||
DashboardLogger: DashboardLogger = DashboardLogger()
|
DashboardLogger: DashboardLogger = DashboardLogger()
|
||||||
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
||||||
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
||||||
|
NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates()
|
||||||
|
|
||||||
InitWireguardConfigurationsList(startup=True)
|
InitWireguardConfigurationsList(startup=True)
|
||||||
|
|
||||||
|
88
src/modules/NewConfigurationTemplates.py
Normal file
88
src/modules/NewConfigurationTemplates.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
from pydantic import BaseModel, field_serializer
|
||||||
|
import sqlalchemy as db
|
||||||
|
from .ConnectionString import ConnectionString
|
||||||
|
|
||||||
|
|
||||||
|
class NewConfigurationTemplate(BaseModel):
|
||||||
|
TemplateID: str = ''
|
||||||
|
Subnet: str = ''
|
||||||
|
ListenPortStart: int = 0
|
||||||
|
ListenPortEnd: int = 0
|
||||||
|
Notes: str = ""
|
||||||
|
|
||||||
|
class NewConfigurationTemplates:
|
||||||
|
def __init__(self):
|
||||||
|
self.engine = db.create_engine(ConnectionString("wgdashboard"))
|
||||||
|
self.metadata = db.MetaData()
|
||||||
|
self.templatesTable = db.Table(
|
||||||
|
'NewConfigurationTemplates', self.metadata,
|
||||||
|
db.Column('TemplateID', db.String(255), primary_key=True),
|
||||||
|
db.Column('Subnet', db.String(255)),
|
||||||
|
db.Column('ListenPortStart', db.Integer),
|
||||||
|
db.Column('ListenPortEnd', db.Integer),
|
||||||
|
db.Column('Notes', db.Text),
|
||||||
|
)
|
||||||
|
self.metadata.create_all(self.engine)
|
||||||
|
self.Templates: list[NewConfigurationTemplate] = []
|
||||||
|
self.__getTemplates()
|
||||||
|
|
||||||
|
def GetTemplates(self):
|
||||||
|
self.__getTemplates()
|
||||||
|
return list(map(lambda x : x.model_dump(), self.Templates))
|
||||||
|
|
||||||
|
def __getTemplates(self):
|
||||||
|
with self.engine.connect() as conn:
|
||||||
|
templates = conn.execute(
|
||||||
|
self.templatesTable.select()
|
||||||
|
).mappings().fetchall()
|
||||||
|
self.Templates.clear()
|
||||||
|
self.Templates = [NewConfigurationTemplate(**template) for template in templates]
|
||||||
|
|
||||||
|
def CreateTemplate(self) -> NewConfigurationTemplate:
|
||||||
|
return NewConfigurationTemplate(TemplateID=str(uuid.uuid4()))
|
||||||
|
|
||||||
|
def SearchTemplate(self, template: NewConfigurationTemplate):
|
||||||
|
try:
|
||||||
|
first = next(filter(lambda x : x.TemplateID == template.TemplateID, self.Templates))
|
||||||
|
except StopIteration:
|
||||||
|
return None
|
||||||
|
return first
|
||||||
|
|
||||||
|
def UpdateTemplate(self, template: dict[str, str]) -> tuple[bool, str] | tuple[bool, None]:
|
||||||
|
try:
|
||||||
|
template = NewConfigurationTemplate(**template)
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
if self.SearchTemplate(template):
|
||||||
|
conn.execute(
|
||||||
|
self.templatesTable.update().values(
|
||||||
|
template.model_dump(exclude={'TemplateID'})
|
||||||
|
).where(
|
||||||
|
self.templatesTable.c.TemplateID == template.TemplateID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
conn.execute(
|
||||||
|
self.templatesTable.insert().values(
|
||||||
|
template.model_dump()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.__getTemplates()
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
def DeleteTemplate(self, template: dict[str, str]) -> tuple[bool, str] | tuple[bool, None]:
|
||||||
|
try:
|
||||||
|
template = NewConfigurationTemplate(**template)
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
conn.execute(
|
||||||
|
self.templatesTable.delete().where(
|
||||||
|
self.templatesTable.c.TemplateID == template.TemplateID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.__getTemplates()
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
return True, None
|
@@ -0,0 +1,250 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import {computed, onMounted, reactive, ref, watch} from "vue";
|
||||||
|
import {containsCidr, expandCidr, mergeCidr, parseCidr} from "cidr-tools";
|
||||||
|
import {fetchPost} from "@/utilities/fetch.js"
|
||||||
|
const props = defineProps(['template', 'edit', 'isNew', 'peersCount'])
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore";
|
||||||
|
const store = WireguardConfigurationsStore()
|
||||||
|
const edit = ref(false)
|
||||||
|
if (props.edit){
|
||||||
|
edit.value = true
|
||||||
|
}
|
||||||
|
const data = ref({...props.template})
|
||||||
|
|
||||||
|
|
||||||
|
const peersCount = ref(256)
|
||||||
|
const groups = ref([])
|
||||||
|
const show = ref(20)
|
||||||
|
const emits = defineEmits(['subnet', 'port', 'update', 'remove'])
|
||||||
|
const selectedSubnet = ref(undefined)
|
||||||
|
const selectedPort = ref(undefined)
|
||||||
|
const ports = ref([])
|
||||||
|
const availableSubnets = () => {
|
||||||
|
groups.value = []
|
||||||
|
if (props.template.Subnet){
|
||||||
|
let templateExpand = new Set([...expandCidr(props.template.Subnet)])
|
||||||
|
if (props.peersCount && props.peersCount > 0){
|
||||||
|
for (let c of store.Configurations){
|
||||||
|
let address = c.Address.replace(" ", "").split(",")
|
||||||
|
for (let a of address){
|
||||||
|
if (containsCidr(props.template.Subnet, a)){
|
||||||
|
templateExpand = templateExpand.difference(
|
||||||
|
new Set([...expandCidr(a)])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let groupsCount = Math.floor(templateExpand.size / props.peersCount)
|
||||||
|
let sliced = 0
|
||||||
|
templateExpand = Array.from(templateExpand)
|
||||||
|
for (let g = 0; g < (groupsCount > 10 ? 10 : groupsCount); g++){
|
||||||
|
groups.value.push(mergeCidr(templateExpand.slice(sliced, sliced + props.peersCount)))
|
||||||
|
sliced += props.peersCount
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const availablePorts = () => {
|
||||||
|
if (props.template.ListenPortStart && props.template.ListenPortEnd){
|
||||||
|
let start = props.template.ListenPortStart
|
||||||
|
let end = props.template.ListenPortEnd
|
||||||
|
if (start > end){
|
||||||
|
start = props.template.ListenPortEnd
|
||||||
|
end = props.template.ListenPortStart
|
||||||
|
}
|
||||||
|
let p = new Set(Array.from(
|
||||||
|
{
|
||||||
|
length: end - start + 1
|
||||||
|
}, (val, index) => start + index
|
||||||
|
))
|
||||||
|
ports.value = [...p.difference(new Set(store.Configurations.map(
|
||||||
|
c => Number(c.ListenPort)
|
||||||
|
)))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!props.isNew){
|
||||||
|
availableSubnets()
|
||||||
|
availablePorts()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.peersCount, () => {
|
||||||
|
availableSubnets()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(selectedSubnet, () => {
|
||||||
|
emits("subnet", selectedSubnet.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(selectedPort, () => {
|
||||||
|
emits("port", selectedPort.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.template, () => {
|
||||||
|
availableSubnets()
|
||||||
|
availablePorts()
|
||||||
|
}, {
|
||||||
|
deep: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const readyToSave = computed(() => {
|
||||||
|
try{
|
||||||
|
const {start, end} = parseCidr(data.value.Subnet)
|
||||||
|
if (end - start >= 1000000n) {
|
||||||
|
throw new Error("Too many IPs");
|
||||||
|
}
|
||||||
|
return data.value.Subnet && data.value.ListenPortStart && data.value.ListenPortEnd && (data.value.ListenPortEnd >= data.value.ListenPortStart)
|
||||||
|
}catch (e){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const saveTemplate = async () => {
|
||||||
|
await fetchPost("/api/newConfigurationTemplates/updateTemplate", {
|
||||||
|
Template: data.value
|
||||||
|
}, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
emits('update', data.value)
|
||||||
|
edit.value = false
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTemplate = async () => {
|
||||||
|
await fetchPost("/api/newConfigurationTemplates/deleteTemplate", {
|
||||||
|
Template: data.value
|
||||||
|
}, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
emits('remove', data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card rounded-3">
|
||||||
|
<div class="card-body ">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="d-flex flex-column gap-2">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<label class="text-muted">
|
||||||
|
<small><LocaleText t="Subnet"></LocaleText></small>
|
||||||
|
</label>
|
||||||
|
<p class="mb-0 ms-auto" v-if="!edit"><small>{{ template.Subnet }}</small></p>
|
||||||
|
<input class="form-control-sm form-control rounded-3 w-auto ms-auto" v-model="data.Subnet" v-else>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 flex-column" v-if="!edit">
|
||||||
|
<label class="text-muted d-flex align-items-center gap-1" style="white-space: nowrap">
|
||||||
|
<small><LocaleText t="Available Subnets"></LocaleText></small>
|
||||||
|
<span class="badge rounded-pill text-bg-success ms-auto">
|
||||||
|
{{ groups.length }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
v-model="selectedSubnet"
|
||||||
|
class="form-select form-select-sm rounded-3 w-100 ms-auto">
|
||||||
|
<option :value="undefined" disabled>
|
||||||
|
<LocaleText t="Select..."></LocaleText>
|
||||||
|
</option>
|
||||||
|
<option v-for="s in groups" :value='s.join(", ")'>
|
||||||
|
{{ s.join(", ") }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="d-flex flex-column gap-2 h-100">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<label class="text-muted">
|
||||||
|
<small><LocaleText t="Listen Port Range"></LocaleText></small>
|
||||||
|
</label>
|
||||||
|
<p class="mb-0 ms-auto" v-if="!edit">
|
||||||
|
<small>
|
||||||
|
{{ template.ListenPortStart }}<i class="bi bi-arrow-right mx-2"></i>
|
||||||
|
{{ template.ListenPortEnd }}
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
<div v-else class="d-flex ms-auto align-items-center">
|
||||||
|
<input class="form-control-sm form-control rounded-3 ms-auto"
|
||||||
|
style="width: 80px"
|
||||||
|
v-model="data.ListenPortStart"
|
||||||
|
type="number"
|
||||||
|
>
|
||||||
|
<i class="bi bi-arrow-right mx-2"></i>
|
||||||
|
<input class="form-control-sm form-control rounded-3 ms-auto"
|
||||||
|
style="width: 80px"
|
||||||
|
v-model="data.ListenPortEnd"
|
||||||
|
type="number"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2 flex-column mt-auto" v-if="!edit">
|
||||||
|
<label class="text-muted d-flex align-items-center gap-1" style="white-space: nowrap">
|
||||||
|
<small><LocaleText t="Available Ports"></LocaleText></small>
|
||||||
|
<span class="badge rounded-pill text-bg-success ms-auto">
|
||||||
|
{{ ports.length }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
v-model="selectedPort"
|
||||||
|
|
||||||
|
class="form-select form-select-sm rounded-3 w-100 ms-auto">
|
||||||
|
<option :value="undefined" disabled>
|
||||||
|
<LocaleText t="Select..."></LocaleText>
|
||||||
|
</option>
|
||||||
|
<option v-for="p in [...ports]" :value='p'>
|
||||||
|
{{ p }}
|
||||||
|
</option>
|
||||||
|
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="d-flex gap-2" v-if="!edit">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="edit = true; data = {...props.template}"
|
||||||
|
class="ms-auto btn btn-sm border-primary-subtle bg-primary-subtle text-primary-emphasis rounded-3">
|
||||||
|
<LocaleText t="Edit"></LocaleText>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="deleteTemplate()"
|
||||||
|
class="btn btn-sm border-danger-subtle bg-danger-subtle text-danger-emphasis rounded-3">
|
||||||
|
<LocaleText t="Delete"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2" v-else>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="isNew ? emits('remove') : edit = false"
|
||||||
|
class="ms-auto btn btn-sm border-secondary-subtle bg-secondary-subtle text-secondary-emphasis rounded-3">
|
||||||
|
<LocaleText t="Cancel"></LocaleText>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="saveTemplate()"
|
||||||
|
:class="{disabled: !readyToSave}"
|
||||||
|
class="btn btn-sm border-primary-subtle bg-primary-subtle text-primary-emphasis rounded-3">
|
||||||
|
<LocaleText t="Save"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@@ -0,0 +1,88 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {fetchGet} from "@/utilities/fetch.js"
|
||||||
|
import NewConfigurationTemplate from "@/components/newConfigurationComponents/newConfigurationTemplate.vue";
|
||||||
|
const emits = defineEmits(['subnet', 'port'])
|
||||||
|
const templates = ref([])
|
||||||
|
|
||||||
|
const getTemplates = async () => {
|
||||||
|
await fetchGet('/api/newConfigurationTemplates', {}, (res) => {
|
||||||
|
templates.value = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await getTemplates()
|
||||||
|
|
||||||
|
const newTemplates = ref([])
|
||||||
|
const newTemplate = async () => {
|
||||||
|
await fetchGet('/api/newConfigurationTemplates/createTemplate', {}, (res) => {
|
||||||
|
newTemplates.value.push(res.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const numberOfIP = ref(256)
|
||||||
|
const calculateIP = ref(256)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<LocaleText t="Templates"></LocaleText>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="newTemplate()"
|
||||||
|
class="btn btn-sm bg-success-subtle text-success-emphasis border-success-subtle rounded-3 ms-auto">
|
||||||
|
<i class="bi bi-plus-circle me-2"></i><LocaleText t="Add Template"></LocaleText>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<small class="text-muted">
|
||||||
|
<LocaleText t="Create templates to keep track a list of available Subnets & Listen Ports"></LocaleText>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex gap-2 align-items-center mb-2" v-if="templates.length > 0">
|
||||||
|
<label class="text-muted" style="white-space: nowrap">
|
||||||
|
<small><LocaleText t="No. of IP Address / Subnet"></LocaleText></small>
|
||||||
|
</label>
|
||||||
|
<input type="number"
|
||||||
|
v-model="numberOfIP"
|
||||||
|
@change="calculateIP = numberOfIP"
|
||||||
|
class="form-control form-control-sm rounded-3 w-100 ms-auto">
|
||||||
|
</div>
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-12" v-if="newTemplates.length === 0 && templates.length === 0">
|
||||||
|
<p class="text-center text-muted m-0">
|
||||||
|
<LocaleText t="No Templates"></LocaleText>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12" v-for="template in newTemplates">
|
||||||
|
<NewConfigurationTemplate
|
||||||
|
:edit="true"
|
||||||
|
:isNew="true"
|
||||||
|
@remove="newTemplates = newTemplates.filter(x => x.TemplateID !== template.TemplateID)"
|
||||||
|
@update="newTemplates = newTemplates.filter(x => x.TemplateID !== template.TemplateID); getTemplates()"
|
||||||
|
@subnet="args => emits('subnet', args)"
|
||||||
|
@port="args => emits('port', args)"
|
||||||
|
:template="template"></NewConfigurationTemplate>
|
||||||
|
</div>
|
||||||
|
<div class="col-12" v-for="(template, index) in templates">
|
||||||
|
<NewConfigurationTemplate
|
||||||
|
:key="template.TemplateID"
|
||||||
|
:peersCount="calculateIP"
|
||||||
|
@remove="getTemplates()"
|
||||||
|
@update="getTemplates()"
|
||||||
|
@subnet="args => emits('subnet', args)"
|
||||||
|
@port="args => emits('port', args)"
|
||||||
|
:template="template"></NewConfigurationTemplate>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import {parseCidr} from "cidr-tools";
|
import {parseCidr, containsCidr, mergeCidr, expandCidr} from "cidr-tools";
|
||||||
import '@/utilities/wireguard.js'
|
import '@/utilities/wireguard.js'
|
||||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||||
@@ -7,10 +7,12 @@ import LocaleText from "@/components/text/localeText.vue";
|
|||||||
import {parseInterface, parsePeers} from "@/utilities/parseConfigurationFile.js";
|
import {parseInterface, parsePeers} from "@/utilities/parseConfigurationFile.js";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
import {exp} from "qrcode/lib/core/galois-field.js";
|
||||||
|
import NewConfigurationTemplates from "@/components/newConfigurationComponents/newConfigurationTemplates.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "newConfiguration",
|
name: "newConfiguration",
|
||||||
components: {LocaleText},
|
components: {NewConfigurationTemplates, LocaleText},
|
||||||
async setup(){
|
async setup(){
|
||||||
const store = WireguardConfigurationsStore()
|
const store = WireguardConfigurationsStore()
|
||||||
const protocols = ref([])
|
const protocols = ref([])
|
||||||
@@ -137,49 +139,57 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
'newConfiguration.Address'(newVal){
|
'newConfiguration.Address'(newVal){
|
||||||
let ele = document.querySelector("#Address");
|
let ele = document.querySelector("#Address");
|
||||||
ele.classList.remove("is-invalid", "is-valid")
|
if (ele){
|
||||||
try{
|
ele.classList.remove("is-invalid", "is-valid")
|
||||||
if (newVal.trim().split("/").filter(x => x.length > 0).length !== 2){
|
try{
|
||||||
throw Error()
|
this.numberOfAvailableIPs = 0
|
||||||
|
newVal.replace(" ", "").split(",").forEach(x => {
|
||||||
|
let p = parseCidr(x);
|
||||||
|
let i = Number(p.end - p.start);
|
||||||
|
this.numberOfAvailableIPs += i + 1;
|
||||||
|
})
|
||||||
|
ele.classList.add("is-valid")
|
||||||
|
}catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
this.numberOfAvailableIPs = "0";
|
||||||
|
ele.classList.add("is-invalid")
|
||||||
}
|
}
|
||||||
let p = parseCidr(newVal);
|
|
||||||
let i = p.end - p.start;
|
|
||||||
this.numberOfAvailableIPs = i.toLocaleString();
|
|
||||||
ele.classList.add("is-valid")
|
|
||||||
}catch (e) {
|
|
||||||
this.numberOfAvailableIPs = "0";
|
|
||||||
ele.classList.add("is-invalid")
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'newConfiguration.ListenPort'(newVal){
|
'newConfiguration.ListenPort'(newVal){
|
||||||
let ele = document.querySelector("#ListenPort");
|
let ele = document.querySelector("#ListenPort");
|
||||||
ele.classList.remove("is-invalid", "is-valid")
|
if (ele){
|
||||||
|
ele.classList.remove("is-invalid", "is-valid")
|
||||||
if (newVal < 0 || newVal > 65353 || !Number.isInteger(newVal)){
|
|
||||||
ele.classList.add("is-invalid")
|
if (newVal < 0 || newVal > 65353 || !Number.isInteger(newVal)){
|
||||||
}else{
|
ele.classList.add("is-invalid")
|
||||||
ele.classList.add("is-valid")
|
}else{
|
||||||
|
ele.classList.add("is-valid")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'newConfiguration.ConfigurationName'(newVal){
|
'newConfiguration.ConfigurationName'(newVal){
|
||||||
|
|
||||||
let ele = document.querySelector("#ConfigurationName");
|
let ele = document.querySelector("#ConfigurationName");
|
||||||
ele.classList.remove("is-invalid", "is-valid")
|
if (ele){
|
||||||
if (!/^[a-zA-Z0-9_=+.-]{1,15}$/.test(newVal) || newVal.length === 0 || this.store.Configurations.find(x => x.Name === newVal)){
|
ele.classList.remove("is-invalid", "is-valid")
|
||||||
ele.classList.add("is-invalid")
|
if (!/^[a-zA-Z0-9_=+.-]{1,15}$/.test(newVal) || newVal.length === 0 || this.store.Configurations.find(x => x.Name === newVal)){
|
||||||
}else{
|
ele.classList.add("is-invalid")
|
||||||
ele.classList.add("is-valid")
|
}else{
|
||||||
|
ele.classList.add("is-valid")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'newConfiguration.PrivateKey'(newVal){
|
'newConfiguration.PrivateKey'(newVal){
|
||||||
let ele = document.querySelector("#PrivateKey");
|
let ele = document.querySelector("#PrivateKey");
|
||||||
ele.classList.remove("is-invalid", "is-valid")
|
if (ele){
|
||||||
|
ele.classList.remove("is-invalid", "is-valid")
|
||||||
try{
|
|
||||||
wireguard.generatePublicKey(newVal)
|
try{
|
||||||
ele.classList.add("is-valid")
|
wireguard.generatePublicKey(newVal)
|
||||||
}catch (e) {
|
ele.classList.add("is-valid")
|
||||||
ele.classList.add("is-invalid")
|
}catch (e) {
|
||||||
|
ele.classList.add("is-invalid")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -304,6 +314,10 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<NewConfigurationTemplates
|
||||||
|
@subnet="args => this.newConfiguration.Address = args"
|
||||||
|
@port="args => this.newConfiguration.ListenPort = args"
|
||||||
|
></NewConfigurationTemplates>
|
||||||
<div class="card rounded-3 shadow">
|
<div class="card rounded-3 shadow">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<LocaleText t="Listen Port"></LocaleText>
|
<LocaleText t="Listen Port"></LocaleText>
|
||||||
|
Reference in New Issue
Block a user