mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
v2.2-beta4
Finished testing the QR code function and finish debugging the peer file download function.
This commit is contained in:
parent
02886dd420
commit
905c068ed5
23
README.md
23
README.md
@ -20,8 +20,9 @@
|
||||
## 📣 What's New: Version v2.2
|
||||
|
||||
- 🎉 **New Features**
|
||||
- **QR Code**: You can add the private key in peer setting of your existed peer to create a QR code. Or just create a new one, dashboard will now be able to auto generate a private key and public key ;) Don't worry, all keys will be generated on your machine, and **will delete all key files after they got generated**. [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/15)]
|
||||
- **Autostart on boot**: Added a tutorial on how to start the dashboard to on boot! Please read the [tutorial below](#autostart-wireguard-dashboard-on-boot). [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
|
||||
- **QR Code:** You can add the private key in peer setting of your existed peer to create a QR code. Or just create a new one, dashboard will now be able to auto generate a private key and public key ;) Don't worry, all keys will be generated on your machine, and **will delete all key files after they got generated**. [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
|
||||
- **Peer configuration file download:** Same as QR code, you now can download the peer configuration file, so you don't need to manually input all the details on the peer machine! [❤️ in [#40](https://github.com/donaldzou/wireguard-dashboard/issues/40)]
|
||||
- **Autostart on boot:** Added a tutorial on how to start the dashboard to on boot! Please read the [tutorial below](#autostart-wireguard-dashboard-on-boot). [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
|
||||
- 🪚 **Bug Fixed**
|
||||
- When there are comments in the wireguard config file, will cause the dashboard to crash.
|
||||
- Used regex to search for config files.
|
||||
@ -78,11 +79,11 @@
|
||||
1. **Download Wireguard Dashboard**
|
||||
|
||||
```shell
|
||||
$ git clone -b v2.2 https://github.com/donaldzou/Wireguard-Dashboard.git
|
||||
$ git clone -b v2.2 https://github.com/donaldzou/wireguard-dashboard.git
|
||||
2. **Install Python Dependencies**
|
||||
|
||||
```shell
|
||||
$ cd Wireguard-Dashboard/src
|
||||
$ cd wireguard-dashboard/src
|
||||
$ python3 -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
@ -234,9 +235,7 @@ In the `src` folder, it contained a file called `wg-dashboard.service`, we can u
|
||||
$ sudo systemctl restart wg-dashboard.service # <-- To restart the service
|
||||
```
|
||||
|
||||
8. And now you can reboot your system, and use the command at step 6 to see if it will auto start after the reboot. If you have any questions or problem, please report a bug.
|
||||
|
||||
⚠️ **For first time user please also read the next section.**
|
||||
8. **And now you can reboot your system, and use the command at step 6 to see if it will auto start after the reboot. If you have any questions or problem, please report a bug.**
|
||||
|
||||
## ✂️ Dashboard Configuration
|
||||
|
||||
@ -261,9 +260,9 @@ Since version 2.0, Wireguard Dashboard will be using a configuration file called
|
||||
|
||||
All these settings will be able to configure within the dashboard in **Settings** on the sidebar, without changing the actual file. **Except `version` and `auth_req` due to security consideration.**
|
||||
|
||||
#### Generating QR code
|
||||
#### Generating QR code and peer configuration file (.conf)
|
||||
|
||||
Starting version 2.2, dashboard can now generate QR code for each peer. Here is a template of what each QR code encoded with:
|
||||
Starting version 2.2, dashboard can now generate QR code and configuration file for each peer. Here is a template of what each QR code encoded with and the same content will be inside the file:
|
||||
|
||||
```
|
||||
[Interface]
|
||||
@ -282,10 +281,10 @@ Endpoint = 0.0.0.0:51820
|
||||
| **`[Interface]`** | | |
|
||||
| `PrivateKey` | The private key of this peer | N/A |
|
||||
| `Address` | The `allowed_ips` of your peer | N/A |
|
||||
| `DNS` | The DNS server your peer will use | `1.1.1.1` - Cloud flare DNS, you can switch it to Google DNS - `8.8.8.8`, or use your own DNS, you can edit it later in the WireGuard phone app. |
|
||||
| `DNS` | The DNS server your peer will use | `1.1.1.1` - Cloud flare DNS, you can change it when you adding the peer or in the peer setting. |
|
||||
| **`[Peer]`** | | |
|
||||
| `PublicKey` | The public key of your server | N/A |
|
||||
| `AllowedIPs` | IP ranges for which a peer will route traffic | `0.0.0.0/0` - Indicated a default route to send all internet and VPN traffic through that peer |
|
||||
| `AllowedIPs` | IP ranges for which a peer will route traffic | `0.0.0.0/0` - Indicated a default route to send all internet and VPN traffic through that peer. |
|
||||
| `Endpoint` | Your wireguard server ip and port, the dashboard will search for your server's default interface's ip. | `<your server default interface ip>:<listen port>` |
|
||||
|
||||
## ❓ How to update the dashboard?
|
||||
@ -309,7 +308,7 @@ Endpoint = 0.0.0.0:51820
|
||||
### ⚠️ **Update from v1.x.x**
|
||||
|
||||
1. Stop the dashboard if it is running.
|
||||
2. You can use `git pull https://github.com/donaldzou/Wireguard-Dashboard.git v2.2` to get the new update inside `Wireguard-Dashboard` directory.
|
||||
2. You can use `git pull https://github.com/donaldzou/wireguard-dashboard.git v2.2` to get the new update inside `Wireguard-Dashboard` directory.
|
||||
3. Proceed **Step 2 & 3** in the [Install](#-install) step down below.
|
||||
|
||||
## 🔍 Screenshot
|
||||
|
181
src/dashboard.py
181
src/dashboard.py
@ -18,7 +18,7 @@ from tinydb import TinyDB, Query
|
||||
from icmplib import ping, multiping, traceroute, resolve, Host, Hop
|
||||
|
||||
# Dashboard Version
|
||||
dashboard_version = 'v2.1'
|
||||
dashboard_version = 'v2.2'
|
||||
# Dashboard Config Name
|
||||
dashboard_conf = 'wg-dashboard.ini'
|
||||
# Default Wireguard IP
|
||||
@ -186,6 +186,7 @@ def get_conf_peers_data(config_name):
|
||||
db.insert({
|
||||
"id": i['PublicKey'],
|
||||
"private_key": "",
|
||||
"DNS":"1.1.1.1",
|
||||
"name": "",
|
||||
"total_receive": 0,
|
||||
"total_sent": 0,
|
||||
@ -198,8 +199,12 @@ def get_conf_peers_data(config_name):
|
||||
})
|
||||
else:
|
||||
# Update database since V2.2
|
||||
update_db = {}
|
||||
if "private_key" not in search[0]:
|
||||
db.update({'private_key':''}, peers.id == i['PublicKey'])
|
||||
update_db['private_key'] = ''
|
||||
if "DNS" not in search[0]:
|
||||
update_db['DNS'] = '1.1.1.1'
|
||||
db.update(update_db, peers.id == i['PublicKey'])
|
||||
|
||||
tic = time.perf_counter()
|
||||
get_latest_handshake(config_name, db, peers)
|
||||
@ -271,6 +276,61 @@ def get_conf_list():
|
||||
conf = sorted(conf, key=itemgetter('conf'))
|
||||
return conf
|
||||
|
||||
def genKeys():
|
||||
gen = subprocess.check_output('wg genkey > private_key.txt && wg pubkey < private_key.txt > public_key.txt',
|
||||
shell=True)
|
||||
private = open('private_key.txt')
|
||||
private_key = private.readline().strip()
|
||||
public = open('public_key.txt')
|
||||
public_key = public.readline().strip()
|
||||
data = {"private_key": private_key, "public_key": public_key}
|
||||
private.close()
|
||||
public.close()
|
||||
os.remove('private_key.txt')
|
||||
os.remove('public_key.txt')
|
||||
return data
|
||||
|
||||
def genPubKey(private_key):
|
||||
pri_key_file = open('private_key.txt', 'w')
|
||||
pri_key_file.write(private_key)
|
||||
pri_key_file.close()
|
||||
try:
|
||||
check = subprocess.check_output("wg pubkey < private_key.txt > public_key.txt", shell=True)
|
||||
public = open('public_key.txt')
|
||||
public_key = public.readline().strip()
|
||||
os.remove('private_key.txt')
|
||||
os.remove('public_key.txt')
|
||||
return {"status":'success', "msg":"", "data":public_key}
|
||||
except subprocess.CalledProcessError as exc:
|
||||
os.remove('private_key.txt')
|
||||
return {"status":'failed', "msg":"Key is not the correct length or format", "data":""}
|
||||
|
||||
def checkKeyMatch(private_key, public_key, config_name):
|
||||
result = genPubKey(private_key)
|
||||
if result['status'] == 'failed':
|
||||
return result
|
||||
else:
|
||||
db = TinyDB('db/' + config_name + '.json')
|
||||
peers = Query()
|
||||
match = db.search(peers.id == result['data'])
|
||||
if len(match) != 1 or result['data'] != public_key:
|
||||
return {'status': 'failed', 'msg': 'Please check your private key, it does not match with the public key.'}
|
||||
else:
|
||||
return {'status': 'success'}
|
||||
|
||||
def checkAllowedIP(public_key, ip, config_name):
|
||||
db = TinyDB('db/' + config_name + '.json')
|
||||
peers = Query()
|
||||
peer = db.search(peers.id == public_key)
|
||||
if len(peer) != 1:
|
||||
return {'status': 'failed', 'msg': 'Peer does not exist'}
|
||||
else:
|
||||
existed_ip = db.search((peers.id != public_key) & (peers.allowed_ip == ip))
|
||||
if len(existed_ip) != 0:
|
||||
return {'status':'failed', 'msg':"Allowed IP already taken by another peer."}
|
||||
else:
|
||||
return {'status':'success'}
|
||||
|
||||
@app.before_request
|
||||
def auth_req():
|
||||
conf = configparser.ConfigParser(strict=False)
|
||||
@ -285,7 +345,10 @@ def auth_req():
|
||||
request.endpoint != "auth" and \
|
||||
"username" not in session:
|
||||
print("User not loggedin - Attemped access: "+str(request.endpoint))
|
||||
if request.endpoint != "index":
|
||||
session['message'] = "You need to sign in first!"
|
||||
else:
|
||||
session['message'] = ""
|
||||
return redirect(url_for("signin"))
|
||||
else:
|
||||
if request.endpoint in ['signin', 'signout', 'auth', 'settings', 'update_acct', 'update_pwd',
|
||||
@ -509,6 +572,7 @@ def get_conf(config_name):
|
||||
"public_key": get_conf_pub_key(config_name),
|
||||
"listen_port": get_conf_listen_port(config_name),
|
||||
"running_peer": get_conf_running_peer_number(config_name),
|
||||
|
||||
}
|
||||
if conf_data['status'] == "stopped":
|
||||
# return redirect('/')
|
||||
@ -540,6 +604,8 @@ def switch(config_name):
|
||||
|
||||
@app.route('/add_peer/<config_name>', methods=['POST'])
|
||||
def add_peer(config_name):
|
||||
db = TinyDB("db/" + config_name + ".json")
|
||||
peers = Query()
|
||||
data = request.get_json()
|
||||
public_key = data['public_key']
|
||||
allowed_ips = data['allowed_ips']
|
||||
@ -548,6 +614,8 @@ def add_peer(config_name):
|
||||
return config_name+" is not running."
|
||||
if public_key in keys:
|
||||
return "Public key already exist."
|
||||
if len(db.search(peers.allowed_ip.matches(allowed_ips))) != 0:
|
||||
return "Allowed IP already taken by another peer."
|
||||
else:
|
||||
status = ""
|
||||
try:
|
||||
@ -555,12 +623,11 @@ def add_peer(config_name):
|
||||
"wg set " + config_name + " peer " + public_key + " allowed-ips " + allowed_ips, shell=True, stderr=subprocess.STDOUT)
|
||||
status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT)
|
||||
get_conf_peers_data(config_name)
|
||||
db = TinyDB("db/" + config_name + ".json")
|
||||
peers = Query()
|
||||
db.update({"name": data['name'], "private_key": data['private_key']}, peers.id == public_key)
|
||||
db.update({"name": data['name'], "private_key": data['private_key'], "DNS": data['DNS']}, peers.id == public_key)
|
||||
db.close()
|
||||
return "true"
|
||||
except subprocess.CalledProcessError as exc:
|
||||
db.close()
|
||||
return exc.output.strip()
|
||||
|
||||
|
||||
@ -590,19 +657,47 @@ def remove_peer(config_name):
|
||||
return exc.output.strip()
|
||||
|
||||
|
||||
@app.route('/save_peer_name/<config_name>', methods=['POST'])
|
||||
def save_peer_name(config_name):
|
||||
@app.route('/save_peer_setting/<config_name>', methods=['POST'])
|
||||
def save_peer_setting(config_name):
|
||||
data = request.get_json()
|
||||
id = data['id']
|
||||
name = data['name']
|
||||
private_key = data['private_key']
|
||||
DNS = data['DNS']
|
||||
allowed_ip = data['allowed_ip']
|
||||
db = TinyDB("db/" + config_name + ".json")
|
||||
peers = Query()
|
||||
db.update({"name": name}, peers.id == id)
|
||||
if len(db.search(peers.id == id)) == 1:
|
||||
check_ip = checkAllowedIP(id, allowed_ip, config_name)
|
||||
if private_key != "":
|
||||
check_key = checkKeyMatch(private_key, id, config_name)
|
||||
if check_key['status'] == "failed":
|
||||
return jsonify(check_key)
|
||||
if check_ip['status'] == "failed":
|
||||
return jsonify(check_ip)
|
||||
|
||||
try:
|
||||
if allowed_ip == "":
|
||||
allowed_ip = '""'
|
||||
change_ip = subprocess.check_output('wg set '+config_name+" peer "+id+" allowed-ips "+allowed_ip, shell=True, stderr=subprocess.STDOUT)
|
||||
save_change_ip = subprocess.check_output('wg-quick save '+ config_name, shell=True,stderr=subprocess.STDOUT)
|
||||
if change_ip.decode("UTF-8") != "":
|
||||
return jsonify({"status":"failed", "msg": change_ip.decode("UTF-8")})
|
||||
|
||||
db.update({"name": name, "private_key": private_key, "DNS": DNS}, peers.id == id)
|
||||
db.close()
|
||||
return id + " " + name
|
||||
return jsonify({"status": "success", "msg": ""})
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return jsonify({"status":"failed", "msg": str(exc.output.decode("UTF-8").strip())})
|
||||
|
||||
|
||||
@app.route('/get_peer_name/<config_name>', methods=['POST'])
|
||||
|
||||
|
||||
else:
|
||||
return jsonify({"status":"failed","msg":"This peer does not exist."})
|
||||
|
||||
|
||||
@app.route('/get_peer_data/<config_name>', methods=['POST'])
|
||||
def get_peer_name(config_name):
|
||||
data = request.get_json()
|
||||
id = data['id']
|
||||
@ -610,39 +705,55 @@ def get_peer_name(config_name):
|
||||
peers = Query()
|
||||
result = db.search(peers.id == id)
|
||||
db.close()
|
||||
return result[0]['name']
|
||||
data = {"name": result[0]['name'], "allowed_ip":result[0]['allowed_ip'], "DNS": result[0]['DNS'], "private_key": result[0]['private_key']}
|
||||
return jsonify(data)
|
||||
|
||||
@app.route('/generate_peer', methods=['GET'])
|
||||
def generate_peer():
|
||||
gen = subprocess.check_output('wg genkey > private_key.txt && wg pubkey < private_key.txt > public_key.txt',shell=True)
|
||||
private = open('private_key.txt')
|
||||
private_key = private.readline().strip()
|
||||
public = open('public_key.txt')
|
||||
public_key = public.readline().strip()
|
||||
data = {"private_key": private_key, "public_key": public_key}
|
||||
private.close()
|
||||
public.close()
|
||||
os.remove('private_key.txt')
|
||||
os.remove('public_key.txt')
|
||||
return jsonify(data)
|
||||
return jsonify(genKeys())
|
||||
|
||||
@app.route('/generate_public_key', methods=['POST'])
|
||||
def generate_public_key():
|
||||
data = request.get_json()
|
||||
private_key = data['private_key']
|
||||
pri_key_file = open('private_key.txt', 'w')
|
||||
pri_key_file.write(private_key)
|
||||
pri_key_file.close()
|
||||
try:
|
||||
check = subprocess.check_output("wg pubkey < private_key.txt > public_key.txt", shell=True)
|
||||
public = open('public_key.txt')
|
||||
public_key = public.readline().strip()
|
||||
os.remove('private_key.txt')
|
||||
os.remove('public_key.txt')
|
||||
return jsonify({"status":'success', "msg":"", "data":public_key})
|
||||
except subprocess.CalledProcessError as exc:
|
||||
os.remove('private_key.txt')
|
||||
return jsonify({"status":'failed', "msg":"Key is not the correct length or format", "data":""})
|
||||
return jsonify(genPubKey(private_key))
|
||||
|
||||
@app.route('/check_key_match/<config_name>', methods=['POST'])
|
||||
def check_key_match(config_name):
|
||||
data = request.get_json()
|
||||
private_key = data['private_key']
|
||||
public_key = data['public_key']
|
||||
return jsonify(checkKeyMatch(private_key,public_key, config_name))
|
||||
|
||||
@app.route('/download/<config_name>', methods=['GET'])
|
||||
def download(config_name):
|
||||
id = request.args.get('id')
|
||||
db = TinyDB("db/" + config_name + ".json")
|
||||
peers = Query()
|
||||
print(id)
|
||||
get_peer = db.search(peers.id == id)
|
||||
print(get_peer)
|
||||
if len(get_peer) == 1:
|
||||
peer = get_peer[0]
|
||||
if peer['private_key'] != "":
|
||||
public_key = get_conf_pub_key(config_name)
|
||||
listen_port = get_conf_listen_port(config_name)
|
||||
endpoint = wg_ip+":"+listen_port
|
||||
private_key = peer['private_key']
|
||||
allowed_ip = peer['allowed_ip']
|
||||
DNS = peer['DNS']
|
||||
name = "".join(peer['name'].split(' '))
|
||||
if name == "": name = public_key
|
||||
def generate(private_key, allowed_ip, DNS, public_key, endpoint):
|
||||
yield "[Interface]\nPrivateKey = "+private_key+"\nAddress = "+allowed_ip+"\nDNS = "+DNS+"\n\n[Peer]\nPublicKey = "+public_key+"\nAllowedIPs = 0.0.0.0/0\nEndpoint = "+endpoint
|
||||
|
||||
return app.response_class(generate(private_key,allowed_ip,DNS, public_key,endpoint), mimetype='text/conf', headers={"Content-Disposition":"attachment;filename="+name+".conf"})
|
||||
else:
|
||||
return redirect("/configuration/" + config_name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def init_dashboard():
|
||||
# Set Default INI File
|
||||
|
@ -2,3 +2,4 @@ Flask==1.1.2
|
||||
tinydb==4.3.0
|
||||
ifcfg==0.21
|
||||
icmplib==2.1.1
|
||||
flask-qrcode==3.0.0
|
228
src/static/configuration.js
Normal file
228
src/static/configuration.js
Normal file
@ -0,0 +1,228 @@
|
||||
// Config Toggle
|
||||
$("body").on("click", ".switch", function (){
|
||||
$(this).siblings($(".spinner-border")).css("display", "inline-block");
|
||||
$(this).remove()
|
||||
location.replace("/switch/"+$(this).attr('id'));
|
||||
})
|
||||
|
||||
// Generating Keys
|
||||
function generate_key(){
|
||||
$.ajax({
|
||||
"url": "/generate_peer",
|
||||
"method": "GET",
|
||||
}).done(function(res){
|
||||
$("#private_key").val(res.private_key)
|
||||
$("#public_key").val(res.public_key)
|
||||
$("#add_peer_alert").addClass("d-none");
|
||||
$("#re_generate_key i").removeClass("rotating")
|
||||
})
|
||||
}
|
||||
|
||||
function generate_public_key(){
|
||||
$.ajax({
|
||||
"url": "/generate_public_key",
|
||||
"method": "POST",
|
||||
"headers":{"Content-Type": "application/json"},
|
||||
"data": JSON.stringify({"private_key": $("#private_key").val()})
|
||||
}).done(function(res){
|
||||
if(res['status'] === "failed"){
|
||||
$("#add_peer_alert").html(res['msg']+$("#add_peer_alert").html());
|
||||
$("#add_peer_alert").removeClass("d-none");
|
||||
}else{
|
||||
$("#add_peer_alert").addClass("d-none");
|
||||
}
|
||||
$("#public_key").val(res['data'])
|
||||
$("#re_generate_key i").removeClass("rotating")
|
||||
})
|
||||
}
|
||||
|
||||
// Add Peer
|
||||
$("#private_key").change(function(){
|
||||
if ($("#private_key").val().length > 0){
|
||||
$("#re_generate_key i").addClass("rotating")
|
||||
generate_public_key()
|
||||
}else{
|
||||
$("#public_key").removeAttr("disabled")
|
||||
$("#public_key").val("")
|
||||
}
|
||||
})
|
||||
|
||||
$('#add_modal').on('show.bs.modal', function (event) {
|
||||
generate_key()
|
||||
})
|
||||
|
||||
$("#re_generate_key").click(function (){
|
||||
$("#public_key").attr("disabled","disabled")
|
||||
$("#re_generate_key i").addClass("rotating")
|
||||
generate_key()
|
||||
})
|
||||
|
||||
$("#save_peer").click(function(){
|
||||
if ($("#allowed_ips") !== "" && $("#public_key") !== ""){
|
||||
var conf = $(this).attr('conf_id')
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/add_peer/"+conf,
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({
|
||||
"private_key":$("#private_key").val(),
|
||||
"public_key":$("#public_key").val(),
|
||||
"allowed_ips": $("#allowed_ips").val(),
|
||||
"name":$("#new_add_name").val(),
|
||||
"DNS": $("#DNS").val()
|
||||
}),
|
||||
success: function (response){
|
||||
if(response != "true"){
|
||||
$("#add_peer_alert").html(response+$("#add_peer_alert").html());
|
||||
$("#add_peer_alert").removeClass("d-none");
|
||||
}
|
||||
else{
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
var qrcodeModal = new bootstrap.Modal(document.getElementById('qrcode_modal'), {
|
||||
keyboard: false
|
||||
})
|
||||
|
||||
// QR Code
|
||||
$("body").on("click", ".btn-qrcode-peer", function (){
|
||||
qrcodeModal.toggle();
|
||||
$("#qrcode_img").attr('src', $(this).attr('img_src'))
|
||||
})
|
||||
|
||||
// Delete Peer Modal
|
||||
var deleteModal = new bootstrap.Modal(document.getElementById('delete_modal'), {
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
$("body").on("click", ".btn-delete-peer", function(){
|
||||
var peer_id = $(this).attr("id");
|
||||
$("#delete_peer").attr("peer_id", peer_id);
|
||||
deleteModal.toggle();
|
||||
})
|
||||
|
||||
$("#delete_peer").click(function(){
|
||||
var peer_id = $(this).attr("peer_id");
|
||||
var config = $(this).attr("conf_id");
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/remove_peer/"+config,
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({"action": "delete", "peer_id": peer_id}),
|
||||
success: function (response){
|
||||
if(response !== "true"){
|
||||
$("#remove_peer_alert").html(response+$("#add_peer_alert").html());
|
||||
$("#remove_peer_alert").removeClass("d-none");
|
||||
}
|
||||
else{
|
||||
deleteModal.toggle();
|
||||
load_data();
|
||||
$('#alertToast').toast('show');
|
||||
$('#alertToast .toast-body').html("Peer deleted!");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Peer Setting Modal
|
||||
var settingModal = new bootstrap.Modal(document.getElementById('setting_modal'), {
|
||||
keyboard: false
|
||||
})
|
||||
$("body").on("click", ".btn-setting-peer", function(){
|
||||
settingModal.toggle();
|
||||
var peer_id = $(this).attr("id");
|
||||
$("#save_peer_setting").attr("peer_id", peer_id);
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/get_peer_data/"+$("#setting_modal").attr("conf_id"),
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({"id": peer_id}),
|
||||
success: function(response){
|
||||
let peer_name = ((response['name'] === "") ? "Untitled Peer" : response['name']);
|
||||
$("#setting_modal .peer_name").html(peer_name);
|
||||
$("#setting_modal #peer_name_textbox").val(peer_name)
|
||||
$("#setting_modal #peer_private_key_textbox").val(response['private_key'])
|
||||
$("#setting_modal #peer_DNS_textbox").val(response['DNS'])
|
||||
$("#setting_modal #peer_allowed_ip_textbox").val(response['allowed_ip'])
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$('#setting_modal').on('hidden.bs.modal', function (event) {
|
||||
$("#setting_peer_alert").addClass("d-none");
|
||||
})
|
||||
|
||||
$("#peer_private_key_textbox").change(function(){
|
||||
if ($(this).val().length > 0){
|
||||
$.ajax({
|
||||
"url": "/check_key_match/"+$("#save_peer_setting").attr("conf_id"),
|
||||
"method": "POST",
|
||||
"headers":{"Content-Type": "application/json"},
|
||||
"data": JSON.stringify({
|
||||
"private_key": $("#peer_private_key_textbox").val(),
|
||||
"public_key": $("#save_peer_setting").attr("peer_id")
|
||||
})
|
||||
}).done(function(res){
|
||||
if(res['status'] == "failed"){
|
||||
$("#setting_peer_alert").html(res['msg']);
|
||||
$("#setting_peer_alert").removeClass("d-none");
|
||||
}else{
|
||||
$("#setting_peer_alert").addClass("d-none");
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
$("#save_peer_setting").click(function (){
|
||||
$(this).attr("disabled","disabled")
|
||||
$(this).html("Saving...")
|
||||
if ($("#peer_DNS_textbox").val() !== "" && $("#peer_allowed_ip_textbox").val() !== ""){
|
||||
var peer_id = $(this).attr("peer_id");
|
||||
var conf_id = $(this).attr("conf_id");
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/save_peer_setting/"+conf_id,
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({
|
||||
id: peer_id,
|
||||
name: $("#peer_name_textbox").val(),
|
||||
DNS: $("#peer_DNS_textbox").val(),
|
||||
private_key: $("#peer_private_key_textbox").val(),
|
||||
allowed_ip: $("#peer_allowed_ip_textbox").val()
|
||||
}),
|
||||
success: function (response){
|
||||
if (response['status'] === "failed"){
|
||||
$("#setting_peer_alert").html(response['msg']);
|
||||
$("#setting_peer_alert").removeClass("d-none");
|
||||
}else{
|
||||
settingModal.toggle();
|
||||
load_data();
|
||||
$('#alertToast').toast('show');
|
||||
$('#alertToast .toast-body').html("Peer Saved!");
|
||||
}
|
||||
$("#save_peer_setting").removeAttr("disabled")
|
||||
$("#save_peer_setting").html("Save")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
|
||||
$(".peer_private_key_textbox_switch").click(function (){
|
||||
let mode = (($("#peer_private_key_textbox").attr('type') === 'password') ? "text":"password")
|
||||
let icon = (($("#peer_private_key_textbox").attr('type') === 'password') ? "bi bi-eye-slash-fill":"bi bi-eye-fill")
|
||||
$("#peer_private_key_textbox").attr('type',mode)
|
||||
$(".peer_private_key_textbox_switch i").removeClass().addClass(icon)
|
||||
})
|
@ -130,8 +130,11 @@ body {
|
||||
|
||||
.btn-control{
|
||||
border: none !important;
|
||||
padding: 0;
|
||||
padding-right: 0.5rem;
|
||||
padding: 0 1rem 0 0;
|
||||
}
|
||||
|
||||
.share_peer_btn_group .btn-control{
|
||||
padding: 0 0 0 1rem;
|
||||
}
|
||||
|
||||
.btn-control:hover{
|
||||
@ -146,6 +149,10 @@ body {
|
||||
color:#007bff
|
||||
}
|
||||
|
||||
.btn-download-peer:hover{
|
||||
color: #17a2b8;
|
||||
}
|
||||
|
||||
.login-container{
|
||||
padding: 2rem;
|
||||
}
|
||||
@ -232,3 +239,11 @@ main{
|
||||
-o-animation: rotating 0.75s linear infinite;
|
||||
animation: rotating 0.75s linear infinite;
|
||||
}
|
||||
|
||||
.peer_private_key_textbox_switch{
|
||||
position: absolute;
|
||||
right: 2rem;
|
||||
transform: translateY(-28px);
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
</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">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="staticBackdropLabel">Add a new peer</h5>
|
||||
@ -25,8 +25,8 @@
|
||||
<form id="add_peer_form">
|
||||
<div class="alert alert-warning" role="alert" style="font-size: 0.8rem">
|
||||
To generate QR code for this new peer, you need to provide the private key, or use the generated key. If you don't need the QR code, simply remove the private key and insert your existed public key.
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<label for="private_key">Private Key</label>
|
||||
</div>
|
||||
@ -37,19 +37,31 @@
|
||||
<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" id="public_key" aria-describedby="public_key" disabled>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="form-group">
|
||||
<label for="allowed_ips">Allowed IPs <code>(Required)</code></label>
|
||||
<input type="text" class="form-control" id="allowed_ips">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="form-group">
|
||||
<label for="DNS">DNS</label>
|
||||
<input type="text" class="form-control" id="DNS" value="1.1.1.1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<div class="form-group">
|
||||
<label for="new_add_name">Name</label>
|
||||
<input type="text" class="form-control" id="new_add_name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -88,7 +100,7 @@
|
||||
|
||||
<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">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="peer_name"></h5>
|
||||
@ -97,15 +109,33 @@
|
||||
</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">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="peer_name" class="form-label">Name</label>
|
||||
<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 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 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 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="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" id="save_peer_name" conf_id={{conf_data['name']}} peer_id="">Save</button>
|
||||
<button type="button" class="btn btn-primary" id="save_peer_setting" conf_id={{conf_data['name']}} peer_id="">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -180,180 +210,5 @@
|
||||
load_data();
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function generate_key(){
|
||||
$.ajax({
|
||||
"url": "/generate_peer",
|
||||
"method": "GET",
|
||||
}).done(function(res){
|
||||
$("#private_key").val(res.private_key)
|
||||
$("#public_key").val(res.public_key)
|
||||
$("#add_peer_alert").addClass("d-none");
|
||||
$("#re_generate_key i").removeClass("rotating")
|
||||
})
|
||||
}
|
||||
function generate_public_key(){
|
||||
$.ajax({
|
||||
"url": "/generate_public_key",
|
||||
"method": "POST",
|
||||
"headers":{"Content-Type": "application/json"},
|
||||
"data": JSON.stringify({"private_key": $("#private_key").val()})
|
||||
}).done(function(res){
|
||||
if(res['status'] === "failed"){
|
||||
$("#add_peer_alert").html(res['msg']+$("#add_peer_alert").html());
|
||||
$("#add_peer_alert").removeClass("d-none");
|
||||
}else{
|
||||
$("#add_peer_alert").addClass("d-none");
|
||||
}
|
||||
$("#public_key").val(res['data'])
|
||||
$("#re_generate_key i").removeClass("rotating")
|
||||
})
|
||||
}
|
||||
|
||||
$("#private_key").change(function(){
|
||||
if ($("#private_key").val().length > 0){
|
||||
$("#re_generate_key i").addClass("rotating")
|
||||
generate_public_key()
|
||||
}else{
|
||||
$("#public_key").removeAttr("disabled")
|
||||
$("#public_key").val("")
|
||||
}
|
||||
})
|
||||
$('#add_modal').on('show.bs.modal', function (event) {
|
||||
generate_key()
|
||||
})
|
||||
$("#re_generate_key").click(function (){
|
||||
$("#public_key").attr("disabled","disabled")
|
||||
$("#re_generate_key i").addClass("rotating")
|
||||
generate_key()
|
||||
})
|
||||
|
||||
$("body").on("click", ".switch", function (){
|
||||
$(this).siblings($(".spinner-border")).css("display", "inline-block");
|
||||
$(this).remove()
|
||||
location.replace("/switch/"+$(this).attr('id'));
|
||||
})
|
||||
|
||||
|
||||
|
||||
$("#save_peer").click(function(){
|
||||
if ($("#allowed_ips") !== "" && $("#public_key") !== ""){
|
||||
var conf = $(this).attr('conf_id')
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/add_peer/"+conf,
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({
|
||||
"private_key":$("#private_key").val(),
|
||||
"public_key":$("#public_key").val(),
|
||||
"allowed_ips": $("#allowed_ips").val(),
|
||||
"name":$("#new_add_name").val()
|
||||
}),
|
||||
success: function (response){
|
||||
if(response != "true"){
|
||||
$("#add_peer_alert").html(response+$("#add_peer_alert").html());
|
||||
$("#add_peer_alert").removeClass("d-none");
|
||||
}
|
||||
else{
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
var qrcodeModal = new bootstrap.Modal(document.getElementById('qrcode_modal'), {
|
||||
keyboard: false
|
||||
})
|
||||
|
||||
$("body").on("click", ".btn-qrcode-peer", function (){
|
||||
qrcodeModal.toggle();
|
||||
$("#qrcode_img").attr('src', $(this).attr('img_src'))
|
||||
})
|
||||
|
||||
var deleteModal = new bootstrap.Modal(document.getElementById('delete_modal'), {
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
$("body").on("click", ".btn-delete-peer", function(){
|
||||
var peer_id = $(this).attr("id");
|
||||
$("#delete_peer").attr("peer_id", peer_id);
|
||||
deleteModal.toggle();
|
||||
})
|
||||
|
||||
$("#delete_peer").click(function(){
|
||||
var peer_id = $(this).attr("peer_id");
|
||||
var config = $(this).attr("conf_id");
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/remove_peer/"+config,
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({"action": "delete", "peer_id": peer_id}),
|
||||
success: function (response){
|
||||
if(response !== "true"){
|
||||
$("#remove_peer_alert").html(response+$("#add_peer_alert").html());
|
||||
$("#remove_peer_alert").removeClass("d-none");
|
||||
}
|
||||
else{
|
||||
deleteModal.toggle();
|
||||
load_data();
|
||||
$('#alertToast').toast('show');
|
||||
$('#alertToast .toast-body').html("Peer deleted!");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
var settingModal = new bootstrap.Modal(document.getElementById('setting_modal'), {
|
||||
keyboard: false
|
||||
})
|
||||
|
||||
$("body").on("click", ".btn-setting-peer", function(){
|
||||
settingModal.toggle();
|
||||
var peer_id = $(this).attr("id");
|
||||
$("#save_peer_name").attr("peer_id", peer_id);
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/get_peer_name/"+$("#setting_modal").attr("conf_id"),
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({"id": peer_id}),
|
||||
success: function(response){
|
||||
if (response == ""){
|
||||
$("#setting_modal .peer_name").html("Untitled Peer");
|
||||
$("#peer_name_textbox").val("")
|
||||
}else{
|
||||
$("#setting_modal .peer_name").html(response);
|
||||
$("#peer_name_textbox").val(response)
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
$("#save_peer_name").click(function (){
|
||||
var peer_id = $(this).attr("peer_id");
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "/save_peer_name/"+"{{conf_data['name']}}",
|
||||
headers:{
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify({id: peer_id, name: $("#peer_name_textbox").val()}),
|
||||
success: function (response){
|
||||
settingModal.toggle();
|
||||
load_data();
|
||||
$('#alertToast').toast('show');
|
||||
$('#alertToast .toast-body').html("Name Saved!");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<script src="{{ url_for('static',filename='configuration.js') }}"></script>
|
||||
</html>
|
@ -107,14 +107,21 @@
|
||||
</div>
|
||||
<div class="w-100"></div>
|
||||
<div class="col-sm">
|
||||
<div class="button-group">
|
||||
<hr>
|
||||
<div class="button-group" style="display:flex">
|
||||
|
||||
<button type="button" class="btn btn-outline-primary btn-setting-peer btn-control" id="{{i['id']}}" data-toggle="modal"><i class="bi bi-gear-fill"></i></button>
|
||||
<button type="button" class="btn btn-outline-danger btn-delete-peer btn-control" id="{{i['id']}}" data-toggle="modal"><i class="bi bi-x-circle-fill"></i></button>
|
||||
{% if i['private_key'] %}
|
||||
<div class="share_peer_btn_group" style="margin-left: auto !important; display: inline">
|
||||
<button type="button" class="btn btn-outline-success btn-qrcode-peer btn-control" img_src="{{ qrcode("[Interface]\nPrivateKey = "+i['private_key']+"\nAddress = "+i['allowed_ip']+"\nDNS = 1.1.1.1\n\n[Peer]\nPublicKey = "+conf_data['public_key']+"\nAllowedIPs = 0.0.0.0/0\nEndpoint = "+wg_ip+":"+conf_data['listen_port']) }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="width: 19px;" fill="#28a745"><path d="M3 11h8V3H3v8zm2-6h4v4H5V5zM3 21h8v-8H3v8zm2-6h4v4H5v-4zM13 3v8h8V3h-8zm6 6h-4V5h4v4zM13 13h2v2h-2zM15 15h2v2h-2zM13 17h2v2h-2zM17 17h2v2h-2zM19 19h2v2h-2zM15 19h2v2h-2zM17 13h2v2h-2zM19 15h2v2h-2z"/></svg>
|
||||
</button>
|
||||
<a href="/download/{{ conf_data['name'] }}?id={{ i['id']|urlencode }}" type="button" class="btn btn-outline-info btn-download-peer btn-control">
|
||||
<i class="bi bi-download"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -93,9 +93,9 @@ if [ "$#" != 1 ];
|
||||
stop_wgd
|
||||
sleep 2
|
||||
printf "Wireguard Dashboard is stopped. \n"
|
||||
start_wgd_debug
|
||||
start_wgd
|
||||
else
|
||||
start_wgd_debug
|
||||
start_wgd
|
||||
fi
|
||||
elif [ "$1" = "debug" ]; then
|
||||
if check_wgd_status; then
|
||||
|
Loading…
Reference in New Issue
Block a user