mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
Brand new switch button and toast UI
This commit is contained in:
parent
2d3dffe5fc
commit
bdd984a887
@ -536,5 +536,4 @@ 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):
|
||||||
"""
|
"""
|
||||||
@ -499,7 +504,19 @@ def get_conf_list():
|
|||||||
total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
|
total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
|
||||||
status VARCHAR NULL, latest_handshake VARCHAR NULL, allowed_ip 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,
|
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,
|
keepalive INT NULL, remote_endpoint VARCHAR NULL, preshared_key VARCHAR NULL,
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
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)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
@ -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
|
||||||
"""
|
"""
|
||||||
@ -1624,6 +1668,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):
|
||||||
|
@ -9,7 +9,7 @@ body {
|
|||||||
vertical-align: text-bottom;
|
vertical-align: text-bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary{
|
.btn-primary {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
||||||
*/
|
*/
|
||||||
@ -90,11 +94,11 @@ body {
|
|||||||
right: 1rem;
|
right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control{
|
.form-control {
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control:disabled{
|
.form-control:disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +119,7 @@ body {
|
|||||||
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dot{
|
.dot {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
@ -123,157 +127,171 @@ body {
|
|||||||
margin-left: auto !important;
|
margin-left: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dot-running{
|
.dot-running {
|
||||||
background-color: #28a745!important;
|
background-color: #28a745!important;
|
||||||
box-shadow: 0 0 0 0.2rem #28a74545;
|
box-shadow: 0 0 0 0.2rem #28a74545;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h6-dot-running{
|
.h6-dot-running {
|
||||||
margin-left: 0.3rem;
|
margin-left: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dot-stopped{
|
.dot-stopped {
|
||||||
background-color: #6c757d!important;
|
background-color: #6c757d!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-running{
|
.card-running {
|
||||||
border-color: #28a745;
|
border-color: #28a745;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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 {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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) {
|
||||||
margin-left: auto !important;
|
margin-left: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-control{
|
.btn-control {
|
||||||
border: none !important;
|
border: none !important;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-qrcode-peer{
|
.btn-qrcode-peer {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
.share_peer_btn_group .btn-control{
|
.share_peer_btn_group .btn-control {
|
||||||
margin: 0 0 0 1rem;
|
margin: 0 0 0 1rem;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
transition: all 0.4s cubic-bezier(1, -0.43, 0, 1.37);
|
transition: all 0.4s cubic-bezier(1, -0.43, 0, 1.37);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-control:hover{
|
.btn-control:hover {
|
||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-delete-peer:hover{
|
.btn-delete-peer:hover {
|
||||||
color: #dc3545;
|
color: #dc3545;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-lock-peer:hover{
|
.btn-lock-peer:hover {
|
||||||
color: #6c757d;
|
color: #28a745;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-setting-peer:hover{
|
.btn-lock-peer.lock{
|
||||||
color:#007bff
|
color: #6c757d
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-download-peer:hover{
|
.btn-lock-peer.lock:hover{
|
||||||
|
color: #6c757d
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-setting-peer:hover {
|
||||||
|
color: #007bff
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-download-peer:hover {
|
||||||
color: #17a2b8;
|
color: #17a2b8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-container{
|
.login-container {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 992px){
|
@media (max-width: 992px) {
|
||||||
.card-col{
|
.card-col {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch{
|
.switch {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
.switch:hover{
|
|
||||||
|
.switch:hover {
|
||||||
text-decoration: none
|
text-decoration: none
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-group-label:hover{
|
.btn-group-label:hover {
|
||||||
color: #007bff;
|
color: #007bff;
|
||||||
border-color: #007bff;
|
border-color: #007bff;
|
||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peer_data_group{
|
.peer_data_group {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 0.5rem
|
margin-bottom: 0.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.peer_data_group p{
|
.peer_data_group p {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-right: 1rem
|
margin-right: 1rem
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.peer_data_group{
|
.peer_data_group {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.index-switch{
|
.index-switch {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
main{
|
main {
|
||||||
margin-bottom: 3rem;
|
margin-bottom: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peer_list{
|
.peer_list {
|
||||||
margin-bottom: 7rem
|
margin-bottom: 7rem
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.add_btn{
|
.add_btn {
|
||||||
bottom: 1.5rem !important;
|
bottom: 1.5rem !important;
|
||||||
}
|
}
|
||||||
|
.peer_list {
|
||||||
.peer_list{
|
|
||||||
margin-bottom: 7rem !important;
|
margin-bottom: 7rem !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-manage-group{
|
.btn-manage-group {
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 3rem;
|
bottom: 3rem;
|
||||||
@ -281,7 +299,7 @@ main{
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-manage-group .setting_btn_menu{
|
.btn-manage-group .setting_btn_menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -124px;
|
top: -124px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@ -296,16 +314,16 @@ main{
|
|||||||
transition: all 0.3s cubic-bezier(0.58, 0.03, 0.05, 1.28);
|
transition: all 0.3s cubic-bezier(0.58, 0.03, 0.05, 1.28);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-manage-group .setting_btn_menu.show{
|
.btn-manage-group .setting_btn_menu.show {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting_btn_menu.showing{
|
.setting_btn_menu.showing {
|
||||||
transform: translateY(0px);
|
transform: translateY(0px);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting_btn_menu a{
|
.setting_btn_menu a {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
transition: all 0.1s ease-in-out;
|
transition: all 0.1s ease-in-out;
|
||||||
@ -314,36 +332,38 @@ main{
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting_btn_menu a:hover{
|
.setting_btn_menu a:hover {
|
||||||
background-color: #efefef;
|
background-color: #efefef;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting_btn_menu a i{
|
.setting_btn_menu a i {
|
||||||
margin-right: auto !important;
|
margin-right: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add_btn{
|
.add_btn {
|
||||||
height: 54px;
|
height: 54px;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
border-radius: 100px !important;
|
border-radius: 100px !important;
|
||||||
padding: 0 14px;
|
padding: 0 14px;
|
||||||
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting_btn{
|
.setting_btn {
|
||||||
height: 54px;
|
height: 54px;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
border-radius: 100px !important;
|
border-radius: 100px !important;
|
||||||
padding: 0 14px;
|
padding: 0 14px;
|
||||||
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
||||||
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);
|
||||||
@ -380,7 +401,7 @@ main{
|
|||||||
animation: rotating 0.75s linear infinite;
|
animation: rotating 0.75s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peer_private_key_textbox_switch{
|
.peer_private_key_textbox_switch {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 2rem;
|
right: 2rem;
|
||||||
transform: translateY(-28px);
|
transform: translateY(-28px);
|
||||||
@ -388,139 +409,153 @@ main{
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#peer_private_key_textbox, #private_key, #public_key, #peer_preshared_key_textbox{
|
#peer_private_key_textbox,
|
||||||
font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
|
#private_key,
|
||||||
|
#public_key,
|
||||||
|
#peer_preshared_key_textbox {
|
||||||
|
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar{
|
.progress-bar {
|
||||||
transition: 0.3s ease-in-out;
|
transition: 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key{
|
.key {
|
||||||
transition: 0.2s ease-in-out;
|
transition: 0.2s ease-in-out;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key:hover{
|
.key:hover {
|
||||||
color: #007bff;
|
color: #007bff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card{
|
.card {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peer_list .card .button-group{
|
.peer_list .card .button-group {
|
||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control{
|
.form-control {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn{
|
.btn {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
/*padding: 0.6rem 0.9em;*/
|
/*padding: 0.6rem 0.9em;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#username, #password{
|
#username,
|
||||||
padding: 0.6rem calc( 0.9rem + 32px );
|
#password {
|
||||||
|
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"]{*/
|
/*label[for="password"]{*/
|
||||||
|
|
||||||
|
|
||||||
/* transform: translateY(32px) translateX(16px);*/
|
/* transform: translateY(32px) translateX(16px);*/
|
||||||
|
|
||||||
|
|
||||||
/*}*/
|
/*}*/
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
.modal-content{
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-inner{
|
.tooltip-inner {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes loading {
|
@-webkit-keyframes loading {
|
||||||
0%{
|
0% {
|
||||||
background-color: #dfdfdf;
|
background-color: #dfdfdf;
|
||||||
}
|
}
|
||||||
50%{
|
50% {
|
||||||
background-color: #adadad;
|
background-color: #adadad;
|
||||||
}
|
}
|
||||||
100%{
|
100% {
|
||||||
background-color: #dfdfdf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@-moz-keyframes loading {
|
|
||||||
0%{
|
|
||||||
background-color: #dfdfdf;
|
|
||||||
}
|
|
||||||
50%{
|
|
||||||
background-color: #adadad;
|
|
||||||
}
|
|
||||||
100%{
|
|
||||||
background-color: #dfdfdf;
|
background-color: #dfdfdf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.conf_card{
|
@-moz-keyframes loading {
|
||||||
|
0% {
|
||||||
|
background-color: #dfdfdf;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-color: #adadad;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: #dfdfdf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.conf_card {
|
||||||
transition: 0.2s ease-in-out;
|
transition: 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conf_card:hover{
|
.conf_card:hover {
|
||||||
border-color: #007bff;
|
border-color: #007bff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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 {
|
||||||
transition: 0.2s ease-in-out;
|
transition: 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#conf_status_btn.info_loading{
|
#conf_status_btn.info_loading {
|
||||||
height: 38px;
|
height: 38px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
animation: loading 3s infinite ease-in-out;
|
animation: loading 3s infinite ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#qrcode_img img{
|
#qrcode_img img {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
#add_modal.ip_modal_open{
|
#add_modal.ip_modal_open {
|
||||||
transition: filter 0.2s ease-in-out;
|
transition: filter 0.2s ease-in-out;
|
||||||
filter: brightness(0.5);
|
filter: brightness(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#delete_bulk_modal .list-group a.active{
|
#delete_bulk_modal .list-group a.active {
|
||||||
background-color: #dc3545;
|
background-color: #dc3545;
|
||||||
border-color: #dc3545;
|
border-color: #dc3545;
|
||||||
}
|
}
|
||||||
|
|
||||||
#selected_peer_list{
|
#selected_peer_list {
|
||||||
max-height: 80px;
|
max-height: 80px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-response{
|
.no-response {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -534,93 +569,195 @@ label[for="username"], label[for="password"]{
|
|||||||
transition: all 1s ease-in-out;
|
transition: all 1s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-response.active{
|
.no-response.active {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-response.active.show{
|
.no-response.active.show {
|
||||||
opacity: 100;
|
opacity: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-response .container > *{
|
.no-response .container>* {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-responding{
|
.no-responding {
|
||||||
transition: all 1s ease-in-out;
|
transition: all 1s ease-in-out;
|
||||||
filter: blur(10px);
|
filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
pre.index-alert{
|
pre.index-alert {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: #343a40;
|
background-color: #343a40;
|
||||||
border: 1px solid rgba(0,0,0,.125);
|
border: 1px solid rgba(0, 0, 0, .125);
|
||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peerNameCol{
|
.peerNameCol {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 0.2rem
|
margin-bottom: 0.2rem
|
||||||
}
|
}
|
||||||
|
|
||||||
.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
#config_body{
|
#config_body {
|
||||||
transition: 0.3s ease-in-out;
|
transition: 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#config_body.firstLoading{
|
#config_body.firstLoading {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chartTitle{
|
.chartTitle {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chartControl{
|
.chartControl {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chartTitle h6{
|
.chartTitle h6 {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chartContainer.fullScreen{
|
.chartContainer.fullScreen {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: calc( 100% + 15px );
|
width: calc( 100% + 15px);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chartContainer.fullScreen .col-sm{
|
.chartContainer.fullScreen .col-sm {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chartContainer.fullScreen .chartCanvasContainer{
|
.chartContainer.fullScreen .chartCanvasContainer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
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
File diff suppressed because it is too large
Load Diff
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