mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
Getting ready for v3.0
This commit is contained in:
parent
584118805a
commit
6d56967a0f
54
README.md
54
README.md
@ -1,19 +1,9 @@
|
|||||||
<hr>
|
|
||||||
|
|
||||||
> Hi! I'm planning the next major update for this project, please let me know if you have any suggestions or feature requests ;) You can create an issue with the "Feature request" template. Cheers!
|
|
||||||
|
|
||||||
### Help Wanted
|
|
||||||
|
|
||||||
> If anyone know a better way to distribute releases of python application other than GitHub, please let me know in <a href="https://github.com/donaldzou/wireguard-dashboard/issues/103">#103</a>!
|
|
||||||
|
|
||||||
> Please provide your OS name and version if you can run the dashboard on it perfectly in <a href="https://github.com/donaldzou/wireguard-dashboard/issues/31">#31</a>, since I only tested on Ubuntu. Thank you!
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="WGDashboard" src="img/logo.png" width="128">
|
<img alt="WGDashboard" src="img/logo.png" width="128">
|
||||||
</p>
|
</p>
|
||||||
<h1 align="center">WGDashboard</h1>
|
<h1 align="center">WGDashboard</h1>
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="http://ForTheBadge.com/images/badges/made-with-python.svg">
|
<img src="http://ForTheBadge.com/images/badges/made-with-python.svg">
|
||||||
</p>
|
</p>
|
||||||
@ -24,19 +14,25 @@
|
|||||||
<p align="center">Monitoring WireGuard is not convinient, need to login into server and type <code>wg show</code>. That's why this platform is being created, to view all configurations and manage them in a easier way.</p>
|
<p align="center">Monitoring WireGuard is not convinient, need to login into server and type <code>wg show</code>. That's why this platform is being created, to view all configurations and manage them in a easier way.</p>
|
||||||
<p align="center"><small>Note: This project is not affiliate to the official WireGuard Project ;)</small></p>
|
<p align="center"><small>Note: This project is not affiliate to the official WireGuard Project ;)</small></p>
|
||||||
|
|
||||||
## 📣 What's New: v2.3
|
## 📣 What's New: v3.0
|
||||||
|
|
||||||
- 🎉 **New Features**
|
- 🎉 **New Features**
|
||||||
- **Update directly from `wgd.sh`:** Now you can update WGDashboard directly from the bash script.
|
- **Add Peers by Bulk: ** Now you can add peers by bulk, just simply set the amount and click add.
|
||||||
- **Displaying Peers:** You can switch the display mode between list and table in the configuration page.
|
- **Delete Peers by Bulk**: User can click the menu button (three-dots) on the bottom right of each configuration, and click **Delete Peers**, then select peers.
|
||||||
|
- **Added Pre-shared Key to peers:** Now each peer can add with a pre-shared key to enhance security. Previously added peers can add the pre-shared key through the peer setting button.
|
||||||
- 🪚 **Bug Fixed**
|
- 🪚 **Bug Fixed**
|
||||||
- [Peer DNS Validation Fails #67](issues/67): Added DNS format check. [❤️ @realfian]
|
- [IP Sorting range issues #99](https://github.com/donaldzou/WGDashboard/issues/99) [❤️ @barryboom]
|
||||||
- [configparser.NoSectionError: No section: 'Interface' #66](issues/66): Changed permission requirement for `etc/wireguard` from `744` to `755`. [❤️ @ramalmaty]
|
- [INvalid character written to tunnel json file #108](https://github.com/donaldzou/WGDashboard/issues/108) [❤️ @ ikidd]
|
||||||
- [Feature request: Interface not loading when information missing #73](issues/73): Fixed when Configuration Address and Listen Port is missing will crash the dashboard. [❤️ @js32]
|
- [Add IPv6 #91](https://github.com/donaldzou/WGDashboard/pull/91) [❤️ @ pgalonza]
|
||||||
- [Remote Peer, MTU and PersistentKeepalives added #70](pull/70): Added MTU, remote peer and Persistent Keepalive. [❤️ @realfian]
|
- [Added MTU and PersistentKeepalive to QR code and download files #112](https://github.com/donaldzou/WGDashboard/pull/112) [:heart: @reafian]
|
||||||
- [Fixes DNS check to support search domain #65](pull/65): Added allow input domain into DNS. [❤️@davejlong]
|
|
||||||
- **🧐 Other Changes**
|
- **🧐 Other Changes**
|
||||||
- Moved Add Peer Button into the right bottom corner.
|
- **Key generating moved to front-end**: No longer need to use the server's WireGuard to generate keys, thanks to the `wireguard.js` from the [official repository](https://git.zx2c4.com/wireguard-tools/tree/contrib/keygen-html/wireguard.js)!
|
||||||
|
- **Peer transfer calculation**: each peer will now show all transfer amount (previously was only showing transfer amount from the last configuration start-up).
|
||||||
|
- **UI adjustment on running peers**: peers will have a new style indicating that it is running.
|
||||||
|
- **Moved from TinyDB to SQLite**: This could provide better performance and loading speed when showing peers, also avoided changing the database could crash.
|
||||||
|
- UI adjustment on the whole dashboard.
|
||||||
|
- **`wgd.sh` finally can update itself**: So now user could update the whole dashboard from `wgd.sh`, with the `update` command.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -358,6 +354,24 @@ Endpoint = 0.0.0.0:51820
|
|||||||
|
|
||||||
## ⏰ Changelog
|
## ⏰ Changelog
|
||||||
|
|
||||||
|
#### v2.3.1 - Sep 8, 2021
|
||||||
|
|
||||||
|
- Updated dashboard's name to **WGDashboard**!!
|
||||||
|
|
||||||
|
#### v2.3 - Sep 8, 2021
|
||||||
|
|
||||||
|
- 🎉 **New Features**
|
||||||
|
- **Update directly from `wgd.sh`:** Now you can update WGDashboard directly from the bash script.
|
||||||
|
- **Displaying Peers:** You can switch the display mode between list and table in the configuration page.
|
||||||
|
- 🪚 **Bug Fixed**
|
||||||
|
- [Peer DNS Validation Fails #67](issues/67): Added DNS format check. [❤️ @realfian]
|
||||||
|
- [configparser.NoSectionError: No section: 'Interface' #66](issues/66): Changed permission requirement for `etc/wireguard` from `744` to `755`. [❤️ @ramalmaty]
|
||||||
|
- [Feature request: Interface not loading when information missing #73](issues/73): Fixed when Configuration Address and Listen Port is missing will crash the dashboard. [❤️ @js32]
|
||||||
|
- [Remote Peer, MTU and PersistentKeepalives added #70](pull/70): Added MTU, remote peer and Persistent Keepalive. [❤️ @realfian]
|
||||||
|
- [Fixes DNS check to support search domain #65](pull/65): Added allow input domain into DNS. [❤️@davejlong]
|
||||||
|
- **🧐 Other Changes**
|
||||||
|
- Moved Add Peer Button into the right bottom corner.
|
||||||
|
|
||||||
#### v2.2.1 - Aug 16, 2021
|
#### v2.2.1 - Aug 16, 2021
|
||||||
|
|
||||||
Bug Fixed:
|
Bug Fixed:
|
||||||
|
@ -1000,24 +1000,29 @@ def remove_peer(config_name):
|
|||||||
if get_conf_status(config_name) == "stopped":
|
if get_conf_status(config_name) == "stopped":
|
||||||
return "Your need to turn on " + config_name + " first."
|
return "Your need to turn on " + config_name + " first."
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
delete_key = data['peer_id']
|
delete_keys = data['peer_ids']
|
||||||
keys = get_conf_peer_key(config_name)
|
keys = get_conf_peer_key(config_name)
|
||||||
if not isinstance(keys, list):
|
if not isinstance(keys, list):
|
||||||
return config_name + " is not running."
|
return config_name + " is not running."
|
||||||
if delete_key not in keys:
|
|
||||||
return "This key does not exist"
|
|
||||||
else:
|
else:
|
||||||
|
sql_command = []
|
||||||
|
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:
|
try:
|
||||||
remove_wg = subprocess.check_output(f"wg set {config_name} peer {delete_key} remove",
|
remove_wg = subprocess.check_output(" ".join(wg_command),
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
shell=True, stderr=subprocess.STDOUT)
|
||||||
save_wg = subprocess.check_output(f"wg-quick save {config_name}",
|
save_wg = subprocess.check_output(f"wg-quick save {config_name}", shell=True, stderr=subprocess.STDOUT)
|
||||||
shell=True, stderr=subprocess.STDOUT)
|
g.cur.executescript(' '.join(sql_command))
|
||||||
sql = "DELETE FROM " + config_name + " WHERE id = ?"
|
|
||||||
g.cur.execute(sql, (delete_key,))
|
|
||||||
g.db.commit()
|
g.db.commit()
|
||||||
return "true"
|
|
||||||
except subprocess.CalledProcessError as exc:
|
except subprocess.CalledProcessError as exc:
|
||||||
return exc.output.strip()
|
return exc.output.strip()
|
||||||
|
return "true"
|
||||||
|
|
||||||
|
|
||||||
# Save peer settings
|
# Save peer settings
|
||||||
|
@ -241,18 +241,77 @@ main{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.peer_list{
|
.peer_list{
|
||||||
margin-bottom: 4rem !important;
|
margin-bottom: 7rem !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.add_btn{
|
.btn-manage-group{
|
||||||
|
z-index: 99;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 3rem;
|
bottom: 3rem;
|
||||||
right: 2rem;
|
right: 2rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-manage-group .setting_btn_menu{
|
||||||
|
position: absolute;
|
||||||
|
top: -124px;
|
||||||
|
background-color: white;
|
||||||
|
padding: 1rem 0;
|
||||||
|
right: 0;
|
||||||
|
box-shadow: 0 10px 20px rgb(0 0 0 / 19%), 0 6px 6px rgb(0 0 0 / 23%);
|
||||||
|
border-radius: 10px;
|
||||||
|
min-width: 200px;
|
||||||
|
display: none;
|
||||||
|
transform: translateY(-30px);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s cubic-bezier(0.58, 0.03, 0.05, 1.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-manage-group .setting_btn_menu.show{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting_btn_menu.showing{
|
||||||
|
transform: translateY(0px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting_btn_menu a{
|
||||||
|
display: flex;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
transition: all 0.1s ease-in-out;
|
||||||
|
font-size: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting_btn_menu a:hover{
|
||||||
|
background-color: #efefef;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting_btn_menu a i{
|
||||||
|
margin-right: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add_btn{
|
||||||
|
height: 54px;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
border-radius: 100px !important;
|
border-radius: 100px !important;
|
||||||
padding: 10px 20px;
|
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;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting_btn{
|
||||||
|
height: 54px;
|
||||||
|
z-index: 99;
|
||||||
|
border-radius: 100px !important;
|
||||||
|
padding: 0 14px;
|
||||||
|
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
|
||||||
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -394,11 +453,22 @@ main{
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#selected_ip_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{
|
||||||
|
background-color: #dc3545;
|
||||||
|
border-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
#selected_peer_list{
|
||||||
|
max-height: 80px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
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
@ -6,7 +6,7 @@ let available_ips = [];
|
|||||||
let $save_peer = $("#save_peer");
|
let $save_peer = $("#save_peer");
|
||||||
|
|
||||||
$(".add_btn").on("click", function(){
|
$(".add_btn").on("click", function(){
|
||||||
addModal.toggle();
|
$addModal.toggle();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,7 +157,8 @@ $body.on("click", ".available-ip-item", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let $ipModal = new bootstrap.Modal(document.getElementById('available_ip_modal'), {
|
let $ipModal = new bootstrap.Modal(document.getElementById('available_ip_modal'), {
|
||||||
keyboard: false
|
keyboard: false,
|
||||||
|
backdrop: 'static'
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#search_available_ip").on("click", function () {
|
$("#search_available_ip").on("click", function () {
|
||||||
@ -209,8 +210,9 @@ $("#re_generate_key").on("click",function (){
|
|||||||
generate_key();
|
generate_key();
|
||||||
});
|
});
|
||||||
|
|
||||||
let addModal = new bootstrap.Modal(document.getElementById('add_modal'), {
|
let $addModal = new bootstrap.Modal(document.getElementById('add_modal'), {
|
||||||
keyboard: false
|
keyboard: false,
|
||||||
|
backdrop: 'static'
|
||||||
});
|
});
|
||||||
|
|
||||||
function clean_ip(val){
|
function clean_ip(val){
|
||||||
@ -220,8 +222,9 @@ function clean_ip(val){
|
|||||||
}
|
}
|
||||||
|
|
||||||
let bulk_add_peers = () => {
|
let bulk_add_peers = () => {
|
||||||
|
let $new_add_amount = $("#new_add_amount");
|
||||||
$save_peer.attr("disabled","disabled");
|
$save_peer.attr("disabled","disabled");
|
||||||
$save_peer.html("Adding...");
|
$save_peer.html("Adding "+$new_add_amount.val()+" peers...");
|
||||||
|
|
||||||
let $new_add_DNS = $("#new_add_DNS");
|
let $new_add_DNS = $("#new_add_DNS");
|
||||||
$new_add_DNS.val(clean_ip($new_add_DNS.val()));
|
$new_add_DNS.val(clean_ip($new_add_DNS.val()));
|
||||||
@ -230,7 +233,6 @@ let bulk_add_peers = () => {
|
|||||||
let $new_add_MTU = $("#new_add_MTU");
|
let $new_add_MTU = $("#new_add_MTU");
|
||||||
let $new_add_keep_alive = $("#new_add_keep_alive");
|
let $new_add_keep_alive = $("#new_add_keep_alive");
|
||||||
let $enable_preshare_key = $("#enable_preshare_key");
|
let $enable_preshare_key = $("#enable_preshare_key");
|
||||||
let $new_add_amount = $("#new_add_amount");
|
|
||||||
let data_list = [$new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive];
|
let data_list = [$new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive];
|
||||||
if ($new_add_amount.val() > 0){
|
if ($new_add_amount.val() > 0){
|
||||||
if ($new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
|
if ($new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
|
||||||
@ -263,8 +265,8 @@ let bulk_add_peers = () => {
|
|||||||
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
data_list.forEach((ele) => ele.removeAttr("disabled"));
|
||||||
$("#add_peer_form").trigger("reset");
|
$("#add_peer_form").trigger("reset");
|
||||||
$save_peer.removeAttr("disabled").html("Save");
|
$save_peer.removeAttr("disabled").html("Save");
|
||||||
showToast($new_add_amount.val()+"peers added successful!");
|
showToast($new_add_amount.val()+" peers added successful!");
|
||||||
addModal.toggle();
|
$addModal.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -334,7 +336,7 @@ $save_peer.on("click",function(){
|
|||||||
$("#add_peer_form").trigger("reset");
|
$("#add_peer_form").trigger("reset");
|
||||||
$save_peer.removeAttr("disabled").html("Save");
|
$save_peer.removeAttr("disabled").html("Save");
|
||||||
showToast("Add peer successful!");
|
showToast("Add peer successful!");
|
||||||
addModal.toggle();
|
$addModal.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -366,7 +368,8 @@ $body.on("click", ".btn-qrcode-peer", function (){
|
|||||||
|
|
||||||
// Delete Peer Modal
|
// Delete Peer Modal
|
||||||
let deleteModal = new bootstrap.Modal(document.getElementById('delete_modal'), {
|
let deleteModal = new bootstrap.Modal(document.getElementById('delete_modal'), {
|
||||||
keyboard: false
|
keyboard: false,
|
||||||
|
backdrop: 'static'
|
||||||
});
|
});
|
||||||
|
|
||||||
$body.on("click", ".btn-delete-peer", function(){
|
$body.on("click", ".btn-delete-peer", function(){
|
||||||
@ -380,19 +383,40 @@ $("#delete_peer").on("click",function(){
|
|||||||
$(this).html("Deleting...");
|
$(this).html("Deleting...");
|
||||||
let peer_id = $(this).attr("peer_id");
|
let peer_id = $(this).attr("peer_id");
|
||||||
let config = $(this).attr("conf_id");
|
let config = $(this).attr("conf_id");
|
||||||
|
let peer_ids = [peer_id];
|
||||||
|
deletePeers(config, peer_ids);
|
||||||
|
});
|
||||||
|
|
||||||
|
function deletePeers(config, peer_ids){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/remove_peer/"+config,
|
url: "/remove_peer/"+config,
|
||||||
headers:{
|
headers:{
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
data: JSON.stringify({"action": "delete", "peer_id": peer_id}),
|
data: JSON.stringify({"action": "delete", "peer_ids": peer_ids}),
|
||||||
success: function (response){
|
success: function (response){
|
||||||
if(response !== "true"){
|
if(response !== "true"){
|
||||||
$("#remove_peer_alert").html(response+$("#add_peer_alert").html()).removeClass("d-none");
|
if (deleteModal._isShown) {
|
||||||
|
$("#remove_peer_alert").html(response+$("#add_peer_alert").html())
|
||||||
|
.removeClass("d-none");
|
||||||
|
}
|
||||||
|
if (deleteBulkModal._isShown){
|
||||||
|
$("#bulk_remove_peer_alert").html(response+$("#bulk_remove_peer_alert").html())
|
||||||
|
.removeClass("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
deleteModal.toggle();
|
if (deleteModal._isShown) {
|
||||||
|
deleteModal.toggle()
|
||||||
|
}
|
||||||
|
if (deleteBulkModal._isShown){
|
||||||
|
$("#confirm_delete_bulk_peers").removeAttr("disabled").html("Delete");
|
||||||
|
$("#selected_peer_list").html('');
|
||||||
|
$(".delete-bulk-peer-item.active").removeClass('active');
|
||||||
|
deleteBulkModal.toggle();
|
||||||
|
}
|
||||||
load_data($('#search_peer_textbox').val());
|
load_data($('#search_peer_textbox').val());
|
||||||
$('#alertToast').toast('show');
|
$('#alertToast').toast('show');
|
||||||
$('#alertToast .toast-body').html("Peer deleted!");
|
$('#alertToast .toast-body').html("Peer deleted!");
|
||||||
@ -400,11 +424,12 @@ $("#delete_peer").on("click",function(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// Peer Setting Modal
|
// Peer Setting Modal
|
||||||
let settingModal = new bootstrap.Modal(document.getElementById('setting_modal'), {
|
let settingModal = new bootstrap.Modal(document.getElementById('setting_modal'), {
|
||||||
keyboard: false
|
keyboard: false,
|
||||||
|
backdrop: 'static'
|
||||||
});
|
});
|
||||||
$body.on("click", ".btn-setting-peer", function(){
|
$body.on("click", ".btn-setting-peer", function(){
|
||||||
startProgressBar();
|
startProgressBar();
|
||||||
@ -638,6 +663,7 @@ $body.on("click", ".display_mode", function(){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Toggle bulk add mode
|
||||||
$("#bulk_add").on("change", function (){
|
$("#bulk_add").on("change", function (){
|
||||||
let hide = $(".non-bulk").find("input");
|
let hide = $(".non-bulk").find("input");
|
||||||
let amount = $("#new_add_amount");
|
let amount = $("#new_add_amount");
|
||||||
@ -655,4 +681,125 @@ $("#bulk_add").on("change", function (){
|
|||||||
}
|
}
|
||||||
amount.attr("disabled", "disabled");
|
amount.attr("disabled", "disabled");
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Configuration sub menu
|
||||||
|
let $setting_btn_menu = $(".setting_btn_menu");
|
||||||
|
$setting_btn_menu.css("top", ($setting_btn_menu.height() + 54)*(-1));
|
||||||
|
let $setting_btn = $(".setting_btn");
|
||||||
|
$setting_btn.on("click", function(){
|
||||||
|
if ($setting_btn_menu.hasClass("show")){
|
||||||
|
$setting_btn_menu.removeClass("showing");
|
||||||
|
setTimeout(function(){
|
||||||
|
$setting_btn_menu.removeClass("show");
|
||||||
|
}, 201)
|
||||||
|
|
||||||
|
}else{
|
||||||
|
$setting_btn_menu.addClass("show");
|
||||||
|
setTimeout(function(){
|
||||||
|
$setting_btn_menu.addClass("showing");
|
||||||
|
},10)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$body.on("click", function(r){
|
||||||
|
if (document.querySelector(".setting_btn") !== r.target){
|
||||||
|
if (!document.querySelector(".setting_btn").contains(r.target)){
|
||||||
|
if (!document.querySelector(".setting_btn_menu").contains(r.target)){
|
||||||
|
$setting_btn_menu.removeClass("showing");
|
||||||
|
setTimeout(function(){
|
||||||
|
$setting_btn_menu.removeClass("show");
|
||||||
|
}, 310)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete peers by bulk
|
||||||
|
let deleteBulkModal = new bootstrap.Modal(document.getElementById('delete_bulk_modal'), {
|
||||||
|
keyboard: false,
|
||||||
|
backdrop: 'static'
|
||||||
|
});
|
||||||
|
$("#delete_peers_by_bulk_btn").on("click", () => {
|
||||||
|
let $delete_bulk_modal_list = $("#delete_bulk_modal .list-group");
|
||||||
|
$delete_bulk_modal_list.html('');
|
||||||
|
peers.forEach((peer) => {
|
||||||
|
let name = ""
|
||||||
|
if (peer["name"] === "") { name = "Untitled Peer"; }
|
||||||
|
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="'
|
||||||
|
+peer['id']+'" data-name="'+name+'">'+name+'<br><code>'+peer['id']+'</code></a>');
|
||||||
|
});
|
||||||
|
deleteBulkModal.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleBulkIP(element){
|
||||||
|
let $selected_peer_list = $("#selected_peer_list");
|
||||||
|
let id = element.data("id");
|
||||||
|
let name = element.data("name") === "" ? "Untitled Peer" : element.data("name");
|
||||||
|
if (element.hasClass("active")){
|
||||||
|
element.removeClass("active");
|
||||||
|
$("#selected_peer_list .badge[data-id='"+id+"']").remove();
|
||||||
|
}else{
|
||||||
|
element.addClass("active");
|
||||||
|
$selected_peer_list.append('<span class="badge badge-danger delete-peer-bulk-badge" style="cursor: pointer; text-overflow: ellipsis; max-width: 100%; overflow-x: hidden" data-id="'+id+'">'+name+' - '+id+'</span>')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$body.on("click", ".delete-bulk-peer-item", function(){
|
||||||
|
toggleBulkIP($(this));
|
||||||
|
}).on("click", ".delete-peer-bulk-badge", function(){
|
||||||
|
toggleBulkIP($(".delete-bulk-peer-item[data-id='" + $(this).data("id") + "']"));
|
||||||
|
});
|
||||||
|
|
||||||
|
let $selected_peer_list = document.getElementById("selected_peer_list");
|
||||||
|
let changeObserver = new MutationObserver(function(mutationsList, observer){
|
||||||
|
if ($selected_peer_list.hasChildNodes()){
|
||||||
|
$("#confirm_delete_bulk_peers").removeAttr("disabled");
|
||||||
|
}else{
|
||||||
|
$("#confirm_delete_bulk_peers").attr("disabled", "disabled");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
changeObserver.observe($selected_peer_list, {
|
||||||
|
attributes: true,
|
||||||
|
childList: true,
|
||||||
|
characterData: true
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
let confirm_delete_bulk_peers_interval = undefined;
|
||||||
|
$("#confirm_delete_bulk_peers").on("click", function(){
|
||||||
|
let btn = $(this);
|
||||||
|
if (confirm_delete_bulk_peers_interval !== undefined){
|
||||||
|
clearInterval(confirm_delete_bulk_peers_interval);
|
||||||
|
confirm_delete_bulk_peers_interval = undefined;
|
||||||
|
btn.html("Delete");
|
||||||
|
}else{
|
||||||
|
let timer = 5;
|
||||||
|
btn.html(`Deleting in ${timer} secs... Click to cancel`);
|
||||||
|
confirm_delete_bulk_peers_interval = setInterval(function(){
|
||||||
|
timer -= 1;
|
||||||
|
btn.html(`Deleting in ${timer} secs... Click to cancel`);
|
||||||
|
if (timer === 0){
|
||||||
|
btn.html(`Deleting...`);
|
||||||
|
btn.attr("disabled", "disabled");
|
||||||
|
let ips = [];
|
||||||
|
$selected_peer_list.childNodes.forEach((ele) => ips.push(ele.dataset.id));
|
||||||
|
deletePeers(btn.data("conf"), ips);
|
||||||
|
clearInterval(confirm_delete_bulk_peers_interval);
|
||||||
|
confirm_delete_bulk_peers_interval = undefined;
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#select_all_delete_bulk_peers").on("click", function(){
|
||||||
|
$(".delete-bulk-peer-item").each(function(){
|
||||||
|
if (!$(this).hasClass("active")) toggleBulkIP($(this));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(deleteBulkModal._element).on("hidden.bs.modal", function(){
|
||||||
|
$(".delete-bulk-peer-item").each(function(){
|
||||||
|
if ($(this).hasClass("active")) toggleBulkIP($(this));
|
||||||
|
});
|
||||||
})
|
})
|
2
src/static/js/configuration.min.js
vendored
2
src/static/js/configuration.min.js
vendored
File diff suppressed because one or more lines are too long
@ -100,7 +100,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary add_btn"><i class="bi bi-plus-circle-fill" style=""></i> Add Peer</button>
|
<div class="btn-manage-group">
|
||||||
|
<button type="button" class="btn btn-primary add_btn"><i class="bi bi-plus-circle-fill" style=""></i></button>
|
||||||
|
<button type="button" class="btn btn-secondary setting_btn"><i class="bi bi-three-dots"></i></button>
|
||||||
|
<div class="setting_btn_menu">
|
||||||
|
<a class="text-danger" id="delete_peers_by_bulk_btn"><i class="bi bi-trash-fill"></i> Delete Peers</a>
|
||||||
|
<a class="text-info"><i class="bi bi-cloud-download-fill"></i> Download All Peers</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row peer_list"></div>
|
<div class="row peer_list"></div>
|
||||||
@ -113,26 +121,23 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="staticBackdropLabel">Add a new peer</h5>
|
<h5 class="modal-title" id="staticBackdropLabel">Add New Peer</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row">
|
<div>
|
||||||
<div class="col-sm" style="display: flex; align-items: center;">
|
<div class="custom-control custom-switch" style="margin-bottom: 1rem">
|
||||||
<div class="custom-control custom-switch">
|
<input class="custom-control-input" type="checkbox" id="bulk_add">
|
||||||
<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>
|
||||||
<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>
|
||||||
<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>
|
</div>
|
||||||
<div class="col-sm">
|
<div class="form-group" style="margin: 0">
|
||||||
<div class="form-group" style="margin: 0">
|
|
||||||
{# <label for="new_add_amount">Amount</label>#}
|
{# <label for="new_add_amount">Amount</label>#}
|
||||||
<input type="number" class="form-control" id="new_add_amount" min="1" placeholder="Amount" disabled>
|
<input type="number" class="form-control" id="new_add_amount" min="1" placeholder="Amount" disabled>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div id="add_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
|
<div id="add_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert">
|
||||||
@ -242,7 +247,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="setting_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
|
<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="">
|
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-dialog modal-dialog-centered modal-lg">
|
||||||
@ -316,7 +320,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="available_ip_modal" data-backdrop="static" data-keyboard="false" tabindex="1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
<div class="modal fade" id="available_ip_modal" data-backdrop="static" data-keyboard="false">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -341,7 +345,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</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">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="bulk_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">×</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"
|
<div class="modal fade" id="qrcode_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
|
||||||
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
@ -358,7 +389,6 @@
|
|||||||
</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" 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">
|
||||||
@ -381,6 +411,7 @@
|
|||||||
let load_timeout;
|
let load_timeout;
|
||||||
let load_interval = 0;
|
let load_interval = 0;
|
||||||
let conf_name = "{{ conf_data['name'] }}"
|
let conf_name = "{{ conf_data['name'] }}"
|
||||||
|
let peers = [];
|
||||||
$(".sb-"+conf_name+"-url").addClass("active");
|
$(".sb-"+conf_name+"-url").addClass("active");
|
||||||
function load_data(search){
|
function load_data(search){
|
||||||
startProgressBar()
|
startProgressBar()
|
||||||
@ -392,6 +423,7 @@
|
|||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
success: function (response){
|
success: function (response){
|
||||||
|
peers = response["peer_data"];
|
||||||
{# Check all status #}
|
{# Check all status #}
|
||||||
if (response["listen_port"] === "" && response["status"] === "stopped"){
|
if (response["listen_port"] === "" && response["status"] === "stopped"){
|
||||||
$("config_info_alert").append('<div class="alert alert-warning" role="alert">Peer QR Code and configuration file download required a specified <strong>Listen Port</strong>.</div>')
|
$("config_info_alert").append('<div class="alert alert-warning" role="alert">Peer QR Code and configuration file download required a specified <strong>Listen Port</strong>.</div>')
|
||||||
@ -480,11 +512,9 @@
|
|||||||
},response["dashboard_refresh_interval"])
|
},response["dashboard_refresh_interval"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{#$("#config_body").html(response);#}
|
|
||||||
$(".dot.dot-running").attr("title","Peer Running").tooltip();
|
$(".dot.dot-running").attr("title","Peer Running").tooltip();
|
||||||
$(".dot.dot-stopped").attr("title","Peer Stopped").tooltip();
|
$(".dot.dot-stopped").attr("title","Peer Stopped").tooltip();
|
||||||
$("i[data-toggle='tooltip']").tooltip();
|
$("i[data-toggle='tooltip']").tooltip();
|
||||||
{#$("#search_peer_textbox").css("display", "block")#}
|
|
||||||
endProgressBar()
|
endProgressBar()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user