mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 07:10:09 +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 itertools, random
|
||||||
import random
|
|
||||||
import shutil
|
import shutil
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import configparser
|
import configparser
|
||||||
@ -7,7 +6,6 @@ import hashlib
|
|||||||
import ipaddress
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
# Python Built-in Library
|
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -17,45 +15,30 @@ import urllib.error
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import bcrypt
|
import bcrypt
|
||||||
# PIP installed library
|
|
||||||
import ifcfg
|
import ifcfg
|
||||||
import psutil
|
import psutil
|
||||||
import pyotp
|
import pyotp
|
||||||
from flask import Flask, request, render_template, session, g
|
from flask import Flask, request, render_template, session, g
|
||||||
from json import JSONEncoder
|
from json import JSONEncoder
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
|
|
||||||
from icmplib import ping, traceroute
|
from icmplib import ping, traceroute
|
||||||
|
|
||||||
# Import other python files
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from flask.json.provider import DefaultJSONProvider
|
from flask.json.provider import DefaultJSONProvider
|
||||||
|
|
||||||
|
|
||||||
DASHBOARD_VERSION = 'v4.1'
|
DASHBOARD_VERSION = 'v4.1'
|
||||||
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
||||||
DB_PATH = os.path.join(CONFIGURATION_PATH, 'db')
|
DB_PATH = os.path.join(CONFIGURATION_PATH, 'db')
|
||||||
if not os.path.isdir(DB_PATH):
|
if not os.path.isdir(DB_PATH):
|
||||||
os.mkdir(DB_PATH)
|
os.mkdir(DB_PATH)
|
||||||
DASHBOARD_CONF = os.path.join(CONFIGURATION_PATH, 'wg-dashboard.ini')
|
DASHBOARD_CONF = os.path.join(CONFIGURATION_PATH, 'wg-dashboard.ini')
|
||||||
|
|
||||||
# WireGuard's configuration path
|
|
||||||
WG_CONF_PATH = None
|
WG_CONF_PATH = None
|
||||||
# Dashboard Config Name
|
|
||||||
# Upgrade Required
|
|
||||||
UPDATE = None
|
UPDATE = None
|
||||||
# Flask App Configuration
|
|
||||||
|
|
||||||
app = Flask("WGDashboard")
|
app = Flask("WGDashboard")
|
||||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
|
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
|
||||||
app.secret_key = secrets.token_urlsafe(32)
|
app.secret_key = secrets.token_urlsafe(32)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ModelEncoder(JSONEncoder):
|
class ModelEncoder(JSONEncoder):
|
||||||
def default(self, o: Any) -> Any:
|
def default(self, o: Any) -> Any:
|
||||||
if hasattr(o, 'toJson'):
|
if hasattr(o, 'toJson'):
|
||||||
@ -63,12 +46,10 @@ class ModelEncoder(JSONEncoder):
|
|||||||
else:
|
else:
|
||||||
return super(ModelEncoder, self).default(o)
|
return super(ModelEncoder, self).default(o)
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Classes
|
Classes
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
def ResponseObject(status=True, message=None, data=None) -> Flask.response_class:
|
def ResponseObject(status=True, message=None, data=None) -> Flask.response_class:
|
||||||
response = Flask.make_response(app, {
|
response = Flask.make_response(app, {
|
||||||
"status": status,
|
"status": status,
|
||||||
@ -325,7 +306,20 @@ class PeerJobs:
|
|||||||
self.Jobs))
|
self.Jobs))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(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):
|
def runJob(self):
|
||||||
needToDelete = []
|
needToDelete = []
|
||||||
for job in self.Jobs:
|
for job in self.Jobs:
|
||||||
@ -357,6 +351,10 @@ class PeerJobs:
|
|||||||
JobLogger.log(job.JobID, s["status"],
|
JobLogger.log(job.JobID, s["status"],
|
||||||
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
|
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
needToDelete.append(job)
|
||||||
|
else:
|
||||||
|
needToDelete.append(job)
|
||||||
for j in needToDelete:
|
for j in needToDelete:
|
||||||
self.deleteJob(j)
|
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()
|
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 = sqlSelect("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
|
||||||
existingTables = [t['name'] for t in existingTables]
|
existingTables = [t['name'] for t in existingTables]
|
||||||
if self.Name not in existingTables:
|
if dbName not in existingTables:
|
||||||
sqlUpdate(
|
sqlUpdate(
|
||||||
"""
|
"""
|
||||||
CREATE TABLE '%s'(
|
CREATE TABLE '%s'(
|
||||||
@ -555,10 +556,10 @@ class WireguardConfiguration:
|
|||||||
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
""" % self.Name
|
""" % dbName
|
||||||
)
|
)
|
||||||
|
|
||||||
if f'{self.Name}_restrict_access' not in existingTables:
|
if f'{dbName}_restrict_access' not in existingTables:
|
||||||
sqlUpdate(
|
sqlUpdate(
|
||||||
"""
|
"""
|
||||||
CREATE TABLE '%s_restrict_access' (
|
CREATE TABLE '%s_restrict_access' (
|
||||||
@ -570,9 +571,9 @@ class WireguardConfiguration:
|
|||||||
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
""" % self.Name
|
""" % dbName
|
||||||
)
|
)
|
||||||
if f'{self.Name}_transfer' not in existingTables:
|
if f'{dbName}_transfer' not in existingTables:
|
||||||
sqlUpdate(
|
sqlUpdate(
|
||||||
"""
|
"""
|
||||||
CREATE TABLE '%s_transfer' (
|
CREATE TABLE '%s_transfer' (
|
||||||
@ -580,9 +581,9 @@ class WireguardConfiguration:
|
|||||||
total_sent FLOAT NULL, total_data FLOAT NULL,
|
total_sent FLOAT NULL, total_data FLOAT NULL,
|
||||||
cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, time DATETIME
|
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(
|
sqlUpdate(
|
||||||
"""
|
"""
|
||||||
CREATE TABLE '%s_deleted' (
|
CREATE TABLE '%s_deleted' (
|
||||||
@ -594,7 +595,7 @@ class WireguardConfiguration:
|
|||||||
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
""" % self.Name
|
""" % dbName
|
||||||
)
|
)
|
||||||
|
|
||||||
def __dumpDatabase(self):
|
def __dumpDatabase(self):
|
||||||
@ -976,7 +977,7 @@ class WireguardConfiguration:
|
|||||||
os.mkdir(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup'))
|
os.mkdir(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], 'WGDashboard_Backup'))
|
||||||
time = datetime.now().strftime("%Y%m%d%H%M%S")
|
time = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
shutil.copy(
|
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')
|
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:
|
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)
|
os.remove(self.__configPath)
|
||||||
self.__dropDatabase()
|
self.__dropDatabase()
|
||||||
return True
|
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:
|
class Peer:
|
||||||
def __init__(self, tableData, configuration: WireguardConfiguration):
|
def __init__(self, tableData, configuration: WireguardConfiguration):
|
||||||
@ -1430,7 +1452,6 @@ class DashboardConfig:
|
|||||||
the_dict[section][key] = self.GetConfig(section, key)[1]
|
the_dict[section][key] = self.GetConfig(section, key)[1]
|
||||||
return the_dict
|
return the_dict
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Private Functions
|
Private Functions
|
||||||
'''
|
'''
|
||||||
@ -1583,13 +1604,10 @@ cors = CORS(app, resources={rf"{APP_PREFIX}/api/*": {
|
|||||||
"allow_headers": ["Content-Type", "wg-dashboard-apikey"]
|
"allow_headers": ["Content-Type", "wg-dashboard-apikey"]
|
||||||
}})
|
}})
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
API Routes
|
API Routes
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def auth_req():
|
def auth_req():
|
||||||
if request.method.lower() == 'options':
|
if request.method.lower() == 'options':
|
||||||
@ -1647,7 +1665,6 @@ def auth_req():
|
|||||||
def API_ValidateAPIKey():
|
def API_ValidateAPIKey():
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
|
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
|
||||||
def API_ValidateAuthentication():
|
def API_ValidateAuthentication():
|
||||||
token = request.cookies.get("authToken") + ""
|
token = request.cookies.get("authToken") + ""
|
||||||
@ -1655,7 +1672,6 @@ def API_ValidateAuthentication():
|
|||||||
return ResponseObject(False, "Invalid authentication.")
|
return ResponseObject(False, "Invalid authentication.")
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/authenticate')
|
@app.post(f'{APP_PREFIX}/api/authenticate')
|
||||||
def API_AuthenticateLogin():
|
def API_AuthenticateLogin():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@ -1691,20 +1707,17 @@ def API_AuthenticateLogin():
|
|||||||
else:
|
else:
|
||||||
return ResponseObject(False, "Sorry, your username or password is incorrect.")
|
return ResponseObject(False, "Sorry, your username or password is incorrect.")
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/signout')
|
@app.get(f'{APP_PREFIX}/api/signout')
|
||||||
def API_SignOut():
|
def API_SignOut():
|
||||||
resp = ResponseObject(True, "")
|
resp = ResponseObject(True, "")
|
||||||
resp.delete_cookie("authToken")
|
resp.delete_cookie("authToken")
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@app.route(f'{APP_PREFIX}/api/getWireguardConfigurations', methods=["GET"])
|
@app.route(f'{APP_PREFIX}/api/getWireguardConfigurations', methods=["GET"])
|
||||||
def API_getWireguardConfigurations():
|
def API_getWireguardConfigurations():
|
||||||
_getConfigurationList()
|
_getConfigurationList()
|
||||||
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.route(f'{APP_PREFIX}/api/addWireguardConfiguration', methods=["POST"])
|
||||||
def API_addWireguardConfiguration():
|
def API_addWireguardConfiguration():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@ -1751,7 +1764,6 @@ def API_addWireguardConfiguration():
|
|||||||
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data)
|
WireguardConfigurations[data['ConfigurationName']] = WireguardConfiguration(data=data)
|
||||||
return ResponseObject()
|
return ResponseObject()
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration/')
|
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration/')
|
||||||
def API_toggleWireguardConfiguration():
|
def API_toggleWireguardConfiguration():
|
||||||
configurationName = request.args.get('configurationName')
|
configurationName = request.args.get('configurationName')
|
||||||
@ -1788,6 +1800,21 @@ def API_deleteWireguardConfiguration():
|
|||||||
WireguardConfigurations.pop(data.get("Name"))
|
WireguardConfigurations.pop(data.get("Name"))
|
||||||
return ResponseObject(status)
|
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')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
|
||||||
def API_getWireguardConfigurationBackup():
|
def API_getWireguardConfigurationBackup():
|
||||||
configurationName = request.args.get('configurationName')
|
configurationName = request.args.get('configurationName')
|
||||||
@ -1876,7 +1903,6 @@ def API_restoreWireguardConfigurationBackup():
|
|||||||
def API_getDashboardConfiguration():
|
def API_getDashboardConfiguration():
|
||||||
return ResponseObject(data=DashboardConfig.toJson())
|
return ResponseObject(data=DashboardConfig.toJson())
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updateDashboardConfigurationItem')
|
@app.post(f'{APP_PREFIX}/api/updateDashboardConfigurationItem')
|
||||||
def API_updateDashboardConfigurationItem():
|
def API_updateDashboardConfigurationItem():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@ -1924,7 +1950,6 @@ def API_deleteDashboardAPIKey():
|
|||||||
return ResponseObject(True, data=DashboardConfig.DashboardAPIKeys)
|
return ResponseObject(True, data=DashboardConfig.DashboardAPIKeys)
|
||||||
return ResponseObject(False, "Dashboard API Keys function is disbaled")
|
return ResponseObject(False, "Dashboard API Keys function is disbaled")
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updatePeerSettings/<configName>')
|
@app.post(f'{APP_PREFIX}/api/updatePeerSettings/<configName>')
|
||||||
def API_updatePeerSettings(configName):
|
def API_updatePeerSettings(configName):
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@ -1970,7 +1995,6 @@ def API_deletePeers(configName: str) -> ResponseObject:
|
|||||||
|
|
||||||
return ResponseObject(False, "Configuration does not exist")
|
return ResponseObject(False, "Configuration does not exist")
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/restrictPeers/<configName>')
|
@app.post(f'{APP_PREFIX}/api/restrictPeers/<configName>')
|
||||||
def API_restrictPeers(configName: str) -> ResponseObject:
|
def API_restrictPeers(configName: str) -> ResponseObject:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@ -2139,7 +2163,6 @@ def API_addPeers(configName):
|
|||||||
|
|
||||||
return ResponseObject(False, "Configuration does not exist")
|
return ResponseObject(False, "Configuration does not exist")
|
||||||
|
|
||||||
|
|
||||||
@app.get(f"{APP_PREFIX}/api/downloadPeer/<configName>")
|
@app.get(f"{APP_PREFIX}/api/downloadPeer/<configName>")
|
||||||
def API_downloadPeer(configName):
|
def API_downloadPeer(configName):
|
||||||
data = request.args
|
data = request.args
|
||||||
@ -2150,8 +2173,6 @@ def API_downloadPeer(configName):
|
|||||||
if len(data['id']) == 0 or not peerFound:
|
if len(data['id']) == 0 or not peerFound:
|
||||||
return ResponseObject(False, "Peer does not exist")
|
return ResponseObject(False, "Peer does not exist")
|
||||||
return ResponseObject(data=peer.downloadPeer())
|
return ResponseObject(data=peer.downloadPeer())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.get(f"{APP_PREFIX}/api/downloadAllPeers/<configName>")
|
@app.get(f"{APP_PREFIX}/api/downloadAllPeers/<configName>")
|
||||||
def API_downloadAllPeers(configName):
|
def API_downloadAllPeers(configName):
|
||||||
@ -2168,13 +2189,11 @@ def API_downloadAllPeers(configName):
|
|||||||
peerData.append(file)
|
peerData.append(file)
|
||||||
return ResponseObject(data=peerData)
|
return ResponseObject(data=peerData)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f"{APP_PREFIX}/api/getAvailableIPs/<configName>")
|
@app.get(f"{APP_PREFIX}/api/getAvailableIPs/<configName>")
|
||||||
def API_getAvailableIPs(configName):
|
def API_getAvailableIPs(configName):
|
||||||
status, ips = _getWireguardConfigurationAvailableIP(configName)
|
status, ips = _getWireguardConfigurationAvailableIP(configName)
|
||||||
return ResponseObject(status=status, data=ips)
|
return ResponseObject(status=status, data=ips)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationInfo')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationInfo')
|
||||||
def API_getConfigurationInfo():
|
def API_getConfigurationInfo():
|
||||||
configurationName = request.args.get("configurationName")
|
configurationName = request.args.get("configurationName")
|
||||||
@ -2186,7 +2205,6 @@ def API_getConfigurationInfo():
|
|||||||
"configurationRestrictedPeers": WireguardConfigurations[configurationName].getRestrictedPeersList()
|
"configurationRestrictedPeers": WireguardConfigurations[configurationName].getRestrictedPeersList()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
|
@app.get(f'{APP_PREFIX}/api/getDashboardTheme')
|
||||||
def API_getDashboardTheme():
|
def API_getDashboardTheme():
|
||||||
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
return ResponseObject(data=DashboardConfig.GetConfig("Server", "dashboard_theme")[1])
|
||||||
@ -2245,13 +2263,10 @@ def API_getPeerScheduleJobLogs(configName):
|
|||||||
requestAll = True
|
requestAll = True
|
||||||
return ResponseObject(data=JobLogger.getLogs(requestAll, configName))
|
return ResponseObject(data=JobLogger.getLogs(requestAll, configName))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Tools
|
Tools
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/ping/getAllPeersIpAddress')
|
@app.get(f'{APP_PREFIX}/api/ping/getAllPeersIpAddress')
|
||||||
def API_ping_getAllPeersIpAddress():
|
def API_ping_getAllPeersIpAddress():
|
||||||
ips = {}
|
ips = {}
|
||||||
|
@ -4,6 +4,8 @@ import {onMounted, reactive, ref, useTemplateRef, watch} from "vue";
|
|||||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
import {fetchPost} from "@/utilities/fetch.js";
|
import {fetchPost} from "@/utilities/fetch.js";
|
||||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
import UpdateConfigurationName
|
||||||
|
from "@/components/configurationComponents/editConfigurationComponents/updateConfigurationName.vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
configurationInfo: Object
|
configurationInfo: Object
|
||||||
})
|
})
|
||||||
@ -47,6 +49,8 @@ const saveForm = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const updateConfigurationName = ref(false)
|
||||||
|
|
||||||
watch(data, () => {
|
watch(data, () => {
|
||||||
dataChanged.value = JSON.stringify(data) !== JSON.stringify(props.configurationInfo);
|
dataChanged.value = JSON.stringify(data) !== JSON.stringify(props.configurationInfo);
|
||||||
}, {
|
}, {
|
||||||
@ -67,120 +71,137 @@ watch(data, () => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body px-4 pb-4">
|
<div class="card-body px-4 pb-4">
|
||||||
<div class="d-flex gap-2 flex-column">
|
<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">
|
<small class="text-muted">
|
||||||
<LocaleText t="Name"></LocaleText>
|
<LocaleText t="Name"></LocaleText>
|
||||||
</small>
|
</small>
|
||||||
<small class="ms-auto"><samp>{{data.Name}}</samp></small>
|
<small>{{data.Name}}</small>
|
||||||
</div>
|
<button
|
||||||
<div class="d-flex align-items-center">
|
@click="updateConfigurationName = true"
|
||||||
<small class="text-muted">
|
class="btn btn-sm bg-danger-subtle border-danger-subtle text-danger-emphasis rounded-3 ms-auto">
|
||||||
<LocaleText t="Public Key"></LocaleText>
|
Update Name
|
||||||
</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>
|
|
||||||
</button>
|
</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>
|
</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>
|
</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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -199,9 +198,7 @@ export default {
|
|||||||
v-model="this.newConfiguration.PublicKey" disabled
|
v-model="this.newConfiguration.PublicKey" disabled
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card rounded-3 shadow">
|
<div class="card rounded-3 shadow">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
|
Loading…
Reference in New Issue
Block a user