mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 23:27:45 +01:00
Brand new switch button and toast UI
This commit is contained in:
parent
2d3dffe5fc
commit
bdd984a887
@ -537,4 +537,3 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
||||||
|
|
||||||
|
2
src/api.py
Normal file
2
src/api.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from dashboard import request, jsonify, app
|
||||||
|
|
@ -28,8 +28,7 @@ from icmplib import ping, traceroute
|
|||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
|
|
||||||
# Import other python files
|
# Import other python files
|
||||||
from util import regex_match, check_DNS, check_Allowed_IPs, check_remote_endpoint, \
|
from util import *
|
||||||
check_IP_with_range, clean_IP_with_range
|
|
||||||
|
|
||||||
# Dashboard Version
|
# Dashboard Version
|
||||||
DASHBOARD_VERSION = 'v3.0.5'
|
DASHBOARD_VERSION = 'v3.0.5'
|
||||||
@ -375,6 +374,12 @@ def get_all_peers_data(config_name):
|
|||||||
get_endpoint(config_name)
|
get_endpoint(config_name)
|
||||||
get_allowed_ip(conf_peer_data, config_name)
|
get_allowed_ip(conf_peer_data, config_name)
|
||||||
|
|
||||||
|
def getLockAccessPeers(config_name):
|
||||||
|
col = g.cur.execute(f"PRAGMA table_info({config_name}_restrict_access)").fetchall()
|
||||||
|
col = [a[1] for a in col]
|
||||||
|
data = g.cur.execute(f"SELECT * FROM {config_name}_restrict_access").fetchall()
|
||||||
|
result = [{col[i]: data[k][i] for i in range(len(col))} for k in range(len(data))]
|
||||||
|
return result
|
||||||
|
|
||||||
def get_peers(config_name, search, sort_t):
|
def get_peers(config_name, search, sort_t):
|
||||||
"""
|
"""
|
||||||
@ -504,6 +509,18 @@ def get_conf_list():
|
|||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
g.cur.execute(create_table)
|
g.cur.execute(create_table)
|
||||||
|
create_table = f"""
|
||||||
|
CREATE TABLE IF NOT EXISTS {i}_restrict_access (
|
||||||
|
id VARCHAR NOT NULL, private_key VARCHAR NULL, DNS VARCHAR NULL,
|
||||||
|
endpoint_allowed_ip VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL,
|
||||||
|
total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
|
||||||
|
status VARCHAR NULL, latest_handshake VARCHAR NULL, allowed_ip VARCHAR NULL,
|
||||||
|
cumu_receive FLOAT NULL, cumu_sent FLOAT NULL, cumu_data FLOAT NULL, mtu INT NULL,
|
||||||
|
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
g.cur.execute(create_table)
|
||||||
temp = {"conf": i, "status": get_conf_status(i), "public_key": get_conf_pub_key(i)}
|
temp = {"conf": i, "status": get_conf_status(i), "public_key": get_conf_pub_key(i)}
|
||||||
if temp['status'] == "running":
|
if temp['status'] == "running":
|
||||||
temp['checked'] = 'checked'
|
temp['checked'] = 'checked'
|
||||||
@ -620,7 +637,6 @@ def f_available_ips(config_name):
|
|||||||
Flask Functions
|
Flask Functions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@app.teardown_request
|
@app.teardown_request
|
||||||
def close_DB(exception):
|
def close_DB(exception):
|
||||||
"""
|
"""
|
||||||
@ -1056,7 +1072,8 @@ def get_conf(config_name):
|
|||||||
"wg_ip": wg_ip,
|
"wg_ip": wg_ip,
|
||||||
"sort_tag": sort,
|
"sort_tag": sort,
|
||||||
"dashboard_refresh_interval": int(config.get("Server", "dashboard_refresh_interval")),
|
"dashboard_refresh_interval": int(config.get("Server", "dashboard_refresh_interval")),
|
||||||
"peer_display_mode": peer_display_mode
|
"peer_display_mode": peer_display_mode,
|
||||||
|
"lock_access_peers": getLockAccessPeers(config_name)
|
||||||
}
|
}
|
||||||
if result['data']['status'] == "stopped":
|
if result['data']['status'] == "stopped":
|
||||||
result['data']['checked'] = "nope"
|
result['data']['checked'] = "nope"
|
||||||
@ -1087,16 +1104,15 @@ def switch(config_name):
|
|||||||
shell=True, stderr=subprocess.STDOUT)
|
shell=True, stderr=subprocess.STDOUT)
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
session["switch_msg"] = exc.output.strip().decode("utf-8")
|
session["switch_msg"] = exc.output.strip().decode("utf-8")
|
||||||
return redirect('/')
|
return jsonify({"status": False, "reason":"Can't stop peer"})
|
||||||
elif status == "stopped":
|
elif status == "stopped":
|
||||||
try:
|
try:
|
||||||
subprocess.check_output("wg-quick up " + config_name,
|
subprocess.check_output("wg-quick up " + config_name,
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
shell=True, stderr=subprocess.STDOUT)
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
session["switch_msg"] = exc.output.strip().decode("utf-8")
|
session["switch_msg"] = exc.output.strip().decode("utf-8")
|
||||||
return redirect('/')
|
return jsonify({"status": False, "reason":"Can't turn on peer"})
|
||||||
return redirect(request.referrer)
|
return jsonify({"status": True, "reason":""})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/add_peer_bulk/<config_name>', methods=['POST'])
|
@app.route('/add_peer_bulk/<config_name>', methods=['POST'])
|
||||||
def add_peer_bulk(config_name):
|
def add_peer_bulk(config_name):
|
||||||
@ -1240,24 +1256,7 @@ def remove_peer(config_name):
|
|||||||
if not isinstance(keys, list):
|
if not isinstance(keys, list):
|
||||||
return config_name + " is not running."
|
return config_name + " is not running."
|
||||||
else:
|
else:
|
||||||
sql_command = []
|
return deletePeers(config_name, delete_keys, g.cur, g.db)
|
||||||
wg_command = ["wg", "set", config_name]
|
|
||||||
for delete_key in delete_keys:
|
|
||||||
if delete_key not in keys:
|
|
||||||
return "This key does not exist"
|
|
||||||
sql_command.append("DELETE FROM " + config_name + " WHERE id = '" + delete_key + "';")
|
|
||||||
wg_command.append("peer")
|
|
||||||
wg_command.append(delete_key)
|
|
||||||
wg_command.append("remove")
|
|
||||||
try:
|
|
||||||
remove_wg = subprocess.check_output(" ".join(wg_command),
|
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
|
||||||
save_wg = subprocess.check_output(f"wg-quick save {config_name}", shell=True, stderr=subprocess.STDOUT)
|
|
||||||
g.cur.executescript(' '.join(sql_command))
|
|
||||||
g.db.commit()
|
|
||||||
except subprocess.CalledProcessError as exc:
|
|
||||||
return exc.output.strip()
|
|
||||||
return "true"
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/save_peer_setting/<config_name>', methods=['POST'])
|
@app.route('/save_peer_setting/<config_name>', methods=['POST'])
|
||||||
@ -1530,6 +1529,51 @@ def switch_display_mode(mode):
|
|||||||
return "false"
|
return "false"
|
||||||
|
|
||||||
|
|
||||||
|
# APIs
|
||||||
|
@app.route('/api/togglePeerAccess', methods=['POST'])
|
||||||
|
def togglePeerAccess():
|
||||||
|
data = request.get_json()
|
||||||
|
print(data['peerID'])
|
||||||
|
returnData = {"status": True, "reason": ""}
|
||||||
|
required = ['peerID', 'config']
|
||||||
|
if checkJSONAllParameter(required, data):
|
||||||
|
checkUnlock = g.cur.execute(f"SELECT * FROM {data['config']} WHERE id='{data['peerID']}'").fetchone()
|
||||||
|
if checkUnlock:
|
||||||
|
moveUnlockToLock = g.cur.execute(f"INSERT INTO {data['config']}_restrict_access SELECT * FROM {data['config']} WHERE id = '{data['peerID']}'")
|
||||||
|
if g.cur.rowcount == 1:
|
||||||
|
print(g.cur.rowcount)
|
||||||
|
print(deletePeers(data['config'], [data['peerID']], g.cur, g.db))
|
||||||
|
else:
|
||||||
|
moveLockToUnlock = g.cur.execute(f"SELECT * FROM {data['config']}_restrict_access WHERE id='{data['peerID']}'").fetchone()
|
||||||
|
try:
|
||||||
|
if len(moveLockToUnlock[-1]) == 0:
|
||||||
|
status = subprocess.check_output(f"wg set {data['config']} peer {moveLockToUnlock[0]} allowed-ips {moveLockToUnlock[11]}",
|
||||||
|
shell=True, stderr=subprocess.STDOUT)
|
||||||
|
else:
|
||||||
|
now = str(datetime.now().strftime("%m%d%Y%H%M%S"))
|
||||||
|
f_name = now + "_tmp_psk.txt"
|
||||||
|
f = open(f_name, "w+")
|
||||||
|
f.write(moveLockToUnlock[-1])
|
||||||
|
f.close()
|
||||||
|
subprocess.check_output(f"wg set {data['config']} peer {moveLockToUnlock[0]} allowed-ips {moveLockToUnlock[11]} preshared-key {f_name}",
|
||||||
|
shell=True, stderr=subprocess.STDOUT)
|
||||||
|
os.remove(f_name)
|
||||||
|
status = subprocess.check_output(f"wg-quick save {data['config']}", shell=True, stderr=subprocess.STDOUT)
|
||||||
|
g.cur.execute(f"INSERT INTO {data['config']} SELECT * FROM {data['config']}_restrict_access WHERE id = '{data['peerID']}'")
|
||||||
|
if g.cur.rowcount == 1:
|
||||||
|
g.cur.execute(f"DELETE FROM {data['config']}_restrict_access WHERE id = '{data['peerID']}'")
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError as exc:
|
||||||
|
returnData["status"] = False
|
||||||
|
returnData["reason"] = exc.output.strip()
|
||||||
|
else:
|
||||||
|
returnData["status"] = False
|
||||||
|
returnData["reason"] = "Please provide all required parameters."
|
||||||
|
|
||||||
|
return jsonify(returnData)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Dashboard Tools Related
|
Dashboard Tools Related
|
||||||
"""
|
"""
|
||||||
@ -1625,6 +1669,7 @@ def init_dashboard():
|
|||||||
Create dashboard default configuration.
|
Create dashboard default configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# Set Default INI File
|
# Set Default INI File
|
||||||
if not os.path.isfile(DASHBOARD_CONF):
|
if not os.path.isfile(DASHBOARD_CONF):
|
||||||
open(DASHBOARD_CONF, "w+").close()
|
open(DASHBOARD_CONF, "w+").close()
|
||||||
|
@ -22,8 +22,10 @@ body {
|
|||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 100; /* Behind the navbar */
|
z-index: 100;
|
||||||
padding: 48px 0 0; /* Height of navbar */
|
/* Behind the navbar */
|
||||||
|
padding: 48px 0 0;
|
||||||
|
/* Height of navbar */
|
||||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +35,8 @@ body {
|
|||||||
height: calc(100vh - 48px);
|
height: calc(100vh - 48px);
|
||||||
padding-top: .5rem;
|
padding-top: .5rem;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
|
overflow-y: auto;
|
||||||
|
/* Scrollable contents if viewport is shorter than content. */
|
||||||
}
|
}
|
||||||
|
|
||||||
@supports ((position: -webkit-sticky) or (position: sticky)) {
|
@supports ((position: -webkit-sticky) or (position: sticky)) {
|
||||||
@ -73,6 +76,7 @@ body {
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Navbar
|
* Navbar
|
||||||
*/
|
*/
|
||||||
@ -142,7 +146,8 @@ body {
|
|||||||
|
|
||||||
.info h6 {
|
.info h6 {
|
||||||
line-break: anywhere;
|
line-break: anywhere;
|
||||||
transition: 0.2s ease-in-out;
|
transition: all 0.4s cubic-bezier(0.96, -0.07, 0.34, 1.01);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info .row .col-sm {
|
.info .row .col-sm {
|
||||||
@ -152,7 +157,6 @@ body {
|
|||||||
|
|
||||||
.info .row .col-sm small {
|
.info .row .col-sm small {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info .row .col-sm small strong:last-child(1) {
|
.info .row .col-sm small strong:last-child(1) {
|
||||||
@ -164,7 +168,8 @@ body {
|
|||||||
padding: 0 1rem 0 0;
|
padding: 0 1rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-control:active, .btn-control:focus{
|
.btn-control:active,
|
||||||
|
.btn-control:focus {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@ -174,12 +179,14 @@ body {
|
|||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-qrcode-peer:active, .btn-qrcode-peer:hover{
|
.btn-qrcode-peer:active,
|
||||||
|
.btn-qrcode-peer:hover {
|
||||||
transform: scale(0.9) rotate(180deg);
|
transform: scale(0.9) rotate(180deg);
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-download-peer:active, .btn-download-peer:hover{
|
.btn-download-peer:active,
|
||||||
|
.btn-download-peer:hover {
|
||||||
color: #17a2b8 !important;
|
color: #17a2b8 !important;
|
||||||
transform: translateY(5px);
|
transform: translateY(5px);
|
||||||
}
|
}
|
||||||
@ -199,7 +206,15 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-lock-peer:hover {
|
.btn-lock-peer:hover {
|
||||||
color: #6c757d;
|
color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lock-peer.lock{
|
||||||
|
color: #6c757d
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lock-peer.lock:hover{
|
||||||
|
color: #6c757d
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-setting-peer:hover {
|
.btn-setting-peer:hover {
|
||||||
@ -223,6 +238,7 @@ body {
|
|||||||
.switch {
|
.switch {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch:hover {
|
.switch:hover {
|
||||||
text-decoration: none
|
text-decoration: none
|
||||||
}
|
}
|
||||||
@ -253,6 +269,9 @@ body {
|
|||||||
|
|
||||||
.index-switch {
|
.index-switch {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -267,7 +286,6 @@ main{
|
|||||||
.add_btn {
|
.add_btn {
|
||||||
bottom: 1.5rem !important;
|
bottom: 1.5rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peer_list {
|
.peer_list {
|
||||||
margin-bottom: 7rem !important;
|
margin-bottom: 7rem !important;
|
||||||
}
|
}
|
||||||
@ -342,8 +360,10 @@ main{
|
|||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes rotating
|
||||||
|
/* Safari and Chrome */
|
||||||
|
|
||||||
@-webkit-keyframes rotating /* Safari and Chrome */ {
|
{
|
||||||
from {
|
from {
|
||||||
-webkit-transform: rotate(0deg);
|
-webkit-transform: rotate(0deg);
|
||||||
-o-transform: rotate(0deg);
|
-o-transform: rotate(0deg);
|
||||||
@ -355,6 +375,7 @@ main{
|
|||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes rotating {
|
@keyframes rotating {
|
||||||
from {
|
from {
|
||||||
-ms-transform: rotate(0deg);
|
-ms-transform: rotate(0deg);
|
||||||
@ -388,7 +409,10 @@ main{
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#peer_private_key_textbox, #private_key, #public_key, #peer_preshared_key_textbox{
|
#peer_private_key_textbox,
|
||||||
|
#private_key,
|
||||||
|
#public_key,
|
||||||
|
#peer_preshared_key_textbox {
|
||||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,22 +446,28 @@ main{
|
|||||||
/*padding: 0.6rem 0.9em;*/
|
/*padding: 0.6rem 0.9em;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#username, #password{
|
#username,
|
||||||
|
#password {
|
||||||
padding: 0.6rem calc( 0.9rem + 32px);
|
padding: 0.6rem calc( 0.9rem + 32px);
|
||||||
height: inherit;
|
height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
label[for="username"], label[for="password"]{
|
label[for="username"],
|
||||||
|
label[for="password"] {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
transform: translateY(30px) translateX(16px);
|
transform: translateY(30px) translateX(16px);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*label[for="password"]{*/
|
|
||||||
/* transform: translateY(32px) translateX(16px);*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
|
/*label[for="password"]{*/
|
||||||
|
|
||||||
|
|
||||||
|
/* transform: translateY(32px) translateX(16px);*/
|
||||||
|
|
||||||
|
|
||||||
|
/*}*/
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@ -458,6 +488,7 @@ label[for="username"], label[for="password"]{
|
|||||||
background-color: #dfdfdf;
|
background-color: #dfdfdf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@-moz-keyframes loading {
|
@-moz-keyframes loading {
|
||||||
0% {
|
0% {
|
||||||
background-color: #dfdfdf;
|
background-color: #dfdfdf;
|
||||||
@ -480,10 +511,13 @@ label[for="username"], label[for="password"]{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.info_loading {
|
.info_loading {
|
||||||
animation: loading 2s infinite ease-in-out;
|
/* animation: loading 2s infinite ease-in-out;
|
||||||
border-radius: 5px;
|
/* border-radius: 5px; */
|
||||||
height: 19px;
|
height: 19.19px;
|
||||||
transition: 0.3s ease-in-out;
|
/* transition: 0.3s ease-in-out; */
|
||||||
|
|
||||||
|
/* transform: translateX(40px); */
|
||||||
|
opacity: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#conf_status_btn {
|
#conf_status_btn {
|
||||||
@ -500,7 +534,8 @@ label[for="username"], label[for="password"]{
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#selected_ip_list .badge, #selected_peer_list .badge{
|
#selected_ip_list .badge,
|
||||||
|
#selected_peer_list .badge {
|
||||||
margin: 0.1rem
|
margin: 0.1rem
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,14 +603,20 @@ pre.index-alert{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.peerName {
|
.peerName {
|
||||||
margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
margin: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peerLightContainer {
|
.peerLightContainer {
|
||||||
text-transform: uppercase; margin: 0; margin-left: auto !important;
|
text-transform: uppercase;
|
||||||
|
margin: 0;
|
||||||
|
margin-left: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conf_card .dot, .info .dot {
|
.conf_card .dot,
|
||||||
|
.info .dot {
|
||||||
transform: translateX(10px);
|
transform: translateX(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,3 +665,99 @@ pre.index-alert{
|
|||||||
height: calc( 100% - 47px) !important;
|
height: calc( 100% - 47px) !important;
|
||||||
max-height: calc( 100% - 47px) !important;
|
max-height: calc( 100% - 47px) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#switch{
|
||||||
|
transition: all 350ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle--switch{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleLabel{
|
||||||
|
width: 64px;
|
||||||
|
height: 32px;
|
||||||
|
background-color: #6c757d17;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
border: 2px solid #6c757d8c;
|
||||||
|
border-radius: 100px;
|
||||||
|
transition: all 350ms ease-in;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle--switch.waiting + .toggleLabel{
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleLabel::before{
|
||||||
|
background-color: #6c757d;
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
content: "";
|
||||||
|
border-radius: 100px;
|
||||||
|
margin: 1px;
|
||||||
|
position: absolute;
|
||||||
|
animation-name: off;
|
||||||
|
animation-duration: 350ms;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
transition: all 350ms ease-in;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle--switch:checked + .toggleLabel{
|
||||||
|
background-color: #007bff17 !important;
|
||||||
|
border: 2px solid #007bff8c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle--switch:checked + .toggleLabel::before{
|
||||||
|
background-color: #007bff;
|
||||||
|
animation-name: on;
|
||||||
|
animation-duration: 350ms;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes on {
|
||||||
|
0%{
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
60%{
|
||||||
|
left: 0px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
left: 32px;
|
||||||
|
width: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes off {
|
||||||
|
0%{
|
||||||
|
left: 32px;
|
||||||
|
}
|
||||||
|
60%{
|
||||||
|
left: 18px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
left: 0px;
|
||||||
|
width: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast{
|
||||||
|
min-width: 300px;
|
||||||
|
background-color: rgba(255,255,255,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-header{
|
||||||
|
background-color: rgba(255,255,255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-progressbar{
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #007bff;
|
||||||
|
border-bottom-left-radius: .25rem;
|
||||||
|
}
|
2
src/static/css/dashboard.min.css
vendored
2
src/static/css/dashboard.min.css
vendored
File diff suppressed because one or more lines are too long
@ -9,7 +9,6 @@
|
|||||||
/**
|
/**
|
||||||
* Definitions
|
* Definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let peers = [];
|
let peers = [];
|
||||||
let configuration_name;
|
let configuration_name;
|
||||||
let configuration_interval;
|
let configuration_interval;
|
||||||
@ -63,8 +62,7 @@
|
|||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
|
||||||
label: 'Data Sent',
|
label: 'Data Sent',
|
||||||
data: [],
|
data: [],
|
||||||
stroke: '#FFFFFF',
|
stroke: '#FFFFFF',
|
||||||
@ -224,17 +222,19 @@
|
|||||||
* @param response
|
* @param response
|
||||||
*/
|
*/
|
||||||
function configurationHeader(response) {
|
function configurationHeader(response) {
|
||||||
let $conf_status_btn = document.getElementById("conf_status_btn");
|
let $conf_status_btn = $(".toggle--switch");
|
||||||
|
|
||||||
if (response.checked === "checked") {
|
if (response.checked === "checked") {
|
||||||
$conf_status_btn.innerHTML = `<a href="#" id="${response.name}" ${response.checked} class="switch text-primary"><i class="bi bi-toggle2-on"></i> ON</a>`;
|
$conf_status_btn.prop("checked", true)
|
||||||
}else{
|
}else{
|
||||||
$conf_status_btn.innerHTML = `<a href="#" id="${response.name}" ${response.checked} class="switch text-primary"><i class="bi bi-toggle2-off"></i> OFF</a>`;
|
$conf_status_btn.prop("checked", false)
|
||||||
}
|
}
|
||||||
|
$conf_status_btn.data("conf-id", configuration_name)
|
||||||
|
|
||||||
|
|
||||||
if (response.running_peer > 0) {
|
if (response.running_peer > 0) {
|
||||||
let d = new Date();
|
let d = new Date();
|
||||||
let time = d.toLocaleString("en-us",
|
let time = d.toLocaleString("en-us", { hour: '2-digit', minute: '2-digit', second: "2-digit", hourCycle: 'h23' });
|
||||||
{hour: '2-digit', minute: '2-digit', second: "2-digit", hourCycle: 'h23'});
|
|
||||||
totalDataUsageChartObj.data.labels.push(`${time}`);
|
totalDataUsageChartObj.data.labels.push(`${time}`);
|
||||||
|
|
||||||
if (totalDataUsageChartObj.data.datasets[0].data.length === 0) {
|
if (totalDataUsageChartObj.data.datasets[0].data.length === 0) {
|
||||||
@ -254,8 +254,7 @@
|
|||||||
let k = 0;
|
let k = 0;
|
||||||
if (chartUnit === "MB") {
|
if (chartUnit === "MB") {
|
||||||
k = 1024;
|
k = 1024;
|
||||||
}
|
} else if (chartUnit === "KB") {
|
||||||
else if (chartUnit === "KB"){
|
|
||||||
k = 1048576;
|
k = 1048576;
|
||||||
} else {
|
} else {
|
||||||
k = 1;
|
k = 1;
|
||||||
@ -270,11 +269,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.querySelector("#conf_name").textContent = configuration_name;
|
document.querySelector("#conf_name").textContent = configuration_name;
|
||||||
$conf_status_btn.classList.remove("info_loading");
|
$("#switch").removeClass("info_loading");
|
||||||
document.querySelectorAll("#sort_by_dropdown option").forEach(ele => ele.removeAttribute("selected"));
|
document.querySelectorAll("#sort_by_dropdown option").forEach(ele => ele.removeAttribute("selected"));
|
||||||
document.querySelector(`#sort_by_dropdown option[value="${response.sort_tag}"]`).setAttribute("selected", "selected");
|
document.querySelector(`#sort_by_dropdown option[value="${response.sort_tag}"]`).setAttribute("selected", "selected");
|
||||||
// document.querySelectorAll(".interval-btn-group button").forEach(ele => ele.classList.remove("active"));
|
|
||||||
// document.querySelector(`button[data-refresh-interval="${response.dashboard_refresh_interval}"]`).classList.add("active");
|
|
||||||
document.querySelector("#conf_status").innerHTML = `${response.status}<span class="dot dot-${response.status}"></span>`;
|
document.querySelector("#conf_status").innerHTML = `${response.status}<span class="dot dot-${response.status}"></span>`;
|
||||||
document.querySelector("#conf_connected_peers").innerHTML = response.running_peer;
|
document.querySelector("#conf_connected_peers").innerHTML = response.running_peer;
|
||||||
document.querySelector("#conf_total_data_usage").innerHTML = `${response.total_data_usage[0]} GB`;
|
document.querySelector("#conf_total_data_usage").innerHTML = `${response.total_data_usage[0]} GB`;
|
||||||
@ -283,9 +280,15 @@
|
|||||||
document.querySelector("#conf_public_key").innerHTML = response.public_key;
|
document.querySelector("#conf_public_key").innerHTML = response.public_key;
|
||||||
document.querySelector("#conf_listen_port").innerHTML = response.listen_port === "" ? "N/A" : response.listen_port;
|
document.querySelector("#conf_listen_port").innerHTML = response.listen_port === "" ? "N/A" : response.listen_port;
|
||||||
document.querySelector("#conf_address").innerHTML = response.conf_address;
|
document.querySelector("#conf_address").innerHTML = response.conf_address;
|
||||||
document.querySelectorAll(".info h6").forEach(ele => ele.classList.remove("info_loading"));
|
let delay = 0;
|
||||||
|
let h6 = $(".info h6");
|
||||||
|
for (let i = 0; i < h6.length; i++){
|
||||||
|
setTimeout(function(){
|
||||||
|
$(h6[i]).removeClass("info_loading");
|
||||||
|
}, delay)
|
||||||
|
delay += 40
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse all responded information onto the peers list
|
* Parse all responded information onto the peers list
|
||||||
* @param response
|
* @param response
|
||||||
@ -324,14 +327,14 @@
|
|||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<hr>
|
<hr>
|
||||||
<div class="button-group" style="display:flex">
|
<div class="button-group" style="display:flex">
|
||||||
<button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="${peer.id}" data-toggle="modal">
|
<button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" data-peer-id="${peer.id}" data-toggle="modal">
|
||||||
<i class="bi bi-gear-fill" data-toggle="tooltip" data-placement="bottom" title="Peer Settings"></i>
|
<i class="bi bi-gear-fill" data-toggle="tooltip" data-placement="bottom" title="Peer Settings"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-danger btn-delete-peer btn-control" id="${peer.id}" data-toggle="modal">
|
<button type="button" class="btn btn-outline-danger btn-delete-peer btn-control" data-peer-id="${peer.id}" data-toggle="modal">
|
||||||
<i class="bi bi-x-circle-fill" data-toggle="tooltip" data-placement="bottom" title="Delete Peer"></i>
|
<i class="bi bi-x-circle-fill" data-toggle="tooltip" data-placement="bottom" title="Delete Peer"></i>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary btn-lock-peer btn-control" id="${peer.id}" data-toggle="modal">
|
<button type="button" class="btn btn-outline-success btn-lock-peer btn-control" data-peer-id="${peer.id}" data-toggle="modal">
|
||||||
<i class="bi bi-lock-fill" data-toggle="tooltip" data-placement="bottom" title=""></i>
|
<i class="bi bi-ethernet" data-toggle="tooltip" data-placement="bottom" data-original-title='Peer enabled. Click to disable peer.' data-peer-name="${peer.name}"></i>
|
||||||
</button>`;
|
</button>`;
|
||||||
if (peer.private_key !== "") {
|
if (peer.private_key !== "") {
|
||||||
peer_control += '<div class="share_peer_btn_group" style="margin-left: auto !important; display: inline"><button type="button" class="btn btn-outline-success btn-qrcode-peer btn-control" data-imgsrc="/qrcode/' + response.name + '?id=' + encodeURIComponent(peer.id) + '"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="width: 19px;" fill="#28a745"><path d="M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zM13 3v8h8V3h-8zm6 6h-4V5h4v4zM13 13h2v2h-2zM15 15h2v2h-2zM13 17h2v2h-2zM17 17h2v2h-2zM19 19h2v2h-2zM15 19h2v2h-2zM17 13h2v2h-2zM19 15h2v2h-2z"/></svg></button><a href="/download/' + response.name + '?id=' + encodeURIComponent(peer.id) + '" class="btn btn-outline-info btn-download-peer btn-control"><i class="bi bi-download"></i></a></div>';
|
peer_control += '<div class="share_peer_btn_group" style="margin-left: auto !important; display: inline"><button type="button" class="btn btn-outline-success btn-qrcode-peer btn-control" data-imgsrc="/qrcode/' + response.name + '?id=' + encodeURIComponent(peer.id) + '"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="width: 19px;" fill="#28a745"><path d="M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zM13 3v8h8V3h-8zm6 6h-4V5h4v4zM13 13h2v2h-2zM15 15h2v2h-2zM13 17h2v2h-2zM17 17h2v2h-2zM19 19h2v2h-2zM15 19h2v2h-2zM17 13h2v2h-2zM19 15h2v2h-2z"/></svg></button><a href="/download/' + response.name + '?id=' + encodeURIComponent(peer.id) + '" class="btn btn-outline-info btn-download-peer btn-control"><i class="bi bi-download"></i></a></div>';
|
||||||
@ -357,6 +360,59 @@
|
|||||||
'</div></div>';
|
'</div></div>';
|
||||||
result += html;
|
result += html;
|
||||||
});
|
});
|
||||||
|
response.lock_access_peers.forEach(function(peer) {
|
||||||
|
let total_r = 0;
|
||||||
|
let total_s = 0;
|
||||||
|
total_r += peer.cumu_receive;
|
||||||
|
total_s += peer.cumu_sent;
|
||||||
|
let spliter = '<div class="w-100"></div>';
|
||||||
|
let peer_name =
|
||||||
|
`<div class="col-sm peerNameCol">
|
||||||
|
<h5 class="peerName">${peer.name === "" ? "Untitled" : peer.name}</h5>
|
||||||
|
<h6 class="peerLightContainer"><span class="dot dot-${peer.status}" style="margin-left: auto !important;" data-toggle="tooltip" data-placement="left"></span></h6>
|
||||||
|
</div>`;
|
||||||
|
let peer_transfer =
|
||||||
|
`<div class="col-12 peer_data_group" style="">
|
||||||
|
<p class="text-primary" style="">
|
||||||
|
<small><i class="bi bi-arrow-down-right"></i> ${roundN(peer.total_receive + total_r, 4)} GB</small>
|
||||||
|
</p>
|
||||||
|
<p class="text-success">
|
||||||
|
<small><i class="bi bi-arrow-up-right"></i> ${roundN(peer.total_sent + total_s, 4)} GB</small>
|
||||||
|
</p>
|
||||||
|
</div>`;
|
||||||
|
let peer_key = '<div class="col-sm"><small class="text-muted" style="display: flex"><strong>PEER</strong><strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out" class="text-primary">CLICK TO COPY</strong></small> <h6><samp class="ml-auto key">' + peer.id + '</samp></h6></div>';
|
||||||
|
let peer_allowed_ip = '<div class="col-sm"><small class="text-muted"><strong>ALLOWED IP</strong></small><h6 style="text-transform: uppercase;">' + peer.allowed_ip + '</h6></div>';
|
||||||
|
let peer_latest_handshake = '<div class="col-sm"> <small class="text-muted"><strong>LATEST HANDSHAKE</strong></small> <h6 style="text-transform: uppercase;">' + peer.latest_handshake + '</h6> </div>';
|
||||||
|
let peer_endpoint = '<div class="col-sm"><small class="text-muted"><strong>END POINT</strong></small><h6 style="text-transform: uppercase;">' + peer.endpoint + '</h6></div>';
|
||||||
|
let peer_control = `
|
||||||
|
<div class="col-sm">
|
||||||
|
<hr>
|
||||||
|
<div class="button-group" style="display:flex; align-items: center;">
|
||||||
|
<button type="button" class="btn btn-outline-success btn-lock-peer btn-control lock" data-peer-id="${peer.id}" data-toggle="modal">
|
||||||
|
<i class="bi bi-ethernet" data-toggle="tooltip" data-placement="bottom" data-original-title='Peer disabled. Click to enable peer.' data-peer-name="${peer.name}"></i>
|
||||||
|
</button>
|
||||||
|
<small class="text-muted" style="margin-left: auto">Peer Disabled</small>
|
||||||
|
</div>`;
|
||||||
|
let html = '<div class="' + mode + '" data-id="' + peer.id + '">' +
|
||||||
|
'<div class="card mb-3 card-' + peer.status + '">' +
|
||||||
|
'<div class="card-body">' +
|
||||||
|
'<div class="row">' +
|
||||||
|
peer_name +
|
||||||
|
spliter +
|
||||||
|
peer_transfer +
|
||||||
|
peer_key +
|
||||||
|
peer_allowed_ip +
|
||||||
|
peer_latest_handshake +
|
||||||
|
spliter +
|
||||||
|
peer_endpoint +
|
||||||
|
spliter +
|
||||||
|
peer_control +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div></div>';
|
||||||
|
result += html;
|
||||||
|
});
|
||||||
document.querySelector(".peer_list").innerHTML = result;
|
document.querySelector(".peer_list").innerHTML = result;
|
||||||
if (configuration_interval === undefined) {
|
if (configuration_interval === undefined) {
|
||||||
setConfigurationInterval();
|
setConfigurationInterval();
|
||||||
@ -407,8 +463,7 @@
|
|||||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||||
$add_peer.removeAttribute("disabled");
|
$add_peer.removeAttribute("disabled");
|
||||||
$add_peer.innerHTML = "Save";
|
$add_peer.innerHTML = "Save";
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
window.configurations.loadPeers("");
|
window.configurations.loadPeers("");
|
||||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||||
$("#add_peer_form").trigger("reset");
|
$("#add_peer_form").trigger("reset");
|
||||||
@ -456,8 +511,7 @@
|
|||||||
.removeClass("d-none");
|
.removeClass("d-none");
|
||||||
$("#confirm_delete_bulk_peers").removeAttr("disabled").html("Delete");
|
$("#confirm_delete_bulk_peers").removeAttr("disabled").html("Delete");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
if (window.configurations.deleteModal()._isShown) {
|
if (window.configurations.deleteModal()._isShown) {
|
||||||
window.configurations.deleteModal().toggle();
|
window.configurations.deleteModal().toggle();
|
||||||
}
|
}
|
||||||
@ -468,8 +522,7 @@
|
|||||||
window.configurations.deleteBulkModal().toggle();
|
window.configurations.deleteBulkModal().toggle();
|
||||||
}
|
}
|
||||||
window.configurations.loadPeers($('#search_peer_textbox').val());
|
window.configurations.loadPeers($('#search_peer_textbox').val());
|
||||||
$('#alertToast').toast('show');
|
window.configurations.showToast(`Deleted ${peer_ids.length} peers`)
|
||||||
$('#alertToast .toast-body').html("Peer deleted!");
|
|
||||||
$("#delete_peer").removeAttr("disabled").html("Delete");
|
$("#delete_peer").removeAttr("disabled").html("Delete");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -567,6 +620,7 @@
|
|||||||
let time = 0;
|
let time = 0;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let d1 = new Date();
|
let d1 = new Date();
|
||||||
|
|
||||||
function loadPeers(searchString) {
|
function loadPeers(searchString) {
|
||||||
d1 = new Date();
|
d1 = new Date();
|
||||||
let good = true;
|
let good = true;
|
||||||
@ -586,6 +640,7 @@
|
|||||||
|
|
||||||
function parsePeers(response) {
|
function parsePeers(response) {
|
||||||
if (response.status) {
|
if (response.status) {
|
||||||
|
removeAllTooltips();
|
||||||
let d2 = new Date();
|
let d2 = new Date();
|
||||||
let seconds = (d2 - d1);
|
let seconds = (d2 - d1);
|
||||||
time += seconds;
|
time += seconds;
|
||||||
@ -612,6 +667,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeAllTooltips(){
|
||||||
|
$(".tooltip").remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAccess(peerID){
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/togglePeerAccess",
|
||||||
|
method: "POST",
|
||||||
|
headers: {"Content-Type": "application/json"},
|
||||||
|
data: JSON.stringify({"peerID": peerID, "config": configuration_name})
|
||||||
|
}).done(function(res){
|
||||||
|
if(res.status){
|
||||||
|
loadPeers($('#search_peer_textbox').val());
|
||||||
|
}else{
|
||||||
|
showToast(res.reason);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate Private and Public key for a new peer
|
* Generate Private and Public key for a new peer
|
||||||
*/
|
*/
|
||||||
@ -628,9 +702,24 @@
|
|||||||
* Show toast
|
* Show toast
|
||||||
* @param msg
|
* @param msg
|
||||||
*/
|
*/
|
||||||
|
let numberToast = 0;
|
||||||
function showToast(msg) {
|
function showToast(msg) {
|
||||||
$('#alertToast').toast('show');
|
$(".toastContainer").append(
|
||||||
$('#alertToast .toast-body').html(msg);
|
`<div id="${numberToast}-toast" class="toast hide" role="alert" data-delay="5000">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">WGDashboard</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">${msg}</div>
|
||||||
|
<div class="toast-progressbar"></div>
|
||||||
|
</div>` )
|
||||||
|
$(`#${numberToast}-toast`).toast('show');
|
||||||
|
$(`#${numberToast}-toast .toast-body`).html(msg);
|
||||||
|
$(`#${numberToast}-toast .toast-progressbar`).css("transition", `width ${$(`#${numberToast}-toast .toast-progressbar`).parent().data('delay')}ms cubic-bezier(0, 0, 0, 0)`);
|
||||||
|
$(`#${numberToast}-toast .toast-progressbar`).css("width", "0px");
|
||||||
|
numberToast++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -754,6 +843,8 @@
|
|||||||
addPeersByBulk: () => { addPeersByBulk(); },
|
addPeersByBulk: () => { addPeersByBulk(); },
|
||||||
deletePeers: (config, peers_ids) => { deletePeers(config, peers_ids); },
|
deletePeers: (config, peers_ids) => { deletePeers(config, peers_ids); },
|
||||||
parsePeers: (response) => { parsePeers(response); },
|
parsePeers: (response) => { parsePeers(response); },
|
||||||
|
toggleAccess: (peerID) => { toggleAccess(peerID) },
|
||||||
|
|
||||||
|
|
||||||
setConfigurationName: (confName) => { configuration_name = confName; },
|
setConfigurationName: (confName) => { configuration_name = confName; },
|
||||||
getConfigurationName: () => { return configuration_name; },
|
getConfigurationName: () => { return configuration_name; },
|
||||||
@ -793,13 +884,33 @@ document.querySelector(".add_btn").addEventListener("click", () => {
|
|||||||
/**
|
/**
|
||||||
* When configuration switch got click
|
* When configuration switch got click
|
||||||
*/
|
*/
|
||||||
document.querySelector(".info").addEventListener("click", (event) => {
|
$(".toggle--switch").on("click", function(){
|
||||||
let selector = document.querySelector(".switch");
|
$(this).addClass("waiting").attr("disabled", "disabled");
|
||||||
if (selector.contains(event.target)){
|
let id = window.configurations.getConfigurationName();
|
||||||
selector.style.display = "none";
|
let status = $(this).prop("checked");
|
||||||
document.querySelector('div[role=status]').style.display = "inline-block";
|
let ele = $(this);
|
||||||
location.replace(`/switch/${selector.getAttribute("id")}`);
|
$.ajax({
|
||||||
|
url: `/switch/${id}`
|
||||||
|
}).done(function(res){
|
||||||
|
console.log();
|
||||||
|
if (res){
|
||||||
|
if (status){
|
||||||
|
window.configurations.showToast(`${id} is running.`)
|
||||||
|
}else{
|
||||||
|
window.configurations.showToast(`${id} is stopped.`)
|
||||||
}
|
}
|
||||||
|
ele.removeClass("waiting");
|
||||||
|
ele.removeAttr("disabled");
|
||||||
|
}else{
|
||||||
|
if (status){
|
||||||
|
$(this).prop("checked", false)
|
||||||
|
}else{
|
||||||
|
$(this).prop("checked", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.configurations.loadPeers($('#search_peer_textbox').val())
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -909,8 +1020,7 @@ $add_peer.addEventListener("click",function(){
|
|||||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||||
$add_peer.removeAttribute("disabled");
|
$add_peer.removeAttribute("disabled");
|
||||||
$add_peer.innerHTML = "Save";
|
$add_peer.innerHTML = "Save";
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
window.configurations.loadPeers("");
|
window.configurations.loadPeers("");
|
||||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||||
$("#add_peer_form").trigger("reset");
|
$("#add_peer_form").trigger("reset");
|
||||||
@ -965,8 +1075,7 @@ $("#bulk_add").on("change", function (){
|
|||||||
$(hide[i]).attr("disabled", "disabled");
|
$(hide[i]).attr("disabled", "disabled");
|
||||||
}
|
}
|
||||||
amount.removeAttr("disabled");
|
amount.removeAttr("disabled");
|
||||||
}
|
} else {
|
||||||
else{
|
|
||||||
for (let i = 0; i < hide.length; i++) {
|
for (let i = 0; i < hide.length; i++) {
|
||||||
if ($(hide[i]).attr('id') !== "public_key") {
|
if ($(hide[i]).attr('id') !== "public_key") {
|
||||||
$(hide[i]).removeAttr("disabled");
|
$(hide[i]).removeAttr("disabled");
|
||||||
@ -1072,21 +1181,23 @@ $body.on("click", ".btn-qrcode-peer", function (){
|
|||||||
* When the delete button got clicked on each peer
|
* When the delete button got clicked on each peer
|
||||||
*/
|
*/
|
||||||
$body.on("click", ".btn-delete-peer", function() {
|
$body.on("click", ".btn-delete-peer", function() {
|
||||||
let peer_id = $(this).attr("id");
|
let peer_id = $(this).data('peer-id')
|
||||||
$("#delete_peer").data("peer-id", peer_id);
|
$("#delete_peer").data("peer-id", peer_id);
|
||||||
window.configurations.deleteModal().toggle();
|
window.configurations.deleteModal().toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
$body.on("click", ".btn-lock-peer", function() {
|
$body.on("click", ".btn-lock-peer", function() {
|
||||||
let $lockGlyph = "bi-lock-fill";
|
window.configurations.toggleAccess($(this).data('peer-id'), window.configurations.getConfigurationName());
|
||||||
let $unlockGlyph = "bi-unlock-fill";
|
if ($(this).hasClass("lock")) {
|
||||||
|
console.log($(this).data("peer-name"))
|
||||||
if ($(this).children().hasClass($lockGlyph)){
|
window.configurations.showToast(`Enabled ${$(this).children().data("peer-name")}`)
|
||||||
$(this).children().removeClass($lockGlyph).addClass($unlockGlyph);
|
$(this).removeClass("lock")
|
||||||
$(this).children().tooltip('hide').attr('data-original-title', 'Lock Peer').tooltip('show');
|
$(this).children().tooltip('hide').attr('data-original-title', 'Peer enabled. Click to disable peer.').tooltip('show');
|
||||||
} else {
|
} else {
|
||||||
$(this).children().removeClass($unlockGlyph).addClass($lockGlyph);
|
// Currently unlocked
|
||||||
$(this).children().tooltip('hide').attr('data-original-title', 'Unlock Peer').tooltip('show');
|
window.configurations.showToast(`Disabled ${$(this).children().data("peer-name")}`)
|
||||||
|
$(this).addClass("lock");
|
||||||
|
$(this).children().tooltip('hide').attr('data-original-title', 'Peer disabled. Click to enable peer.').tooltip('show');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1112,7 +1223,7 @@ $("#delete_peer").on("click",function(){
|
|||||||
*/
|
*/
|
||||||
$body.on("click", ".btn-setting-peer", function() {
|
$body.on("click", ".btn-setting-peer", function() {
|
||||||
// window.configurations.startProgressBar();
|
// window.configurations.startProgressBar();
|
||||||
let peer_id = $(this).attr("id");
|
let peer_id = $(this).data("peer-id");
|
||||||
$("#save_peer_setting").attr("peer_id", peer_id);
|
$("#save_peer_setting").attr("peer_id", peer_id);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -1405,8 +1516,7 @@ $("#delete_peers_by_bulk_btn").on("click", () => {
|
|||||||
$delete_bulk_modal_list.html('');
|
$delete_bulk_modal_list.html('');
|
||||||
peers.forEach((peer) => {
|
peers.forEach((peer) => {
|
||||||
let name;
|
let name;
|
||||||
if (peer.name === "") { name = "Untitled Peer"; }
|
if (peer.name === "") { name = "Untitled Peer"; } else { name = peer.name; }
|
||||||
else { name = peer.name; }
|
|
||||||
$delete_bulk_modal_list.append('<a class="list-group-item list-group-item-action delete-bulk-peer-item" style="cursor: pointer" data-id="' +
|
$delete_bulk_modal_list.append('<a class="list-group-item list-group-item-action delete-bulk-peer-item" style="cursor: pointer" data-id="' +
|
||||||
peer.id + '" data-name="' + name + '">' + name + '<br><code>' + peer.id + '</code></a>');
|
peer.id + '" data-name="' + name + '">' + name + '<br><code>' + peer.id + '</code></a>');
|
||||||
});
|
});
|
||||||
|
24
src/static/js/configuration.min.js
vendored
24
src/static/js/configuration.min.js
vendored
File diff suppressed because one or more lines are too long
@ -27,9 +27,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<small class="text-muted"><strong>SWITCH</strong></small><br>
|
<small class="text-muted"><strong>SWITCH</strong></small><br>
|
||||||
<div id="conf_status_btn" class="info_loading"></div>
|
<!-- <div id="conf_status_btn" class="info_loading"></div> -->
|
||||||
<div class="spinner-border text-primary" role="status" style="display: none; margin-top: 10px">
|
<div id="switch" class="info_loading">
|
||||||
<span class="sr-only">Loading...</span>
|
<input type="checkbox" class="toggle--switch" id="switch">
|
||||||
|
<label for="switch" class="toggleLabel"></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-100"></div>
|
<div class="w-100"></div>
|
||||||
@ -424,8 +425,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="position-fixed top-0 right-0 p-3" style="z-index: 5; right: 0; top: 50px;">
|
<div class="position-fixed top-0 right-0 p-3 toastContainer" style="z-index: 5; right: 0; top: 50px;">
|
||||||
<div id="alertToast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000">
|
<!-- <div id="alertToast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true" data-delay="5000">
|
||||||
<div class="toast-header">
|
<div class="toast-header">
|
||||||
<strong class="mr-auto">WGDashboard</strong>
|
<strong class="mr-auto">WGDashboard</strong>
|
||||||
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
@ -434,12 +435,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="toast-body">
|
<div class="toast-body">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
{% include "tools.html" %}
|
{% include "tools.html" %}
|
||||||
</body>
|
</body>
|
||||||
{% include "footer.html" %}
|
{% include "footer.html" %}
|
||||||
<script src="{{ url_for('static',filename='js/configuration.js') }}"></script>
|
<script src="{{ url_for('static',filename='js/configuration.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static',filename='js/wireguard.min.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
configurations.setConfigurationName("{{ conf_data['name'] }}");
|
configurations.setConfigurationName("{{ conf_data['name'] }}");
|
||||||
configurations.setActiveConfigurationName();
|
configurations.setActiveConfigurationName();
|
||||||
|
@ -23,8 +23,10 @@
|
|||||||
{% if conf == [] %}
|
{% if conf == [] %}
|
||||||
<p class="text-muted">You don't have any WireGuard configurations yet. Please check the configuration folder or change it in "Settings". By default the folder is "/etc/wireguard".</p>
|
<p class="text-muted">You don't have any WireGuard configurations yet. Please check the configuration folder or change it in "Settings". By default the folder is "/etc/wireguard".</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% for i in conf%}
|
{% for i in conf%}
|
||||||
<div class="card mt-3 conf_card">
|
<div class="card mt-3 conf_card" data-conf-id="{{i['conf']}}">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col card-col">
|
<div class="col card-col">
|
||||||
@ -35,21 +37,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col card-col">
|
<div class="col card-col">
|
||||||
<small class="text-muted"><strong>STATUS</strong></small>
|
<small class="text-muted"><strong>STATUS</strong></small>
|
||||||
<h6 style="text-transform: uppercase; margin:0 !important;">{{i['status']}}<span class="dot dot-{{i['status']}}"></span></h6>
|
<h6 style="text-transform: uppercase; margin:0 !important;"><span>{{i['status']}}</span><span class="dot dot-{{i['status']}}"></span></h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md card-col">
|
<div class="col-sm card-col">
|
||||||
<small class="text-muted"><strong>PUBLIC KEY</strong></small>
|
<small class="text-muted"><strong>PUBLIC KEY</strong></small>
|
||||||
<h6 style="margin:0 !important;"><samp>{{i['public_key']}}</samp></h6>
|
<h6 style="margin:0 !important;"><samp>{{i['public_key']}}</samp></h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md index-switch">
|
<div class="col-sm index-switch">
|
||||||
{% if i['checked'] == "checked" %}
|
<div class="switch-test">
|
||||||
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-primary tt"><i class="bi bi-toggle2-on"></i></a>
|
<input type="checkbox" class="toggle--switch" id="{{i['conf']}}-switch" {{i['checked']}} data-conf-id="{{i['conf']}}">
|
||||||
|
<label for="{{i['conf']}}-switch" class="toggleLabel"></label>
|
||||||
|
</div>
|
||||||
|
<!-- {% if i['checked'] == "checked" %}
|
||||||
|
<a href="#" id="{{i['conf']}}" class="switch text-primary tt"><i class="bi bi-toggle2-on"></i></a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-secondary"><i class="bi bi-toggle2-off"></i></a>
|
<a href="#" id="{{i['conf']}}" {{i['checked']}} class="switch text-secondary"><i class="bi bi-toggle2-off"></i></a>
|
||||||
{% endif %}
|
{% endif %} -->
|
||||||
<div class="spinner-border text-primary" role="status" style="display: none">
|
<!-- <div class="spinner-border text-primary" role="status" style="display: none">
|
||||||
<span class="sr-only">Loading...</span>
|
<span class="sr-only">Loading...</span>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -57,10 +63,69 @@
|
|||||||
{%endfor%}
|
{%endfor%}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="position-fixed top-0 right-0 p-3 toastContainer" style="z-index: 5; right: 0; top: 50px;">
|
||||||
|
|
||||||
|
</div>
|
||||||
{% include "tools.html" %}
|
{% include "tools.html" %}
|
||||||
</body>
|
</body>
|
||||||
{% include "footer.html" %}
|
{% include "footer.html" %}
|
||||||
<script>
|
<script>
|
||||||
|
let numberToast = 0;
|
||||||
|
|
||||||
|
|
||||||
|
function showToast(msg){
|
||||||
|
$(".toastContainer").append(
|
||||||
|
`<div id="${numberToast}-toast" class="toast hide" role="alert" data-delay="5000">
|
||||||
|
<div class="toast-header">
|
||||||
|
<strong class="mr-auto">WGDashboard</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">${msg}</div>
|
||||||
|
<div class="toast-progressbar"></div>
|
||||||
|
</div>` )
|
||||||
|
$(`#${numberToast}-toast`).toast('show');
|
||||||
|
$(`#${numberToast}-toast .toast-body`).html(msg);
|
||||||
|
$(`#${numberToast}-toast .toast-progressbar`).css("transition", `width ${$(`#${numberToast}-toast .toast-progressbar`).parent().data('delay')}ms cubic-bezier(0, 0, 0, 0)`);
|
||||||
|
$(`#${numberToast}-toast .toast-progressbar`).css("width", "0px");
|
||||||
|
numberToast++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(".toggle--switch").on("change", function(){
|
||||||
|
$(this).addClass("waiting").attr("disabled", "disabled");
|
||||||
|
let id = $(this).data("conf-id");
|
||||||
|
let status = $(this).prop("checked");
|
||||||
|
let ele = $(this);
|
||||||
|
let label = $(this).siblings("label");
|
||||||
|
$.ajax({
|
||||||
|
url: `/switch/${id}`
|
||||||
|
}).done(function(res){
|
||||||
|
let dot = $(`div[data-conf-id="${id}"] .dot`);
|
||||||
|
console.log();
|
||||||
|
if (res){
|
||||||
|
if (status){
|
||||||
|
dot.removeClass("dot-stopped").addClass("dot-running");
|
||||||
|
dot.siblings().text("Running");
|
||||||
|
showToast(`${id} is running.`)
|
||||||
|
}else{
|
||||||
|
dot.removeClass("dot-running").addClass("dot-stopped");
|
||||||
|
showToast(`${id} is stopped.`)
|
||||||
|
}
|
||||||
|
ele.removeClass("waiting");
|
||||||
|
ele.removeAttr("disabled");
|
||||||
|
}else{
|
||||||
|
if (status){
|
||||||
|
$(this).prop("checked", false)
|
||||||
|
}else{
|
||||||
|
$(this).prop("checked", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
$('.switch').on("click", function() {
|
$('.switch').on("click", function() {
|
||||||
$(this).siblings($(".spinner-border")).css("display", "inline-block")
|
$(this).siblings($(".spinner-border")).css("display", "inline-block")
|
||||||
@ -70,7 +135,7 @@
|
|||||||
$(".sb-home-url").addClass("active");
|
$(".sb-home-url").addClass("active");
|
||||||
|
|
||||||
$(".card-body").on("click", function(handle){
|
$(".card-body").on("click", function(handle){
|
||||||
if ($(handle.target).attr("class") !== "bi bi-toggle2-off" && $(handle.target).attr("class") !== "bi bi-toggle2-on") {
|
if ($(handle.target).attr("class") !== "toggleLabel" && $(handle.target).attr("class") !== "toggle--switch") {
|
||||||
window.open($(this).find("a").attr("href"), "_self");
|
window.open($(this).find("a").attr("href"), "_self");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
33
src/util.py
33
src/util.py
@ -1,5 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
|
import dashboard
|
||||||
"""
|
"""
|
||||||
Helper Functions
|
Helper Functions
|
||||||
"""
|
"""
|
||||||
@ -79,3 +80,33 @@ def check_remote_endpoint(address):
|
|||||||
return (check_IP(address) or regex_match("(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]",
|
return (check_IP(address) or regex_match("(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]",
|
||||||
address))
|
address))
|
||||||
|
|
||||||
|
|
||||||
|
def deletePeers(config_name, delete_keys, cur, db):
|
||||||
|
sql_command = []
|
||||||
|
wg_command = ["wg", "set", config_name]
|
||||||
|
for delete_key in delete_keys:
|
||||||
|
if delete_key not in dashboard.get_conf_peer_key(config_name):
|
||||||
|
return "This key does not exist"
|
||||||
|
sql_command.append("DELETE FROM " + config_name + " WHERE id = '" + delete_key + "';")
|
||||||
|
wg_command.append("peer")
|
||||||
|
wg_command.append(delete_key)
|
||||||
|
wg_command.append("remove")
|
||||||
|
try:
|
||||||
|
print("deleting...")
|
||||||
|
remove_wg = subprocess.check_output(" ".join(wg_command),
|
||||||
|
shell=True, stderr=subprocess.STDOUT)
|
||||||
|
save_wg = subprocess.check_output(f"wg-quick save {config_name}", shell=True, stderr=subprocess.STDOUT)
|
||||||
|
cur.executescript(' '.join(sql_command))
|
||||||
|
db.commit()
|
||||||
|
except subprocess.CalledProcessError as exc:
|
||||||
|
return exc.output.strip()
|
||||||
|
return "true"
|
||||||
|
|
||||||
|
def checkJSONAllParameter(required, data):
|
||||||
|
if len(data) == 0:
|
||||||
|
print("length 0")
|
||||||
|
return False
|
||||||
|
for i in required:
|
||||||
|
if i not in list(data.keys()):
|
||||||
|
return False
|
||||||
|
return True
|
Loading…
Reference in New Issue
Block a user