1
0
mirror of https://github.com/donaldzou/WGDashboard.git synced 2024-11-22 23:27:45 +01:00

Make the dashboard more mobile friendly

This commit is contained in:
Donald Cheng Hong Zou 2022-04-11 20:01:29 -04:00
parent dcdd4aec85
commit a053504bb8
13 changed files with 585 additions and 384 deletions

View File

@ -55,6 +55,7 @@ app.config['TEMPLATES_AUTO_RELOAD'] = True
QRcode(app) QRcode(app)
# TODO: use class and object oriented programming # TODO: use class and object oriented programming
updateInfo = {}
def connect_db(): def connect_db():
@ -693,6 +694,7 @@ def auth_req():
conf = get_dashboard_conf() conf = get_dashboard_conf()
req = conf.get("Server", "auth_req") req = conf.get("Server", "auth_req")
session['update'] = UPDATE session['update'] = UPDATE
session['updateInfo'] = updateInfo
session['dashboard_version'] = DASHBOARD_VERSION session['dashboard_version'] = DASHBOARD_VERSION
if req == "true": if req == "true":
if '/static/' not in request.path and \ if '/static/' not in request.path and \
@ -1940,11 +1942,13 @@ def check_update():
for i in output: for i in output:
if not i["prerelease"]: if not i["prerelease"]:
release.append(i) release.append(i)
global updateInfo
updateInfo = i
break
if config.get("Server", "version") == release[0]["tag_name"]: if config.get("Server", "version") == release[0]["tag_name"]:
result = "false" result = "false"
else: else:
result = "true" result = "true"
return result return result
except urllib.error.HTTPError: except urllib.error.HTTPError:
return "false" return "false"

View File

@ -50,7 +50,7 @@ body {
} }
} }
.sidebar .nav-link { .sidebar .nav-link, .bottomNavContainer .nav-link{
font-weight: 500; font-weight: 500;
color: #333; color: #333;
transition: 0.2s cubic-bezier(0.82, -0.07, 0, 1.01); transition: 0.2s cubic-bezier(0.82, -0.07, 0, 1.01);
@ -66,7 +66,7 @@ body {
color: #999; color: #999;
} }
.sidebar .nav-link.active { .sidebar .nav-link.active, .bottomNavContainer .nav-link.active {
color: #007bff; color: #007bff;
} }
@ -632,6 +632,7 @@ pre.index-alert {
transition: 0.3s ease-in-out; transition: 0.3s ease-in-out;
} }
#config_body.firstLoading { #config_body.firstLoading {
opacity: 0.2; opacity: 0.2;
} }
@ -829,3 +830,88 @@ pre.index-alert {
max-width: 95vw; max-width: 95vw;
} }
} }
.bottom{
display: none;
}
@media (max-width: 768px){
.bottom{
display: block;
}
.btn-manage-group{
bottom: calc( 3rem + 40px + env(safe-area-inset-bottom, 5px));
}
main{
padding-bottom: calc( 3rem + 40px + env(safe-area-inset-bottom, 5px));
}
}
.bottomNavContainer{
display: flex;
color: #333;
padding-bottom: env(safe-area-inset-bottom, 5px);
box-shadow: inset 0 1px 0 rgb(0 0 0 / 10%);
}
.bottomNavButton{
width: 25vw;
display: flex;
flex-direction: column;
align-items: center;
margin: 0.7rem 0;
color: rgba(51, 51, 51, 0.5);
cursor: pointer;
transition: all ease-in 0.2s;
}
.bottomNavButton.active{
color: #333;
}
.bottomNavButton i{
font-size: 1.2rem;
}
.bottomNavButton .subNav{
width: 100vw;
position: absolute;
z-index: 10000;
bottom: 0;
left: 0;
background-color: #272b30;
display: none;
animation-duration: 400ms;
padding-bottom: env(safe-area-inset-bottom, 5px);
}
.bottomNavButton .subNav.active{
display: block;
}
.bottomNavButton .subNav .nav .nav-item .nav-link{
padding: 0.7rem 1rem;
}
.bottomNavWrapper{
height: 100%;
width: 100%;
background-color: #000000a1;
position: fixed;
z-index: 1030;
display: none;
left: 0;
}
.bottomNavWrapper.active{
display: block;
}
.sb-update-url .dot-running{
transform: translateX(10px);
}

View File

@ -8,6 +8,7 @@ let peers = [];
/** /**
* Definitions * Definitions
*/ */
$(".bottomNavConfigs").addClass("active")
let configuration_name; let configuration_name;
let configuration_interval; let configuration_interval;
let configuration_timeout = window.localStorage.getItem("configurationTimeout"); let configuration_timeout = window.localStorage.getItem("configurationTimeout");

View File

@ -4,6 +4,8 @@ $('[data-toggle="tooltip"]').tooltip()
let $add_configuration = $("#add_configuration"); let $add_configuration = $("#add_configuration");
let addConfigurationModal = $("#addConfigurationModal"); let addConfigurationModal = $("#addConfigurationModal");
$(".bottomNavHome").addClass("active");
addConfigurationModal.modal({ addConfigurationModal.modal({
keyboard: false, keyboard: false,

38
src/static/js/pwa.js Normal file
View File

@ -0,0 +1,38 @@
let wrapper = $(".bottomNavWrapper");
$(".bottomNavConfigs").on("click", function(){
let subNav = $(this).children(".subNav");
subNav.removeClass("animate__fadeOutDown").addClass("active animate__fadeInUp");
wrapper.fadeIn();
});
$(".bottomNavHome").on("click", function(){
window.location.replace('/')
});
$(".bottomNavSettings").on("click", function(){
window.location.replace('/settings')
})
function hideBottomSubNav(){
$(".bottomNavButton .subNav").removeClass("animate__fadeInUp").addClass("animate__fadeOutDown");
wrapper.fadeOut();
setTimeout(function(){
$(".bottomNavButton .subNav").removeClass("active");
},350)
}
wrapper.on("click", function(){
hideBottomSubNav();
});
$(".bottomNavMore").on("click", function(){
let subNav = $(this).children(".subNav");
subNav.removeClass("animate__fadeOutDown").addClass("active animate__fadeInUp");
wrapper.fadeIn();
});
// $(".bottomNavButton .nav-conf-link").on("click", function(){
// hideBottomSubNav();
// })

View File

@ -185,374 +185,7 @@
</div> </div>
</div> </div>
<div class="modal fade" id="peerDataUsage"> {% include "modal.html" %}
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Data Usage</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body">
<div class="row">
<div class="chartControl peerDataUsageChartControl col-sm">
<label><small class="text-muted">Data Unit Size</small></label><br>
<div class="btn-group" role="group" style="width: 100%;">
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="GB">GB</button>
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="MB">MB</button>
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="KB">KB</button>
</div>
</div>
<div class="chartControl peerDataUsageChartControl col-sm">
<label><small class="text-muted">Time Period</small></label><br>
<div class="btn-group" role="group" style="width: 100%;">
<button class="btn btn-outline-primary btn-sm switchTimePeriod" data-time="30min">30
min</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod"
data-time="1h">1hr</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod"
data-time="6h">6hrs</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod"
data-time="24h">24hrs</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod"
data-time="all">All</button>
</div>
</div>
</div>
<hr>
<small class="text-muted peerDataUsageUpdateTime" style="position: absolute; right: 1rem;">2023</small>
<div class="peerDataUsageChartContainer">
<canvas id="peerDataUsageChartObj"></canvas>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="add_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Add New Peer</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
<div class="custom-control custom-switch" style="margin-bottom: 1rem">
<input class="custom-control-input" type="checkbox" id="bulk_add">
<label class="custom-control-label" for="bulk_add"><strong>Add Peers by
bulk</strong></label>
<i class="bi bi-question-circle-fill" style="cursor: pointer" data-container="body"
data-toggle="popover" data-placement="right" data-trigger="click"
data-content="By adding peers by bulk, each peer's name will be auto generated, and Allowed IP will be assign to the next available IP."></i>
</div>
<div class="form-group" style="margin: 0">
<input type="number" class="form-control" id="new_add_amount" min="1" placeholder="Amount"
disabled>
<div id="bulk_amount_validation" class="invalid-feedback"></div>
</div>
</div>
<hr>
<div id="add_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form id="add_peer_form">
<div class="form-group">
<div>
<label for="private_key">Private Key</label>
</div>
<div class="input-group">
<input type="text" class="form-control non-bulk" id="private_key"
aria-describedby="private_key">
<div class="input-group-append">
<button type="button" class="btn btn-danger non-bulk" id="re_generate_key"
data-toggle="tooltip" data-placement="top" title="Regenerate Key"><i
class="bi bi-arrow-repeat"></i></button>
</div>
</div>
</div>
<div class="form-group">
<label for="public_key">Public Key <code>(Required)</code></label>
<input type="text" class="form-control non-bulk" id="public_key"
aria-describedby="public_key" disabled>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_name">Name</label>
<input type="text" class="form-control non-bulk" id="new_add_name">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
<div class="input-group">
<input type="text" class="form-control non-bulk" id="allowed_ips">
<div class="input-group-append">
<button type="button" class="btn btn-primary non-bulk"
id="search_available_ip" data-toggle="tooltip" data-placement="top"
title="Search Available IPs">
<i class="bi bi-search"></i>
</button>
</div>
</div>
<p style="position: absolute; top: 4px; right: 1rem;" class="text-success"
id="allowed_ips_indicator"></p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_DNS">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_DNS" value="{{ DNS }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_endpoint_allowed_ip">Endpoint Allowed IPs
<code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_endpoint_allowed_ip"
value="{{ endpoint_allowed_ip }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_MTU">MTU</label>
<input type="text" class="form-control" id="new_add_MTU" value="{{ mtu }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_keep_alive">Persistent keepalive</label>
<input type="text" class="form-control" id="new_add_keep_alive"
value="{{ keep_alive }}">
</div>
</div>
<div class="col-sm">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable_preshare_key"
name="enable_preshare_key" value="enable_psk">
<label class="form-check-label" for="enable_preshare_key">Use Pre-shared Key</label>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save_peer"
conf_id={{conf_data['name']}}>Add</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="configuration_delete_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Are you sure to delete this configuration?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="remove_configuration_alert" class="alert alert-danger alert-dismissible fade show d-none"
role="alert">
</div>
<p style="margin: 0">This action is not reversible. The configuration will get toggle off, and
delete from database and from the configuration folder.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
<button type="button" class="btn btn-danger" id="sure_delete_configuration">Yes</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="delete_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Are you sure to delete this peer?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none"
role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<h6 style="margin: 0">This action is not reversible.</h6>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
<button type="button" class="btn btn-danger" id="delete_peer" conf_id={{conf_data['name']}}
peer_id="">Yes</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="setting_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true" conf_id={{conf_data['name']}} peer_id="">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title peer_name"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="setting_peer_alert" class="alert alert-danger alert-dismissible fade show d-none"
role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="mb-3">
<label for="peer_private_key_textbox" class="form-label">Private Key
<code>(Required for QR Code and download)</code></label>
<input type="password" class="form-control" id="peer_private_key_textbox"
style="padding-right: 40px">
<a class="peer_private_key_textbox_switch"><i class="bi bi-eye-fill"></i></a>
</div>
<div>
<label for="peer_preshared_key_textbox" class="form-label">Pre-Shared Key</label>
<input type="text" class="form-control" id="peer_preshared_key_textbox">
</div>
<hr>
<div class="row">
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_name_textbox" class="form-label">Name</label>
<input type="text" class="form-control" id="peer_name_textbox" placeholder="">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_allowed_ip_textbox" class="form-label">Allowed IPs
<code>(Required)</code></label>
<input type="text" class="form-control" id="peer_allowed_ip_textbox">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_DNS_textbox" class="form-label">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_DNS_textbox">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_endpoint_allowed_ips" class="form-label">Endpoint Allowed IPs
<code>(Required)</code></label>
<input type="text" class="form-control" id="peer_endpoint_allowed_ips">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_mtu" class="form-label">MTU</label>
<input type="text" class="form-control" id="peer_mtu">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_keep_alive" class="form-label">Persistent Keepalive</label>
<input type="text" class="form-control" id="peer_keep_alive">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save_peer_setting" conf_id={{conf_data['name']}}
peer_id="">Save</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="available_ip_modal" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Select available IP</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="selected_ip" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED IP (CLICK TO REMOVE)</strong></small>
<div id="selected_ip_list"></div>
</div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirm_ip">Confirm</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="delete_bulk_modal" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Select Peers to Delete</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="bulk_remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none"
role="alert" style="margin: 1rem">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="selected_peers" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED PEERS (CLICK TO REMOVE)</strong></small>
<div id="selected_peer_list"></div>
</div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div>
</div>
<div class="modal-footer">
<a class="text-danger" id="select_all_delete_bulk_peers"
style="cursor: pointer; margin-right: auto;"><small><strong>SELECT ALL</strong></small></a>
<button type="button" class="btn btn-danger" id="confirm_delete_bulk_peers" disabled
data-conf="{{conf_data['name']}}">Delete</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="qrcode_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">QR Code</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<img id="qrcode_img" style="width: 100%">
</div>
</div>
</div>
</div>
<div class="position-fixed top-0 right-0 p-3 toastContainer" style="z-index: 5; right: 0; top: 50px;"></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>

View File

@ -1,3 +1,4 @@
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
<script src="{{ url_for('static',filename='js/tools.min.js') }}"></script> <script src="{{ url_for('static',filename='js/tools.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/pwa.js') }}"></script>

View File

@ -16,4 +16,5 @@
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='css/dashboard.css') }}"> <link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='css/dashboard.css') }}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js" integrity="sha512-QSkVNOCYLtj73J4hbmVoOV6KVZuMluZlioC+trLpewV8qMjsWqlIQvkn1KGX2StWvPMdWGBqim1xlC8krl1EKQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>
</head> </head>

View File

@ -27,8 +27,6 @@
{% 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" data-conf-id="{{i['conf']}}"> <div class="card mt-3 conf_card" data-conf-id="{{i['conf']}}">
<div class="card-body"> <div class="card-body">
@ -56,7 +54,6 @@
</div> </div>
<div class="card-message"></div> <div class="card-message"></div>
</div> </div>
</div> </div>
{%endfor%} {%endfor%}
</main> </main>

357
src/templates/modal.html Normal file
View File

@ -0,0 +1,357 @@
<div class="modal fade" id="peerDataUsage">
<div class="modal-dialog modal-dialog-centered modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Data Usage</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body">
<div class="row">
<div class="chartControl peerDataUsageChartControl col-sm">
<label><small class="text-muted">Data Unit Size</small></label><br>
<div class="btn-group" role="group" style="width: 100%;">
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="GB">GB</button>
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="MB">MB</button>
<button class="btn btn-outline-primary btn-sm switchUnit" data-unit="KB">KB</button>
</div>
</div>
<div class="chartControl peerDataUsageChartControl col-sm">
<label><small class="text-muted">Time Period</small></label><br>
<div class="btn-group" role="group" style="width: 100%;">
<button class="btn btn-outline-primary btn-sm switchTimePeriod" data-time="30min">30
min</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod" data-time="1h">1hr</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod" data-time="6h">6hrs</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod"
data-time="24h">24hrs</button>
<button class="btn btn-outline-primary btn-sm switchTimePeriod" data-time="all">All</button>
</div>
</div>
</div>
<hr>
<small class="text-muted peerDataUsageUpdateTime" style="position: absolute; right: 1rem;">2023</small>
<div class="peerDataUsageChartContainer">
<canvas id="peerDataUsageChartObj"></canvas>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="add_modal">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Add New Peer</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
<div class="custom-control custom-switch" style="margin-bottom: 1rem">
<input class="custom-control-input" type="checkbox" id="bulk_add">
<label class="custom-control-label" for="bulk_add"><strong>Add Peers by
bulk</strong></label>
<i class="bi bi-question-circle-fill" style="cursor: pointer" data-container="body"
data-toggle="popover" data-placement="right" data-trigger="click"
data-content="By adding peers by bulk, each peer's name will be auto generated, and Allowed IP will be assign to the next available IP."></i>
</div>
<div class="form-group" style="margin: 0">
<input type="number" class="form-control" id="new_add_amount" min="1" placeholder="Amount"
disabled>
<div id="bulk_amount_validation" class="invalid-feedback"></div>
</div>
</div>
<hr>
<div id="add_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form id="add_peer_form">
<div class="form-group">
<div>
<label for="private_key">Private Key</label>
</div>
<div class="input-group">
<input type="text" class="form-control non-bulk" id="private_key"
aria-describedby="private_key">
<div class="input-group-append">
<button type="button" class="btn btn-danger non-bulk" id="re_generate_key"
data-toggle="tooltip" data-placement="top" title="Regenerate Key"><i
class="bi bi-arrow-repeat"></i></button>
</div>
</div>
</div>
<div class="form-group">
<label for="public_key">Public Key <code>(Required)</code></label>
<input type="text" class="form-control non-bulk" id="public_key" aria-describedby="public_key"
disabled>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_name">Name</label>
<input type="text" class="form-control non-bulk" id="new_add_name">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
<div class="input-group">
<input type="text" class="form-control non-bulk" id="allowed_ips">
<div class="input-group-append">
<button type="button" class="btn btn-primary non-bulk" id="search_available_ip"
data-toggle="tooltip" data-placement="top" title="Search Available IPs">
<i class="bi bi-search"></i>
</button>
</div>
</div>
<p style="position: absolute; top: 4px; right: 1rem;" class="text-success"
id="allowed_ips_indicator"></p>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_DNS">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_DNS" value="{{ DNS }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_endpoint_allowed_ip">Endpoint Allowed IPs
<code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_endpoint_allowed_ip"
value="{{ endpoint_allowed_ip }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_MTU">MTU</label>
<input type="text" class="form-control" id="new_add_MTU" value="{{ mtu }}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_keep_alive">Persistent keepalive</label>
<input type="text" class="form-control" id="new_add_keep_alive"
value="{{ keep_alive }}">
</div>
</div>
<div class="col-sm">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enable_preshare_key"
name="enable_preshare_key" value="enable_psk">
<label class="form-check-label" for="enable_preshare_key">Use Pre-shared Key</label>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save_peer">Add</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="configuration_delete_modal">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Are you sure to delete this configuration?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="remove_configuration_alert" class="alert alert-danger alert-dismissible fade show d-none"
role="alert">
</div>
<p style="margin: 0">This action is not reversible. The configuration will get toggle off, and
delete from database and from the configuration folder.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
<button type="button" class="btn btn-danger" id="sure_delete_configuration">Yes</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="delete_modal">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Are you sure to delete this peer?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<h6 style="margin: 0">This action is not reversible.</h6>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
<button type="button" class="btn btn-danger" id="delete_peer" conf_id={{conf_data['name']}}
peer_id="">Yes</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="setting_modal" peer_id="">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title peer_name"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="setting_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="mb-3">
<label for="peer_private_key_textbox" class="form-label">Private Key
<code>(Required for QR Code and download)</code></label>
<input type="password" class="form-control" id="peer_private_key_textbox"
style="padding-right: 40px">
<a class="peer_private_key_textbox_switch"><i class="bi bi-eye-fill"></i></a>
</div>
<div>
<label for="peer_preshared_key_textbox" class="form-label">Pre-Shared Key</label>
<input type="text" class="form-control" id="peer_preshared_key_textbox">
</div>
<hr>
<div class="row">
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_name_textbox" class="form-label">Name</label>
<input type="text" class="form-control" id="peer_name_textbox" placeholder="">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_allowed_ip_textbox" class="form-label">Allowed IPs
<code>(Required)</code></label>
<input type="text" class="form-control" id="peer_allowed_ip_textbox">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_DNS_textbox" class="form-label">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_DNS_textbox">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_endpoint_allowed_ips" class="form-label">Endpoint Allowed IPs
<code>(Required)</code></label>
<input type="text" class="form-control" id="peer_endpoint_allowed_ips">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_mtu" class="form-label">MTU</label>
<input type="text" class="form-control" id="peer_mtu">
</div>
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_keep_alive" class="form-label">Persistent Keepalive</label>
<input type="text" class="form-control" id="peer_keep_alive">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save_peer_setting" conf_id={{conf_data['name']}}
peer_id="">Save</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="available_ip_modal" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Select available IP</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="selected_ip" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED IP (CLICK TO REMOVE)</strong></small>
<div id="selected_ip_list"></div>
</div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirm_ip">Confirm</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="delete_bulk_modal" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Select Peers to Delete</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div id="bulk_remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert"
style="margin: 1rem">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="selected_peers" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED PEERS (CLICK TO REMOVE)</strong></small>
<div id="selected_peer_list"></div>
</div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div>
</div>
<div class="modal-footer">
<a class="text-danger" id="select_all_delete_bulk_peers"
style="cursor: pointer; margin-right: auto;"><small><strong>SELECT ALL</strong></small></a>
<button type="button" class="btn btn-danger" id="confirm_delete_bulk_peers" disabled
data-conf="{{conf_data['name']}}">Delete</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="qrcode_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">QR Code</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<img id="qrcode_img" style="width: 100%">
</div>
</div>
</div>
</div>

View File

@ -1,10 +1,11 @@
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow"> <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="/">WGDashboard</a> <a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="/">WGDashboard</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" <!-- <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse"
data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation"> data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button> -->
</nav> </nav>
<div class="progress" style="height: 3px; position: fixed; width: 100%; z-index: 10000; background-color: transparent"> <div class="progress" style="height: 3px; position: fixed; width: 100%; z-index: 10000; background-color: transparent">
<div class="progress-bar" role="progressbar" style="z-index: 10000; width: 0%"></div> <div class="progress-bar" role="progressbar" style="z-index: 10000; width: 0%"></div>
</div> </div>

View File

@ -197,8 +197,9 @@
countdown--; countdown--;
}, 1000) }, 1000)
$.post('/update_wg_conf_path', $('.update_wg_conf_path').serialize()) $.post('/update_wg_conf_path', $('.update_wg_conf_path').serialize())
}); });
$(".bottomNavSettings").addClass("active");
</script> </script>
</html> </html>

View File

@ -1,3 +1,76 @@
<div class="bottomNavWrapper"></div>
<div class="bottom">
<nav class="navbar navbar-dark fixed-bottom bg-light flex-md-nowrap p-0 bottomNav">
<div class="bottomNavContainer" style="z-index: 1000;">
<div class="bottomNavButton bottomNavHome">
<i class="bi bi-house"></i>
Home
</div>
<div class="bottomNavButton bottomNavConfigs">
<i class="bi bi-files"></i>
Configs
<div class="subNav bg-light animate__animated">
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Configurations</span>
</h6>
<ul class="nav flex-column">
{% for i in conf%}
<li class="nav-item"><a class="nav-link nav-conf-link sb-{{i['conf']}}-url" href="/configuration/{{i['conf']}}" data-conf-id="{{i['conf']}}"><samp>{{i['conf']}}</samp></a></li>
{%endfor%}
</ul>
<hr>
</div>
</div>
<div class="bottomNavButton bottomNavSettings">
<i class="bi bi-gear"></i>
Settings
</div>
<div class="bottomNavButton bottomNavMore">
<i class="bi bi-justify"></i>
More
<div class="subNav bg-light animate__animated">
<ul class="nav flex-column">
{% if session['update'] == "true" %}
<li class="nav-item sb-update-li">
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>New Update</span>
</h6>
<a class="nav-link sb-update-url text-success" href="https://github.com/donaldzou/WGDashboard#-how-to-update-the-dashboard">
{{ session['updateInfo']['name'] }} -
<code>{{ session['updateInfo']['tag_name'] }}</code>
</a>
</li>
{% endif %}
</ul>
<hr>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Tools</span>
</h6>
<ul class="nav flex-column">
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link" data-toggle="modal" data-target="#ping_modal" href="#">Ping</a></li>
<li class="nav-item"><a class="nav-link" data-toggle="modal" data-target="#traceroute_modal" href="#">Traceroute</a></li>
</ul>
</ul>
<hr>
{% if "username" in session %}
<ul class="nav flex-column">
<li class="nav-item"><a class="nav-link text-danger" href="/signout" style="font-weight: bold">Sign Out</a></li>
</ul>
{% endif %}
<ul class="nav flex-column">
<li class="nav-item"><a href="https://github.com/donaldzou/WGDashboard/releases/tag/{{ session['dashboard_version'] }}"><small class="nav-link text-muted">{{ session['dashboard_version'] }}</small></a></li>
</ul>
</div>
</div>
</div>
</nav>
</div>
<div class="row"> <div class="row">
<div class="row"> <div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse"> <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
@ -9,7 +82,13 @@
{% endif %} {% endif %}
{% if session['update'] == "true" %} {% if session['update'] == "true" %}
<li class="nav-item sb-update-li"> <li class="nav-item sb-update-li">
<a class="nav-link sb-update-url" href="https://github.com/donaldzou/WGDashboard#-how-to-update-the-dashboard">New Update Available!<span class="dot dot-running"></span></a> <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>New Update</span>
</h6>
<a class="nav-link sb-update-url text-success" href="https://github.com/donaldzou/WGDashboard#-how-to-update-the-dashboard">
{{ session['updateInfo']['name'] }} -
<code>{{ session['updateInfo']['tag_name'] }}</code>
</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>