mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-21 23:01:39 +01:00
Rename configuration done
This commit is contained in:
parent
3bc54a4e16
commit
4956b0d89d
121
src/dashboard.py
121
src/dashboard.py
@ -1,5 +1,4 @@
|
||||
import itertools
|
||||
import random
|
||||
import itertools, random
|
||||
import shutil
|
||||
import sqlite3
|
||||
import configparser
|
||||
@ -7,7 +6,6 @@ import hashlib
|
||||
import ipaddress
|
||||
import json
|
||||
import traceback
|
||||
# Python Built-in Library
|
||||
import os
|
||||
import secrets
|
||||
import subprocess
|
||||
@ -17,45 +15,30 @@ import urllib.error
|
||||
import uuid
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
import bcrypt
|
||||
# PIP installed library
|
||||
import ifcfg
|
||||
import psutil
|
||||
import pyotp
|
||||
from flask import Flask, request, render_template, session, g
|
||||
from json import JSONEncoder
|
||||
from flask_cors import CORS
|
||||
|
||||
from icmplib import ping, traceroute
|
||||
|
||||
# Import other python files
|
||||
import threading
|
||||
|
||||
from flask.json.provider import DefaultJSONProvider
|
||||
|
||||
|
||||
DASHBOARD_VERSION = 'v4.1'
|
||||
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
||||
DB_PATH = os.path.join(CONFIGURATION_PATH, 'db')
|
||||
if not os.path.isdir(DB_PATH):
|
||||
os.mkdir(DB_PATH)
|
||||
DASHBOARD_CONF = os.path.join(CONFIGURATION_PATH, 'wg-dashboard.ini')
|
||||
|
||||
# WireGuard's configuration path
|
||||
WG_CONF_PATH = None
|
||||
# Dashboard Config Name
|
||||
# Upgrade Required
|
||||
UPDATE = None
|
||||
# Flask App Configuration
|
||||
|
||||
app = Flask("WGDashboard")
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
|
||||
app.secret_key = secrets.token_urlsafe(32)
|
||||
|
||||
|
||||
|
||||
|
||||
class ModelEncoder(JSONEncoder):
|
||||
def default(self, o: Any) -> Any:
|
||||
if hasattr(o, 'toJson'):
|
||||
@ -63,12 +46,10 @@ class ModelEncoder(JSONEncoder):
|
||||
else:
|
||||
return super(ModelEncoder, self).default(o)
|
||||
|
||||
|
||||
'''
|
||||
Classes
|
||||
'''
|
||||
|
||||
|
||||
def ResponseObject(status=True, message=None, data=None) -> Flask.response_class:
|
||||
response = Flask.make_response(app, {
|
||||
"status": status,
|
||||
@ -325,7 +306,20 @@ class PeerJobs:
|
||||
self.Jobs))
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
|
||||
def updateJobConfigurationName(self, ConfigurationName: str, NewConfigurationName: str) -> tuple[bool, str]:
|
||||
try:
|
||||
with self.jobdb:
|
||||
jobdbCursor = self.jobdb.cursor()
|
||||
jobdbCursor.execute('''
|
||||
UPDATE PeerJobs SET Configuration = ? WHERE Configuration = ?
|
||||
''', (NewConfigurationName, ConfigurationName, ))
|
||||
self.jobdb.commit()
|
||||
self.__getJobs()
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
||||
|
||||
def runJob(self):
|
||||
needToDelete = []
|
||||
for job in self.Jobs:
|
||||
@ -357,6 +351,10 @@ class PeerJobs:
|
||||
JobLogger.log(job.JobID, s["status"],
|
||||
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
|
||||
)
|
||||
else:
|
||||
needToDelete.append(job)
|
||||
else:
|
||||
needToDelete.append(job)
|
||||
for j in needToDelete:
|
||||
self.deleteJob(j)
|
||||
|
||||
@ -540,10 +538,13 @@ class WireguardConfiguration:
|
||||
|
||||
existingTables = sqlSelect(f"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '{self.Name}%'").fetchall()
|
||||
|
||||
def __createDatabase(self):
|
||||
def __createDatabase(self, dbName = None):
|
||||
if dbName is None:
|
||||
dbName = self.Name
|
||||
|
||||
existingTables = sqlSelect("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
|
||||
existingTables = [t['name'] for t in existingTables]
|
||||
if self.Name not in existingTables:
|
||||
if dbName not in existingTables:
|
||||
sqlUpdate(
|
||||
"""
|
||||
CREATE TABLE '%s'(
|
||||
@ -555,10 +556,10 @@ class WireguardConfiguration:
|
||||
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||
PRIMARY KEY (id)
|
||||
)
|
||||
""" % self.Name
|
||||
""" % dbName
|
||||
)
|
||||
|
||||
if f'{self.Name}_restrict_access' not in existingTables:
|
||||
if f'{dbName}_restrict_access' not in existingTables:
|
||||
sqlUpdate(
|
||||
"""
|
||||
CREATE TABLE '%s_restrict_access' (
|
||||
@ -570,9 +571,9 @@ class WireguardConfiguration:
|
||||
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||
PRIMARY KEY (id)
|
||||
)
|
||||
""" % self.Name
|
||||
""" % dbName
|
||||
)
|
||||
if f'{self.Name}_transfer' not in existingTables:
|
||||
if f'{dbName}_transfer' not in existingTables:
|
||||
sqlUpdate(
|
||||
"""
|
||||
CREATE TABLE '%s_transfer' (
|
||||
@ -580,9 +581,9 @@ class WireguardConfiguration:
|
||||
total_sent FLOAT NULL, total_data FLOAT NULL,
|
||||
cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, time DATETIME
|
||||
)
|
||||
""" % self.Name
|
||||
""" % dbName
|
||||
)
|
||||
if f'{self.Name}_deleted' not in existingTables:
|
||||
if f'{dbName}_deleted' not in existingTables:
|
||||
sqlUpdate(
|
||||
"""
|
||||
CREATE TABLE '%s_deleted' (
|
||||
@ -594,7 +595,7 @@ class WireguardConfiguration:
|
||||
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||
PRIMARY KEY (id)
|
||||
)
|
||||
""" % self.Name
|
||||
""" % dbName
|
||||
)
|
||||
|
||||
def __dumpDatabase(self):
|
||||
@ -976,7 +977,7 @@ class WireguardConfiguration:
|
||||
os.mkdir(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup'))
|
||||
time = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
shutil.copy(
|
||||
os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'),
|
||||
self.__configPath,
|
||||
os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f'{self.Name}_{time}.conf')
|
||||
)
|
||||
with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup', f'{self.Name}_{time}.sql'), 'w+') as f:
|
||||
@ -1084,6 +1085,27 @@ class WireguardConfiguration:
|
||||
os.remove(self.__configPath)
|
||||
self.__dropDatabase()
|
||||
return True
|
||||
|
||||
def renameConfiguration(self, newConfigurationName) -> tuple[bool, str]:
|
||||
if newConfigurationName in WireguardConfigurations.keys():
|
||||
return False, "Configuration name already exist"
|
||||
try:
|
||||
if self.getStatus():
|
||||
self.toggleConfiguration()
|
||||
self.__createDatabase(newConfigurationName)
|
||||
sqlUpdate(f'INSERT INTO "{newConfigurationName}" SELECT * FROM "{self.Name}"')
|
||||
sqlUpdate(f'INSERT INTO "{newConfigurationName}_restrict_access" SELECT * FROM "{self.Name}_restrict_access"')
|
||||
sqlUpdate(f'INSERT INTO "{newConfigurationName}_deleted" SELECT * FROM "{self.Name}_deleted"')
|
||||
sqlUpdate(f'INSERT INTO "{newConfigurationName}_transfer" SELECT * FROM "{self.Name}_transfer"')
|
||||
AllPeerJobs.updateJobConfigurationName(self.Name, newConfigurationName)
|
||||
shutil.copy(
|
||||
self.__configPath,
|
||||
os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{newConfigurationName}.conf')
|
||||
)
|
||||
self.deleteConfiguration()
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
return True, None
|
||||
|
||||
class Peer:
|
||||
def __init__(self, tableData, configuration: WireguardConfiguration):
|
||||
@ -1430,7 +1452,6 @@ class DashboardConfig:
|
||||
the_dict[section][key] = self.GetConfig(section, key)[1]
|
||||
return the_dict
|
||||
|
||||
|
||||
'''
|
||||
Private Functions
|
||||
'''
|
||||
@ -1583,13 +1604,10 @@ cors = CORS(app, resources={rf"{APP_PREFIX}/api/*": {
|
||||
"allow_headers": ["Content-Type", "wg-dashboard-apikey"]
|
||||
}})
|
||||
|
||||
|
||||
'''
|
||||
API Routes
|
||||
'''
|
||||
|
||||
|
||||
|
||||
@app.before_request
|
||||
def auth_req():
|
||||
if request.method.lower() == 'options':
|
||||
@ -1647,7 +1665,6 @@ def auth_req():
|
||||
def API_ValidateAPIKey():
|
||||
return ResponseObject(True)
|
||||
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
|
||||
def API_ValidateAuthentication():
|
||||
token = request.cookies.get("authToken") + ""
|
||||
@ -1655,7 +1672,6 @@ def API_ValidateAuthentication():
|
||||
return ResponseObject(False, "Invalid authentication.")
|
||||
return ResponseObject(True)
|
||||
|
||||
|
||||
@app.post(f'{APP_PREFIX}/api/authenticate')
|
||||
def API_AuthenticateLogin():
|
||||
data = request.get_json()
|
||||
@ -1691,20 +1707,17 @@ def API_AuthenticateLogin():
|
||||
else:
|
||||
return ResponseObject(False, "Sorry, your username or password is incorrect.")
|
||||
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/signout')
|
||||
def API_SignOut():
|
||||
resp = ResponseObject(True, "")
|
||||
resp.delete_cookie("authToken")
|
||||
return resp
|
||||
|
||||
|
||||
@app.route(f'{APP_PREFIX}/api/getWireguardConfigurations', methods=["GET"])
|
||||
def API_getWireguardConfigurations():
|
||||
_getConfigurationList()
|
||||
return ResponseObject(data=[wc for wc in WireguardConfigurations.values()])
|
||||
|
||||
|
||||
@app.route(f'{APP_PREFIX}/api/addWireguardConfiguration', methods=["POST"])
|
||||
def API_addWireguardConfiguration():
|
||||
data = request.get_json()
|
||||
@ -1751,7 +1764,6 @@ def API_addWireguardConfiguration():
|
||||
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data)
|
||||
return ResponseObject()
|
||||
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration/')
|
||||
def API_toggleWireguardConfiguration():
|
||||
configurationName = request.args.get('configurationName')
|
||||
@ -1788,6 +1800,21 @@ def API_deleteWireguardConfiguration():
|
||||
WireguardConfigurations.pop(data.get("Name"))
|
||||
return ResponseObject(status)
|
||||
|
||||
@app.post(f'{APP_PREFIX}/api/renameWireguardConfiguration')
|
||||
def API_renameWireguardConfiguration():
|
||||
data = request.get_json()
|
||||
keys = ["Name", "NewConfigurationName"]
|
||||
for k in keys:
|
||||
if (k not in data.keys() or data.get(k) is None or len(data.get(k)) == 0 or
|
||||
(k == "Name" and data.get(k) not in WireguardConfigurations.keys())):
|
||||
return ResponseObject(False, "Please provide the configuration name you want to rename")
|
||||
|
||||
status, message = WireguardConfigurations[data.get("Name")].renameConfiguration(data.get("NewConfigurationName"))
|
||||
if status:
|
||||
WireguardConfigurations.pop(data.get("Name"))
|
||||
WireguardConfigurations[data.get("NewConfigurationName")] = WireguardConfiguration(data.get("NewConfigurationName"))
|
||||
return ResponseObject(status, message)
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
|
||||
def API_getWireguardConfigurationBackup():
|
||||
configurationName = request.args.get('configurationName')
|
||||
@ -1876,7 +1903,6 @@ def API_restoreWireguardConfigurationBackup():
|
||||
def API_getDashboardConfiguration():
|
||||
return ResponseObject(data=DashboardConfig.toJson())
|
||||
|
||||
|
||||
@app.post(f'{APP_PREFIX}/api/updateDashboardConfigurationItem')
|
||||
def API_updateDashboardConfigurationItem():
|
||||
data = request.get_json()
|
||||
@ -1924,7 +1950,6 @@ def API_deleteDashboardAPIKey():
|
||||
return ResponseObject(True, data=DashboardConfig.DashboardAPIKeys)
|
||||
return ResponseObject(False, "Dashboard API Keys function is disbaled")
|
||||
|
||||
|
||||
@app.post(f'{APP_PREFIX}/api/updatePeerSettings/<configName>')
|
||||
def API_updatePeerSettings(configName):
|
||||
data = request.get_json()
|
||||
@ -1970,7 +1995,6 @@ def API_deletePeers(configName: str) -> ResponseObject:
|
||||
|
||||
return ResponseObject(False, "Configuration does not exist")
|
||||
|
||||
|
||||
@app.post(f'{APP_PREFIX}/api/restrictPeers/<configName>')
|
||||
def API_restrictPeers(configName: str) -> ResponseObject:
|
||||
data = request.get_json()
|
||||
@ -2139,7 +2163,6 @@ def API_addPeers(configName):
|
||||
|
||||
return ResponseObject(False, "Configuration does not exist")
|
||||
|
||||
|
||||
@app.get(f"{APP_PREFIX}/api/downloadPeer/<configName>")
|
||||
def API_downloadPeer(configName):
|
||||
data = request.args
|
||||
@ -2150,8 +2173,6 @@ def API_downloadPeer(configName):
|
||||
if len(data['id']) == 0 or not peerFound:
|
||||
return ResponseObject(False, "Peer does not exist")
|
||||
return ResponseObject(data=peer.downloadPeer())
|
||||
|
||||
|
||||
|
||||
@app.get(f"{APP_PREFIX}/api/downloadAllPeers/<configName>")
|
||||
def API_downloadAllPeers(configName):
|
||||
@ -2168,13 +2189,11 @@ def API_downloadAllPeers(configName):
|
||||
peerData.append(file)
|
||||
return ResponseObject(data=peerData)
|
||||
|
||||
|
||||
@app.get(f"{APP_PREFIX}/api/getAvailableIPs/<configName>")
|
||||
def API_getAvailableIPs(configName):
|
||||
status, ips = _getWireguardConfigurationAvailableIP(configName)
|
||||
return ResponseObject(status=status, data=ips)
|
||||
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationInfo')
|
||||
def API_getConfigurationInfo():
|
||||
configurationName = request.args.get("configurationName")
|
||||
@ -2186,7 +2205,6 @@ def API_getConfigurationInfo():
|
||||
"configurationRestrictedPeers": WireguardConfigurations[configurationName].getRestrictedPeersList()
|
||||
})
|
||||
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
|
||||
def API_getDashboardTheme():
|
||||
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
||||
@ -2245,13 +2263,10 @@ def API_getPeerScheduleJobLogs(configName):
|
||||
requestAll = True
|
||||
return ResponseObject(data=JobLogger.getLogs(requestAll, configName))
|
||||
|
||||
|
||||
|
||||
'''
|
||||
Tools
|
||||
'''
|
||||
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/ping/getAllPeersIpAddress')
|
||||
def API_ping_getAllPeersIpAddress():
|
||||
ips = {}
|
||||
|
@ -4,6 +4,8 @@ import {onMounted, reactive, ref, useTemplateRef, watch} from "vue";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import UpdateConfigurationName
|
||||
from "@/components/configurationComponents/editConfigurationComponents/updateConfigurationName.vue";
|
||||
const props = defineProps({
|
||||
configurationInfo: Object
|
||||
})
|
||||
@ -47,6 +49,8 @@ const saveForm = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
const updateConfigurationName = ref(false)
|
||||
|
||||
watch(data, () => {
|
||||
dataChanged.value = JSON.stringify(data) !== JSON.stringify(props.configurationInfo);
|
||||
}, {
|
||||
@ -67,120 +71,137 @@ watch(data, () => {
|
||||
</div>
|
||||
<div class="card-body px-4 pb-4">
|
||||
<div class="d-flex gap-2 flex-column">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex align-items-center gap-3" v-if="!updateConfigurationName">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Name"></LocaleText>
|
||||
</small>
|
||||
<small class="ms-auto"><samp>{{data.Name}}</samp></small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Public Key"></LocaleText>
|
||||
</small>
|
||||
<small class="ms-auto"><samp>{{data.PublicKey}}</samp></small>
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<label for="configuration_private_key" class="form-label d-flex">
|
||||
<small class="text-muted d-block">
|
||||
<LocaleText t="Private Key"></LocaleText>
|
||||
</small>
|
||||
<div class="form-check form-switch ms-auto">
|
||||
<input class="form-check-input"
|
||||
type="checkbox" role="switch" id="editPrivateKeySwitch"
|
||||
v-model="editPrivateKey"
|
||||
>
|
||||
<label class="form-check-label" for="editPrivateKeySwitch">
|
||||
<small>Edit</small>
|
||||
</label>
|
||||
</div>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving || !editPrivateKey"
|
||||
:class="{'is-invalid': !reqField.PrivateKey}"
|
||||
@keyup="genKey()"
|
||||
v-model="data.PrivateKey"
|
||||
id="configuration_private_key">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_ipaddress_cidr" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="IP Address/CIDR"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.Address"
|
||||
id="configuration_ipaddress_cidr">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_listen_port" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Listen Port"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.ListenPort"
|
||||
id="configuration_listen_port">
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_preup" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PreUp"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PreUp"
|
||||
id="configuration_preup">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_predown" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PreDown"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PreDown"
|
||||
id="configuration_predown">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_postup" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PostUp"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PostUp"
|
||||
id="configuration_postup">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_postdown" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PostDown"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PostDown"
|
||||
id="configuration_postdown">
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 mt-4">
|
||||
<button class="btn bg-secondary-subtle border-secondary-subtle text-secondary-emphasis rounded-3 shadow ms-auto"
|
||||
@click="resetForm()"
|
||||
:disabled="!dataChanged || saving">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
<small>{{data.Name}}</small>
|
||||
<button
|
||||
@click="updateConfigurationName = true"
|
||||
class="btn btn-sm bg-danger-subtle border-danger-subtle text-danger-emphasis rounded-3 ms-auto">
|
||||
Update Name
|
||||
</button>
|
||||
<button class="btn bg-primary-subtle border-primary-subtle text-primary-emphasis rounded-3 shadow"
|
||||
:disabled="!dataChanged || saving"
|
||||
@click="saveForm()"
|
||||
>
|
||||
<i class="bi bi-save-fill"></i></button>
|
||||
</div>
|
||||
<UpdateConfigurationName
|
||||
@close="updateConfigurationName = false"
|
||||
:configuration-name="data.Name"
|
||||
v-if="updateConfigurationName"></UpdateConfigurationName>
|
||||
|
||||
<template v-else>
|
||||
<hr>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<small class="text-muted" style="word-break: keep-all">
|
||||
<LocaleText t="Public Key"></LocaleText>
|
||||
</small>
|
||||
<small class="ms-auto" style="word-break: break-all">
|
||||
{{data.PublicKey}}
|
||||
</small>
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<div class="d-flex">
|
||||
<label for="configuration_private_key" class="form-label">
|
||||
<small class="text-muted d-block">
|
||||
<LocaleText t="Private Key"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<div class="form-check form-switch ms-auto">
|
||||
<input class="form-check-input"
|
||||
type="checkbox" role="switch" id="editPrivateKeySwitch"
|
||||
v-model="editPrivateKey"
|
||||
>
|
||||
<label class="form-check-label" for="editPrivateKeySwitch">
|
||||
<small>Edit</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving || !editPrivateKey"
|
||||
:class="{'is-invalid': !reqField.PrivateKey}"
|
||||
@keyup="genKey()"
|
||||
v-model="data.PrivateKey"
|
||||
id="configuration_private_key">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_ipaddress_cidr" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="IP Address/CIDR"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.Address"
|
||||
id="configuration_ipaddress_cidr">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_listen_port" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Listen Port"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.ListenPort"
|
||||
id="configuration_listen_port">
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_preup" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PreUp"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PreUp"
|
||||
id="configuration_preup">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_predown" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PreDown"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PreDown"
|
||||
id="configuration_predown">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_postup" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PostUp"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PostUp"
|
||||
id="configuration_postup">
|
||||
</div>
|
||||
<div>
|
||||
<label for="configuration_postdown" class="form-label">
|
||||
<small class="text-muted">
|
||||
<LocaleText t="PostDown"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="saving"
|
||||
v-model="data.PostDown"
|
||||
id="configuration_postdown">
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 mt-4">
|
||||
<button class="btn bg-secondary-subtle border-secondary-subtle text-secondary-emphasis rounded-3 shadow ms-auto"
|
||||
@click="resetForm()"
|
||||
:disabled="!dataChanged || saving">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
<button class="btn bg-primary-subtle border-primary-subtle text-primary-emphasis rounded-3 shadow"
|
||||
:disabled="!dataChanged || saving"
|
||||
@click="saveForm()"
|
||||
>
|
||||
<i class="bi bi-save-fill"></i></button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,110 @@
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref, watch} from "vue";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {useRouter} from "vue-router";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
const props = defineProps({
|
||||
configurationName: String
|
||||
})
|
||||
const emit = defineEmits(['close'])
|
||||
const newConfigurationName = reactive({
|
||||
data: "",
|
||||
valid: false
|
||||
});
|
||||
const store = WireguardConfigurationsStore()
|
||||
|
||||
onMounted(() => {
|
||||
watch(() => newConfigurationName.data, (newVal) => {
|
||||
newConfigurationName.valid = /^[a-zA-Z0-9_=+.-]{1,15}$/.test(newVal) && newVal.length > 0 && !store.Configurations.find(x => x.Name === newVal);
|
||||
})
|
||||
})
|
||||
const dashboardConfigurationStore = DashboardConfigurationStore()
|
||||
const loading = ref(false)
|
||||
const router = useRouter()
|
||||
const rename = async () => {
|
||||
if (newConfigurationName.data){
|
||||
loading.value = true
|
||||
clearInterval(dashboardConfigurationStore.Peers.RefreshInterval)
|
||||
await fetchPost("/api/renameWireguardConfiguration", {
|
||||
Name: props.configurationName,
|
||||
NewConfigurationName: newConfigurationName.data
|
||||
}, async (res) => {
|
||||
if (res.status){
|
||||
await store.getConfigurations()
|
||||
dashboardConfigurationStore.newMessage("Server", "Configuration renamed", "success")
|
||||
router.push(`/configuration/${newConfigurationName.data}/peers`)
|
||||
}else{
|
||||
dashboardConfigurationStore.newMessage("Server", res.message, "danger")
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card rounded-3 flex-grow-1 bg-danger-subtle border-danger-subtle border shadow">
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<LocaleText t="To update this configuration's name, WGDashboard will execute the following operations:"></LocaleText>
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
<LocaleText t="Duplicate current configuration's database table and .conf file with the new name"></LocaleText>
|
||||
</li>
|
||||
<li>
|
||||
<LocaleText t="Delete current configuration's database table and .conf file"></LocaleText>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="d-flex align-items-center gap-3 inputGroup">
|
||||
<input class="form-control form-control-sm rounded-3" :value="configurationName" disabled>
|
||||
<h3 class="mb-0">
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</h3>
|
||||
<input class="form-control form-control-sm rounded-3"
|
||||
id="newConfigurationName"
|
||||
:class="[newConfigurationName.data ? (newConfigurationName.valid ? 'is-valid' : 'is-invalid') : '']"
|
||||
v-model="newConfigurationName.data">
|
||||
</div>
|
||||
<div class="invalid-feedback" :class="{'d-block': !newConfigurationName.valid && newConfigurationName.data}">
|
||||
<LocaleText t="Configuration name is invalid. Possible reasons:"></LocaleText>
|
||||
<ul class="mb-0">
|
||||
<li>
|
||||
<LocaleText t="Configuration name already exist."></LocaleText>
|
||||
</li>
|
||||
<li>
|
||||
<LocaleText t="Configuration name can only contain 15 lower/uppercase alphabet, numbers, underscore, equal sign, plus sign, period and hyphen."></LocaleText>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="d-flex mt-3">
|
||||
<button
|
||||
@click="emit('close')"
|
||||
class="btn btn-sm bg-secondary-subtle border-secondary-subtle text-secondary-emphasis rounded-3">
|
||||
<LocaleText t="Cancel"></LocaleText>
|
||||
</button>
|
||||
<button
|
||||
@click="rename()"
|
||||
:disabled="!newConfigurationName.data || loading"
|
||||
class="btn btn-sm btn-danger rounded-3 ms-auto">
|
||||
<LocaleText t="Save"></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@media screen and (max-width: 567px) {
|
||||
.inputGroup{
|
||||
flex-direction: column;
|
||||
|
||||
h3{
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -165,7 +165,6 @@ export default {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -199,9 +198,7 @@ export default {
|
||||
v-model="this.newConfiguration.PublicKey" disabled
|
||||
>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card rounded-3 shadow">
|
||||
<div class="card-header">
|
||||
|
Loading…
Reference in New Issue
Block a user