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

Updated documentation, minor fixes

This commit is contained in:
theonlynexus 2022-07-20 15:25:01 +09:00
parent 305c23190c
commit 9e16619e56
9 changed files with 713 additions and 583 deletions

View File

@ -286,9 +286,9 @@ def update_peer_default_config():
config = g.conf config = g.conf
if ( if (
len(request.form["peer_endpoint_allowed_ips"]) == 0 not (request.form["peer_endpoint_allowed_ips"])
or len(request.form["peer_global_DNS"]) == 0 or not (request.form["peer_global_DNS"])
or len(request.form["peer_remote_endpoint"]) == 0 or not (request.form["peer_remote_endpoint"])
): ):
session["message"] = "Please fill in all required boxes." session["message"] = "Please fill in all required boxes."
session["message_status"] = "danger" session["message_status"] = "danger"

View File

@ -3,7 +3,10 @@ from __main__ import app
import sqlite3 import sqlite3
def get_net_stats(interface_name: str) -> list: def get_net_stats(interface_name: str) -> list[sqlite3.Row]:
"""
Gets net stats for all peers of `interface_name` and returns a list of dicts
"""
app.logger.debug(f"db.get_net_stats({interface_name})") app.logger.debug(f"db.get_net_stats({interface_name})")
data = g.cur.execute( data = g.cur.execute(
f"SELECT total_sent, total_receive, cumu_sent, cumu_receive FROM {interface_name}" f"SELECT total_sent, total_receive, cumu_sent, cumu_receive FROM {interface_name}"
@ -12,6 +15,9 @@ def get_net_stats(interface_name: str) -> list:
def get_net_stats_and_peer_status(interface_name: str, id: str) -> sqlite3.Row | None: def get_net_stats_and_peer_status(interface_name: str, id: str) -> sqlite3.Row | None:
"""
Gets net stats for a given peer of `interface_name` and the data as `dict`, `None` if not found.
"""
app.logger.debug(f"db.get_net_stats_and_peer_status({interface_name})") app.logger.debug(f"db.get_net_stats_and_peer_status({interface_name})")
data = g.cur.execute( data = g.cur.execute(
"""SELECT total_receive, total_sent, cumu_receive, cumu_sent, status """SELECT total_receive, total_sent, cumu_receive, cumu_sent, status
@ -21,7 +27,12 @@ def get_net_stats_and_peer_status(interface_name: str, id: str) -> sqlite3.Row |
return data.fetchone() return data.fetchone()
def get_peer_count_by_allowed_ips(interface_name: str, ip: str, id: str): def get_peer_count_by_allowed_ips(
interface_name: str, ip: str, id: str
) -> sqlite3.Row | None:
"""
Gets and returns the number of peers of `interface_name` that have allowed_ips similar to `ip`.
"""
app.logger.debug(f"db.get_peer_count_by_allowed_ips({interface_name}, {ip}, {id})") app.logger.debug(f"db.get_peer_count_by_allowed_ips({interface_name}, {ip}, {id})")
data = g.cur.execute( data = g.cur.execute(
f"""SELECT COUNT(*) FROM {interface_name} f"""SELECT COUNT(*) FROM {interface_name}
@ -31,7 +42,7 @@ def get_peer_count_by_allowed_ips(interface_name: str, ip: str, id: str):
return data.fetchone() return data.fetchone()
def get_peers(interface_name: str, search: str = None) -> list: def get_peers(interface_name: str, search: str = None) -> list[sqlite3.Row]:
"""Returns the list of records which name matches the search string, or all if no search is provided""" """Returns the list of records which name matches the search string, or all if no search is provided"""
app.logger.debug(f"db.get_peers({interface_name}, {search})") app.logger.debug(f"db.get_peers({interface_name}, {search})")
@ -44,8 +55,10 @@ def get_peers(interface_name: str, search: str = None) -> list:
return data.fetchall() return data.fetchall()
def get_peer_by_id(interface_name: str, id: str) -> list: def get_peer_by_id(interface_name: str, id: str) -> sqlite3.Row | None:
"""Returns the record matching the pass id or None.""" """
Returns the peer of `interface_name` matching `id` or None.
"""
app.logger.debug(f"db.get_peer_by_id({interface_name}, {id})") app.logger.debug(f"db.get_peer_by_id({interface_name}, {id})")
sql = "SELECT * FROM %s WHERE id='%s'" % (interface_name, id) sql = "SELECT * FROM %s WHERE id='%s'" % (interface_name, id)
@ -53,21 +66,29 @@ def get_peer_by_id(interface_name: str, id: str) -> list:
return data.fetchone() return data.fetchone()
def get_peer_allowed_ips(interface_name: str) -> list: def get_peer_allowed_ips(interface_name: str) -> list[sqlite3.Row]:
"""
Returns the `allowed_ips` of all peers of `interface_name`.
"""
app.logger.debug(f"db.get_peer_allowed_ips({interface_name})") app.logger.debug(f"db.get_peer_allowed_ips({interface_name})")
sql = f"SELECT allowed_ips FROM {interface_name}" sql = f"SELECT allowed_ips FROM {interface_name}"
data = g.cur.execute(sql) data = g.cur.execute(sql)
return data.fetchall() return data.fetchall()
def get_peer_ids(interface_name: str): def get_peer_ids(interface_name: str) -> list[sqlite3.Row]:
"""
Returns the `id`s of all peers of `interface_name`.
"""
app.logger.debug(f"db.get_peer_ids({interface_name})") app.logger.debug(f"db.get_peer_ids({interface_name})")
data = g.cur.execute("SELECT id FROM %s" % interface_name) data = g.cur.execute("SELECT id FROM %s" % interface_name)
return data.fetchall() return data.fetchall()
def remove_stale_peers(interface_name: str, peer_data: str): def remove_stale_peers(interface_name: str, peer_data: dict):
"""Removes entries that which id is present in the db, but not in peer_data""" """
Removes from the DB entries that are present there, but not in `peer_data`
"""
app.logger.debug(f"db.remove_stale_peers({interface_name}, peer_data)") app.logger.debug(f"db.remove_stale_peers({interface_name}, peer_data)")
db_key = set(map(lambda a: a[0], get_peer_ids(interface_name))) db_key = set(map(lambda a: a[0], get_peer_ids(interface_name)))
@ -79,12 +100,18 @@ def remove_stale_peers(interface_name: str, peer_data: str):
def delete_peer(interface_name: str, id: str): def delete_peer(interface_name: str, id: str):
"""
Removes a peer of `interface_name` with the given `id`
"""
app.logger.debug(f"db.delete_peer({interface_name}, {id})") app.logger.debug(f"db.delete_peer({interface_name}, {id})")
sql = "DELETE FROM %s WHERE id = '%s'" % (interface_name, id) sql = "DELETE FROM %s WHERE id = '%s'" % (interface_name, id)
g.cur.execute(sql) g.cur.execute(sql)
def insert_peer(interface_name: str, data: dict): def insert_peer(interface_name: str, data: dict):
"""
Inserts a peer of `interface_name` with the given `data`
"""
app.logger.debug(f"db.insert_peer({interface_name}, {data})") app.logger.debug(f"db.insert_peer({interface_name}, {data})")
sql = f""" sql = f"""
INSERT INTO {interface_name} INSERT INTO {interface_name}
@ -95,10 +122,13 @@ def insert_peer(interface_name: str, data: dict):
g.cur.execute(sql, data) g.cur.execute(sql, data)
def create_table_if_missing(table_name: str): def create_table_if_missing(interface_name: str):
app.logger.debug(f"db.create_table_if_missing({table_name})") """
Creates a table for `interface_name`, if missing.
"""
app.logger.debug(f"db.create_table_if_missing({interface_name})")
create_table = f""" create_table = f"""
CREATE TABLE IF NOT EXISTS {table_name} ( CREATE TABLE IF NOT EXISTS {interface_name} (
id VARCHAR NOT NULL PRIMARY KEY, private_key VARCHAR NULL UNIQUE, DNS VARCHAR NULL, id VARCHAR NOT NULL PRIMARY KEY, private_key VARCHAR NULL UNIQUE, DNS VARCHAR NULL,
endpoint_allowed_ips VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL, endpoint_allowed_ips VARCHAR NULL, name VARCHAR NULL, total_receive FLOAT NULL,
total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL, total_sent FLOAT NULL, total_data FLOAT NULL, endpoint VARCHAR NULL,
@ -111,6 +141,9 @@ def create_table_if_missing(table_name: str):
def update_peer(interface_name: str, data: dict): def update_peer(interface_name: str, data: dict):
"""
Updates the peer of `interface_name` with the given `data`, if the peer record exists.
"""
app.logger.debug(f"db.interface_name({data})") app.logger.debug(f"db.interface_name({data})")
id = data["id"] id = data["id"]
db_peer = get_peer_by_id(interface_name, id) db_peer = get_peer_by_id(interface_name, id)
@ -121,6 +154,10 @@ def update_peer(interface_name: str, data: dict):
def _update_peer(interface_name: str, data: dict): def _update_peer(interface_name: str, data: dict):
"""
Updates the peer of `interface_name` with the given `data`.
`data` should contain the peer's `id` (public key), plus any other field to be updated.
"""
app.logger.debug(f"db.update_peer({interface_name}, {data})") app.logger.debug(f"db.update_peer({interface_name}, {data})")
sql = f""" sql = f"""
UPDATE {interface_name} SET UPDATE {interface_name} SET

View File

@ -322,7 +322,7 @@ def add_peer(interface_name):
data = request.get_json() data = request.get_json()
public_key = data["public_key"] public_key = data["public_key"]
allowed_ips = data["allowed_ips"] allowed_ips = data["allowed_ips"]
endpoint_allowed_ips = data["endpoint_allowed_ip"] endpoint_allowed_ips = data["endpoint_allowed_ips"]
dns_addresses = data["DNS"] dns_addresses = data["DNS"]
enable_preshared_key = data["enable_preshared_key"] enable_preshared_key = data["enable_preshared_key"]
preshared_key = data["preshared_key"] preshared_key = data["preshared_key"]
@ -398,7 +398,7 @@ def save_peer_setting(interface_name):
private_key = data["private_key"] private_key = data["private_key"]
dns_addresses = data["DNS"] dns_addresses = data["DNS"]
allowed_ips = data["allowed_ips"] allowed_ips = data["allowed_ips"]
endpoint_allowed_ips = data["endpoint_allowed_ip"] endpoint_allowed_ips = data["endpoint_allowed_ips"]
preshared_key = data["preshared_key"] preshared_key = data["preshared_key"]
db_peer = db.get_peer_by_id(interface_name, id) db_peer = db.get_peer_by_id(interface_name, id)
if db_peer: if db_peer:
@ -624,7 +624,7 @@ def add_peer_bulk(interface_name):
data = request.get_json() data = request.get_json()
keys = data["keys"] keys = data["keys"]
endpoint_allowed_ips = data["endpoint_allowed_ip"] endpoint_allowed_ips = data["endpoint_allowed_ips"]
dns_addresses = data["DNS"] dns_addresses = data["DNS"]
enable_preshared_key = data["enable_preshared_key"] enable_preshared_key = data["enable_preshared_key"]
amount = data["amount"] amount = data["amount"]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,9 @@
<!-- configuration.html - < WGDashboard > - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]--> <!-- configuration.html - < WGDashboard > - Copyright(C) 2021 Donald Zou [https://github.com/donaldzou]-->
<html lang="en"> <html lang="en">
{% with title=title%} {% with title=title%}
{% include "header.html"%} {% include "header.html"%}
{% endwith %} {% endwith %}
<body> <body>
<div class="no-response"> <div class="no-response">
<div class="container"> <div class="container">
@ -10,15 +11,15 @@
<h4 class="text-white">Oops!<br>I can't connect to the server.</h4> <h4 class="text-white">Oops!<br>I can't connect to the server.</h4>
</div> </div>
</div> </div>
{% include "navbar.html" %} {% include "navbar.html" %}
<div class="container-fluid" id="right_body"> <div class="container-fluid" id="right_body">
{% include "sidebar.html" %} {% include "sidebar.html" %}
<div class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4"> <div class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" id="search_peer_textbox" placeholder="Search Peer..." value=""> <input type="text" class="form-control" id="search_peer_textbox" placeholder="Search Peer..." value="">
</div> </div>
</div> </div>
<div id="config_body"> <div id="config_body">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4"> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4">
<div class="info mt-4"> <div class="info mt-4">
<div id="config_info_alert"></div> <div id="config_info_alert"></div>
@ -30,7 +31,8 @@
<div class="col"> <div class="col">
<small class="text-muted"><strong>SWITCH</strong></small><br> <small class="text-muted"><strong>SWITCH</strong></small><br>
<div id="conf_status_btn" class="info_loading"></div> <div id="conf_status_btn" class="info_loading"></div>
<div class="spinner-border text-primary" role="status" style="display: none; margin-top: 10px"> <div class="spinner-border text-primary" role="status"
style="display: none; margin-top: 10px">
<span class="sr-only">Loading...</span> <span class="sr-only">Loading...</span>
</div> </div>
</div> </div>
@ -49,7 +51,8 @@
</div> </div>
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"><strong>TOTAL RECEIVED</strong></small> <small class="text-muted"><strong>TOTAL RECEIVED</strong></small>
<h6 style="text-transform: uppercase;" id="conf_total_data_received" class="info_loading"></h6> <h6 style="text-transform: uppercase;" id="conf_total_data_received" class="info_loading">
</h6>
</div> </div>
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"><strong>TOTAL SENT</strong></small> <small class="text-muted"><strong>TOTAL SENT</strong></small>
@ -59,17 +62,20 @@
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"> <small class="text-muted">
<strong>PUBLIC KEY</strong> <strong>PUBLIC KEY</strong>
<strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out" class="text-primary">CLICK TO COPY</strong> <strong style="margin-left: auto!important; opacity: 0; transition: 0.2s ease-in-out"
class="text-primary">CLICK TO COPY</strong>
</small> </small>
<h6 class="info_loading"><samp class="key" id="conf_public_key"></samp></h6> <h6 class="info_loading"><samp class="key" id="conf_public_key"></samp></h6>
</div> </div>
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"><strong>LISTEN PORT</strong></small> <small class="text-muted"><strong>LISTEN PORT</strong></small>
<h6 style="text-transform: uppercase;" class="info_loading"><samp id="conf_listen_port"></samp></h6> <h6 style="text-transform: uppercase;" class="info_loading"><samp
id="conf_listen_port"></samp></h6>
</div> </div>
<div class="col-sm"> <div class="col-sm">
<small class="text-muted"><strong>ADDRESS</strong></small> <small class="text-muted"><strong>ADDRESS</strong></small>
<h6 style="text-transform: uppercase;" class="info_loading"><samp id="conf_address"></samp></h6> <h6 style="text-transform: uppercase;" class="info_loading"><samp id="conf_address"></samp>
</h6>
</div> </div>
</div> </div>
</div> </div>
@ -77,7 +83,7 @@
<div class="button-div mb-3"> <div class="button-div mb-3">
<div class="row"> <div class="row">
<div class="col-sm"> <div class="col-sm">
<div class="form-group"> <div class="form-group">
<label for="sort_by_dropdown"><small class="text-muted">Sort Peers By</small></label> <label for="sort_by_dropdown"><small class="text-muted">Sort Peers By</small></label>
<select class="form-control" id="sort_by_dropdown"> <select class="form-control" id="sort_by_dropdown">
<option value="status">Status</option> <option value="status">Status</option>
@ -90,11 +96,22 @@
<div class="form-group"> <div class="form-group">
<label><small class="text-muted">Refresh Interval</small></label><br> <label><small class="text-muted">Refresh Interval</small></label><br>
<div class="btn-group interval-btn-group" role="group" style="width: 100%"> <div class="btn-group interval-btn-group" role="group" style="width: 100%">
<button style="width: 20%" type="button" class="btn btn-outline-primary btn-group-label refresh" data-toggle="tooltip" data-placement="bottom" title="Refresh Peers"><i class="bi bi-arrow-repeat"></i></button> <button style="width: 20%" type="button"
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="5000">5s</button> class="btn btn-outline-primary btn-group-label refresh" data-toggle="tooltip"
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="10000">10s</button> data-placement="bottom" title="Refresh Peers"><i
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="30000">30s</button> class="bi bi-arrow-repeat"></i></button>
<button style="width: 20%" type="button" class="btn btn-outline-primary update_interval" data-refresh-interval="60000">1m</button> <button style="width: 20%" type="button"
class="btn btn-outline-primary update_interval"
data-refresh-interval="5000">5s</button>
<button style="width: 20%" type="button"
class="btn btn-outline-primary update_interval"
data-refresh-interval="10000">10s</button>
<button style="width: 20%" type="button"
class="btn btn-outline-primary update_interval"
data-refresh-interval="30000">30s</button>
<button style="width: 20%" type="button"
class="btn btn-outline-primary update_interval"
data-refresh-interval="60000">1m</button>
</div> </div>
</div> </div>
</div> </div>
@ -102,17 +119,26 @@
<div class="form-group"> <div class="form-group">
<label><small class="text-muted">Display Mode</small></label><br> <label><small class="text-muted">Display Mode</small></label><br>
<div class="btn-group display-btn-group" role="group" style="width: 100%"> <div class="btn-group display-btn-group" role="group" style="width: 100%">
<button style="width: 20%" type="button" class="btn btn-outline-primary display_mode" data-display-mode="grid"><i class="bi bi-grid-fill" style="font-size: 1.5rem;"></i></button> <button style="width: 20%" type="button"
<button style="width: 20%" type="button" class="btn btn-outline-primary display_mode" data-display-mode="list"><i class="bi bi-list" style="font-size: 1.5rem;"></i></button> class="btn btn-outline-primary display_mode" data-display-mode="grid"><i
class="bi bi-grid-fill" style="font-size: 1.5rem;"></i></button>
<button style="width: 20%" type="button"
class="btn btn-outline-primary display_mode" data-display-mode="list"><i
class="bi bi-list" style="font-size: 1.5rem;"></i></button>
</div> </div>
</div> </div>
</div> </div>
<div class="btn-manage-group"> <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-primary add_btn"><i class="bi bi-plus-circle-fill"
<button type="button" class="btn btn-secondary setting_btn"><i class="bi bi-three-dots"></i></button> 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"> <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-danger" id="delete_peers_by_bulk_btn"><i class="bi bi-trash-fill"></i>
<a class="text-info" id="download_all_peers" data-url="/download_all/{{conf_data['name']}}"><i class="bi bi-cloud-download-fill"></i> Download All Peers</a> Delete Peers</a>
<a class="text-info" id="download_all_peers"
data-url="/download_all/{{conf_data['name']}}"><i
class="bi bi-cloud-download-fill"></i> Download All Peers</a>
</div> </div>
</div> </div>
@ -122,38 +148,42 @@
<small id="peer_loading_time" class="text-muted"></small> <small id="peer_loading_time" class="text-muted"></small>
</main> </main>
</div> </div>
</div> </div>
<div class="modal fade" id="add_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" <div class="modal fade" id="add_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 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 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">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div> <div>
<div class="custom-control custom-switch" style="margin-bottom: 1rem"> <div class="custom-control custom-switch" style="margin-bottom: 1rem">
<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
<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> 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>
<div class="form-group" style="margin: 0"> <div class="form-group" style="margin: 0">
<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 id="bulk_amount_validation" class="invalid-feedback"></div> <div id="bulk_amount_validation" class="invalid-feedback"></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">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<form id="add_peer_form"> <form id="add_peer_form">
<div class="form-group non-bulk"> <div class="form-group non-bulk">
<div> <div>
<label for="private_key">Private Key</label> <label for="private_key">Private Key</label>
@ -161,17 +191,20 @@
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" id="private_key" aria-describedby="private_key"> <input type="text" class="form-control" id="private_key" aria-describedby="private_key">
<div class="input-group-append"> <div class="input-group-append">
<button type="button" class="btn btn-danger" id="re_generate_key" data-toggle="tooltip" data-placement="top" title="Regenerate Key"><i class="bi bi-arrow-repeat"></i></button> <button type="button" class="btn btn-danger" 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>
</div> </div>
<div class="form-group non-bulk"> <div class="form-group non-bulk">
<label for="public_key">Public Key <code>(Required)</code></label> <label for="public_key">Public Key <code>(Required)</code></label>
<input type="text" class="form-control" id="public_key" aria-describedby="public_key" disabled> <input type="text" class="form-control" id="public_key" aria-describedby="public_key"
</div> disabled>
</div>
<div class="row"> <div class="row">
<div class="col-sm-6 non-bulk"> <div class="col-sm-6 non-bulk">
<div class="form-group"> <div class="form-group">
<label for="new_add_name">Name</label> <label for="new_add_name">Name</label>
<input type="text" class="form-control" id="new_add_name"> <input type="text" class="form-control" id="new_add_name">
</div> </div>
@ -182,98 +215,110 @@
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" id="allowed_ips"> <input type="text" class="form-control" id="allowed_ips">
<div class="input-group-append"> <div class="input-group-append">
<button type="button" class="btn btn-primary" id="search_available_ip" data-toggle="tooltip" data-placement="top" title="Search Available IPs"> <button type="button" class="btn btn-primary" id="search_available_ip"
data-toggle="tooltip" data-placement="top" title="Search Available IPs">
<i class="bi bi-search"></i> <i class="bi bi-search"></i>
</button> </button>
</div> </div>
</div> </div>
<p style="position: absolute; top: 4px; right: 1rem;" class="text-success" id="allowed_ips_indicator"></p> <p style="position: absolute; top: 4px; right: 1rem;" class="text-success"
id="allowed_ips_indicator"></p>
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="form-group"> <div class="form-group">
<label for="new_add_DNS">DNS <code>(Required)</code></label> <label for="new_add_DNS">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_DNS" value="{{ DNS }}"> <input type="text" class="form-control" id="new_add_DNS" value="{{ DNS }}">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="form-group"> <div class="form-group">
<label for="new_add_endpoint_allowed_ip">Endpoint Allowed IPs <code>(Required)</code></label> <label for="new_add_endpoint_allowed_ip">Endpoint Allowed IPs
<input type="text" class="form-control" id="new_add_endpoint_allowed_ip" value="{{ endpoint_allowed_ip }}"> <code>(Required)</code></label>
<input type="text" class="form-control" id="new_add_endpoint_allowed_ip"
value="{{ endpoint_allowed_ips }}">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="form-group"> <div class="form-group">
<label for="new_add_MTU">MTU</label> <label for="new_add_MTU">MTU</label>
<input type="text" class="form-control" id="new_add_MTU" value="{{ mtu }}"> <input type="text" class="form-control" id="new_add_MTU" value="{{ mtu }}">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="form-group"> <div class="form-group">
<label for="new_add_keep_alive">Persistent keepalive</label> <label for="new_add_keep_alive">Persistent keepalive</label>
<input type="text" class="form-control" id="new_add_keep_alive" value="{{ keep_alive }}"> <input type="text" class="form-control" id="new_add_keep_alive"
</div> value="{{ keep_alive }}">
</div>
</div> </div>
<div class="col-sm"> <div class="col-sm">
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" id="enable_preshare_key" name="enable_preshare_key" value="enable_psk"> <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> <label class="form-check-label" for="enable_preshare_key">Use Pre-shared Key</label>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <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> <button type="button" class="btn btn-primary" id="save_peer"
</div> conf_id={{conf_data['name']}}>Add</button>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" id="delete_modal" data-backdrop="static" data-keyboard="false" tabindex="-1" </div>
aria-labelledby="staticBackdropLabel" aria-hidden="true"> <div class="modal fade" id="delete_modal" data-backdrop="static" data-keyboard="false" tabindex="-1"
<div class="modal-dialog modal-dialog-centered"> aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-content"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-header"> <div class="modal-content">
<h5 class="modal-title" id="staticBackdropLabel">Are you sure to delete this peer?</h5> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <h5 class="modal-title" id="staticBackdropLabel">Are you sure to delete this peer?</h5>
<span aria-hidden="true">&times;</span> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
</button> <span aria-hidden="true">&times;</span>
</div> </button>
<div class="modal-body"> </div>
<div id="remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert"> <div class="modal-body">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <div id="remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none"
<span aria-hidden="true">&times;</span> role="alert">
</button> <button type="button" class="close" data-dismiss="alert" aria-label="Close">
</div> <span aria-hidden="true">&times;</span>
<h6 style="margin: 0">This action is not reversible.</h6> </button>
</div> </div>
<div class="modal-footer"> <h6 style="margin: 0">This action is not reversible.</h6>
<button type="button" class="btn btn-secondary" data-dismiss="modal">No</button> </div>
<button type="button" class="btn btn-danger" id="delete_peer" conf_id={{conf_data['name']}} peer_id="">Yes</button> <div class="modal-footer">
</div> <button type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
</div> <button type="button" class="btn btn-danger" id="delete_peer" conf_id={{conf_data['name']}}
</div> peer_id="">Yes</button>
</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">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title peer_name"></h5> <h5 class="modal-title peer_name"></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">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="setting_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert"> <div id="setting_peer_alert" class="alert alert-danger alert-dismissible fade show d-none"
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> role="alert">
<span aria-hidden="true">&times;</span> <button type="button" class="close" data-dismiss="alert" aria-label="Close">
</button> <span aria-hidden="true">&times;</span>
</div> </button>
</div>
<div class="mb-3"> <div class="mb-3">
<label for="peer_private_key_textbox" class="form-label">Private Key <code>(Required for QR Code and download)</code></label> <label for="peer_private_key_textbox" class="form-label">Private Key
<input type="password" class="form-control" id="peer_private_key_textbox" style="padding-right: 40px"> <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> <a class="peer_private_key_textbox_switch"><i class="bi bi-eye-fill"></i></a>
</div> </div>
<div> <div>
@ -285,69 +330,72 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<div class="mb-3"> <div class="mb-3">
<label for="peer_name_textbox" class="form-label">Name</label> <label for="peer_name_textbox" class="form-label">Name</label>
<input type="text" class="form-control" id="peer_name_textbox" placeholder=""> <input type="text" class="form-control" id="peer_name_textbox" placeholder="">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="mb-3"> <div class="mb-3">
<label for="peer_allowed_ip_textbox" class="form-label">Allowed IPs <code>(Required)</code></label> <label for="peer_allowed_ip_textbox" class="form-label">Allowed IPs
<input type="text" class="form-control" id="peer_allowed_ip_textbox"> <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_allowed_ip_textbox">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="mb-3"> <div class="mb-3">
<label for="peer_DNS_textbox" class="form-label">DNS <code>(Required)</code></label> <label for="peer_DNS_textbox" class="form-label">DNS <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_DNS_textbox"> <input type="text" class="form-control" id="peer_DNS_textbox">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="mb-3"> <div class="mb-3">
<label for="peer_endpoint_allowed_ips" class="form-label">Endpoint Allowed IPs <code>(Required)</code></label> <label for="peer_endpoint_allowed_ips" class="form-label">Endpoint Allowed IPs
<input type="text" class="form-control" id="peer_endpoint_allowed_ips"> <code>(Required)</code></label>
<input type="text" class="form-control" id="peer_endpoint_allowed_ips">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="mb-3"> <div class="mb-3">
<label for="peer_mtu" class="form-label">MTU</label> <label for="peer_mtu" class="form-label">MTU</label>
<input type="text" class="form-control" id="peer_mtu"> <input type="text" class="form-control" id="peer_mtu">
</div> </div>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="mb-3"> <div class="mb-3">
<label for="peer_keep_alive" class="form-label">Persistent Keepalive</label> <label for="peer_keep_alive" class="form-label">Persistent Keepalive</label>
<input type="text" class="form-control" id="peer_keep_alive"> <input type="text" class="form-control" id="peer_keep_alive">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <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> <button type="button" class="btn btn-primary" id="save_peer_setting" conf_id={{conf_data['name']}}
</div> peer_id="">Save</button>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="modal fade" id="available_ip_modal" data-backdrop="static" data-keyboard="false"> <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">
<h5 class="modal-title" id="staticBackdropLabel">Select available IP</h5> <h5 class="modal-title" id="staticBackdropLabel">Select available IP</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">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="selected_ip" style="padding: 1rem; border-bottom: 1px solid #dee2e6;"> <div class="selected_ip" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED IP (CLICK TO REMOVE)</strong></small> <small class="text-muted"><strong>SELECTED IP (CLICK TO REMOVE)</strong></small>
<div id="selected_ip_list"></div> <div id="selected_ip_list"></div>
</div> </div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;"> <div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div> <div class="list-group"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirm_ip">Confirm</button> <button type="button" class="btn btn-primary" id="confirm_ip">Confirm</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -355,57 +403,60 @@
<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">
<h5 class="modal-title" id="staticBackdropLabel">Select Peers to Delete</h5> <h5 class="modal-title" id="staticBackdropLabel">Select Peers to Delete</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">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div id="bulk_remove_peer_alert" class="alert alert-danger alert-dismissible fade show d-none" role="alert" style="margin: 1rem"> <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"> <button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="selected_peers" style="padding: 1rem; border-bottom: 1px solid #dee2e6;"> <div class="selected_peers" style="padding: 1rem; border-bottom: 1px solid #dee2e6;">
<small class="text-muted"><strong>SELECTED PEERS (CLICK TO REMOVE)</strong></small> <small class="text-muted"><strong>SELECTED PEERS (CLICK TO REMOVE)</strong></small>
<div id="selected_peer_list"></div> <div id="selected_peer_list"></div>
</div> </div>
<div class="modal-body" style="max-height: 400px; overflow-y: scroll;"> <div class="modal-body" style="max-height: 400px; overflow-y: scroll;">
<div class="list-group"></div> <div class="list-group"></div>
</div> </div>
<div class="modal-footer"> <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> <a class="text-danger" id="select_all_delete_bulk_peers"
<button type="button" class="btn btn-danger" id="confirm_delete_bulk_peers" disabled data-conf="{{conf_data['name']}}">Delete</button> style="cursor: pointer; margin-right: auto;"><small><strong>SELECT ALL</strong></small></a>
</div> <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>
</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">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">QR Code</h5> <h5 class="modal-title">QR Code</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">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<img id="qrcode_img" style="width: 100%"> <img id="qrcode_img" style="width: 100%">
</div> </div>
</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">
<strong class="mr-auto">WGDashboard</strong> <strong class="mr-auto">WGDashboard</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
</div> </div>
</div> </div>
</div> </div>
{% include "tools.html" %} {% include "tools.html" %}
</body> </body>
@ -418,9 +469,10 @@
let load_interval = 0; let load_interval = 0;
let conf_name = "{{ conf_data['name'] }}" let conf_name = "{{ conf_data['name'] }}"
let peers = []; let peers = [];
$(".sb-"+conf_name+"-url").addClass("active"); $(".sb-" + conf_name + "-url").addClass("active");
$(function(){ $(function () {
configurations.loadPeers($('#search_peer_textbox').val()); configurations.loadPeers($('#search_peer_textbox').val());
}); });
</script> </script>
</html> </html>

View File

@ -1,22 +1,23 @@
<html> <html>
{% with %} {% with %}
{% set title="Settings" %} {% set title="Settings" %}
{% include "header.html" %} {% include "header.html" %}
{% endwith %} {% endwith %}
<body> <body>
{% include "navbar.html" %} {% include "navbar.html" %}
<div class="container-fluid"> <div class="container-fluid">
{% include "sidebar.html" %} {% include "sidebar.html" %}
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4"> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="setting-container mt-4"> <div class="setting-container mt-4">
{% if message != "" %} {% if message != "" %}
<div class="alert alert-{{ status }}" role="alert"> <div class="alert alert-{{ status }}" role="alert">
{{ message }} {{ message }}
</div> </div>
{% endif %} {% endif %}
<h1 class="">Settings</h1> <h1 class="">Settings</h1>
<hr> <hr>
{% if required_auth == "true" %} {% if required_auth == "true" %}
<div class="card mb-3"> <div class="card mb-3">
<h6 class="card-header">Peer Default Settings</h6> <h6 class="card-header">Peer Default Settings</h6>
<div class="card-body"> <div class="card-body">
@ -26,32 +27,30 @@
<div class="col-sm-6"> <div class="col-sm-6">
<label for="peer_global_DNS">DNS</label> <label for="peer_global_DNS">DNS</label>
<input type="text" class="form-control mb-4" id="peer_global_DNS" <input type="text" class="form-control mb-4" id="peer_global_DNS"
name="peer_global_DNS" name="peer_global_DNS" value="{{ peer_global_DNS }}" required>
value="{{ peer_global_DNS }}" required>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="peer_endpoint_allowed_ip">Peer Endpoint Allowed IPs</label> <label for="peer_endpoint_allowed_ips">Peer Endpoint Allowed IPs</label>
<input type="text" class="form-control mb-4" id="peer_endpoint_allowed_ip" <input type="text" class="form-control mb-4" id="peer_endpoint_allowed_ip"
name="peer_endpoint_allowed_ip" name="peer_endpoint_allowed_ips" value="{{ peer_endpoint_allowed_ips }}"
value="{{ peer_endpoint_allowed_ip }}" required> required>
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="peer_mtu">MTU</label> <label for="peer_mtu">MTU</label>
<input type="text" class="form-control mb-4" id="peer_mtu" <input type="text" class="form-control mb-4" id="peer_mtu" name="peer_mtu"
name="peer_mtu" value="{{ peer_mtu }}">
value="{{ peer_mtu }}">
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<label for="peer_keep_alive">Persistent Keepalive</label> <label for="peer_keep_alive">Persistent Keepalive</label>
<input type="text" class="form-control mb-4" id="peer_keep_alive" <input type="text" class="form-control mb-4" id="peer_keep_alive"
name="peer_keep_alive" name="peer_keep_alive" value="{{ peer_keepalive }}">
value="{{ peer_keepalive }}">
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<label for="peer_remote_endpoint"><strong>Peer Remote Endpoint (This will be change globally, and will be apply to all peer's QR code and configuration file.)</strong></label> <label for="peer_remote_endpoint"><strong>Peer Remote Endpoint (This will be
change globally, and will be apply to all peer's QR code and
configuration file.)</strong></label>
<input type="text" class="form-control mb-4" id="peer_remote_endpoint" <input type="text" class="form-control mb-4" id="peer_remote_endpoint"
name="peer_remote_endpoint" name="peer_remote_endpoint" value="{{ peer_remote_endpoint }}" required>
value="{{ peer_remote_endpoint }}" required>
</div> </div>
</div> </div>
<button class="btn btn-success" type="submit">Update Peer Default Settings</button> <button class="btn btn-success" type="submit">Update Peer Default Settings</button>
@ -67,8 +66,9 @@
<div class="form-group"> <div class="form-group">
<label for="wg_conf_path">Path</label> <label for="wg_conf_path">Path</label>
<input type="text" class="form-control mb-2" id="wg_conf_path" name="wg_conf_path" <input type="text" class="form-control mb-2" id="wg_conf_path" name="wg_conf_path"
value="{{ wg_conf_path }}"> value="{{ wg_conf_path }}">
<p class="text-muted">Remember to remove <code>/</code> at the end of your path. e.g <code>/etc/wireguard</code> <p class="text-muted">Remember to remove <code>/</code> at the end of your path. e.g
<code>/etc/wireguard</code>
</p> </p>
<button class="btn btn-danger change_path">Update Path & Restart Dashboard</button> <button class="btn btn-danger change_path">Update Path & Restart Dashboard</button>
</div> </div>
@ -83,7 +83,7 @@
<div class="form-group"> <div class="form-group">
<label for="username">Username</label> <label for="username">Username</label>
<input type="text" class="form-control mb-4" id="username" name="username" <input type="text" class="form-control mb-4" id="username" name="username"
value="{{ session['username'] }}" required> value="{{ session['username'] }}" required>
<button type="submit" class="btn btn-danger">Update Account</button> <button type="submit" class="btn btn-danger">Update Account</button>
</div> </div>
</form> </form>
@ -106,61 +106,65 @@
</form> </form>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="card"> <div class="card">
<h6 class="card-header">Dashboard Configuration</h6> <h6 class="card-header">Dashboard Configuration</h6>
<div class="card-body"> <div class="card-body">
<form action="/update_app_ip_port" method="post" class="update_app_ip_port"> <form action="/update_app_ip_port" method="post" class="update_app_ip_port">
<div class="form-group"> <div class="form-group">
<div class="row"> <div class="row">
<div class="col-sm"> <div class="col-sm">
<label for="app_ip">Dashboard IP</label> <label for="app_ip">Dashboard IP</label>
<input type="text" class="form-control mb-2" id="app_ip" name="app_ip" value="{{ app_ip }}"> <input type="text" class="form-control mb-2" id="app_ip" name="app_ip"
<p><small class="text-danger mb-4">0.0.0.0 means it can be access by anyone with your server value="{{ app_ip }}">
IP Address.</small></p> <p><small class="text-danger mb-4">0.0.0.0 means it can be access by anyone with
your server
IP Address.</small></p>
</div>
<div class="col-sm">
<label for="app_port">Dashboard Port</label>
<input type="text" class="form-control mb-4" id="app_port" name="app_port"
value="{{ app_port }}">
</div>
</div> </div>
<div class="col-sm"> <button type="button" class="btn btn-danger confirm_modal" data-toggle="modal"
<label for="app_port">Dashboard Port</label>
<input type="text" class="form-control mb-4" id="app_port" name="app_port"
value="{{ app_port }}">
</div>
</div>
<button type="button" class="btn btn-danger confirm_modal" data-toggle="modal"
data-target="#confirmModal">Update Configuration & Restart Dashboard data-target="#confirmModal">Update Configuration & Restart Dashboard
</button> </button>
</div> </div>
</form> </form>
</div>
</div> </div>
</div> </div>
</div> </main>
</main> <!-- Modal -->
<!-- Modal --> <div class="modal fade" id="confirmModal" data-backdrop="static" data-keyboard="false" tabindex="-1"
<div class="modal fade" id="confirmModal" data-backdrop="static" data-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
aria-labelledby="staticBackdropLabel" aria-hidden="true"> <div class="modal-dialog">
<div class="modal-dialog"> <div class="modal-content">
<div class="modal-content"> <div class="modal-header">
<div class="modal-header"> <h5 class="modal-title" id="staticBackdropLabel">Confirm Dashboard Configuration</h5>
<h5 class="modal-title" id="staticBackdropLabel">Confirm Dashboard Configuration</h5> </div>
</div> <div class="modal-body">
<div class="modal-body"> <small>Dashboard Original IP</small>
<small>Dashboard Original IP</small> <p>{{ app_ip }}</p>
<p>{{ app_ip }}</p> <small style="font-weight: bold" class="text-bold">Dashboard New IP</small>
<small style="font-weight: bold" class="text-bold">Dashboard New IP</small> <p class="app_new_ip text-bold text-danger" style="font-weight: bold"></p>
<p class="app_new_ip text-bold text-danger" style="font-weight: bold"></p> <small>Dashboard Original Port</small>
<small>Dashboard Original Port</small> <p>{{ app_port }}</p>
<p>{{ app_port }}</p> <small style="font-weight: bold" class="text-bold">Dashboard New Port</small>
<small style="font-weight: bold" class="text-bold">Dashboard New Port</small> <p class="app_new_port text-bold text-danger" style="font-weight: bold"></p>
<p class="app_new_port text-bold text-danger" style="font-weight: bold"></p> </div>
</div> <div class="modal-footer">
<div class="modal-footer"> <button type="button" class="btn btn-secondary cancel_restart"
<button type="button" class="btn btn-secondary cancel_restart" data-dismiss="modal">Cancel</button> data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger confirm_restart">Confirm & Restart Dashboard</button> <button type="button" class="btn btn-danger confirm_restart">Confirm & Restart
Dashboard</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> {% include "tools.html" %}
{% include "tools.html" %}
</body> </body>
{% include "footer.html" %} {% include "footer.html" %}
@ -201,4 +205,5 @@
}); });
</script> </script>
</html> </html>

View File

@ -14,16 +14,21 @@ Helper Functions
""" """
def adapt_for_rest(x): def get_interface_file_path(interface_name: str, base_dir: str) -> str:
x["allowed_ip"] = x.pop("allowed_ips") return os.path.join(base_dir, f"{interface_name}.conf")
x["endpoint_allowed_ip"] = x.pop("endpoint_allowed_ips")
for i in ["latest_handshake", "endpoint", "allowed_ip"]:
if not x[i]:
x[i] = "N/A"
return x
def connect_db(dashboard_configuration_dir): def adapt_for_rest(peer_dict: dict) -> dict:
"""
Renames necessary entries and replaces falsy values. Returns the modified dict.
"""
for i in ["latest_handshake", "endpoint", "allowed_ips"]:
if not peer_dict[i]:
peer_dict[i] = "(None)"
return peer_dict
def connect_db(dashboard_configuration_dir: str):
""" """
Connect to the database Connect to the database
@return: sqlite3.Connection @return: sqlite3.Connection
@ -35,7 +40,7 @@ def connect_db(dashboard_configuration_dir):
return con return con
def read_dashboard_conf(dashboard_conf_file_path): def read_dashboard_conf(dashboard_conf_file_path: str):
""" """
Get dashboard configuration Get dashboard configuration
@return: configparser.ConfigParser @return: configparser.ConfigParser
@ -45,7 +50,7 @@ def read_dashboard_conf(dashboard_conf_file_path):
return r_config return r_config
def write_dashboard_conf(config, dashboard_conf_file_path): def write_dashboard_conf(config: str, dashboard_conf_file_path: str):
""" """
Write to configuration Write to configuration
@param config: Input configuration @param config: Input configuration
@ -54,7 +59,9 @@ def write_dashboard_conf(config, dashboard_conf_file_path):
config.write(conf_object) config.write(conf_object)
def wg_peer_data_to_db(interface_name, wg_conf_dir, dashboard_conf_file_path): def wg_peer_data_to_db(
interface_name: str, wg_conf_dir: str, dashboard_conf_file_path: str
):
""" """
Look for new peers from WireGuard Look for new peers from WireGuard
@param interface_name: Configuration name @param interface_name: Configuration name
@ -110,9 +117,7 @@ def wg_peer_data_to_db(interface_name, wg_conf_dir, dashboard_conf_file_path):
handshakes = wg.get_interface_peers_latest_handshakes(interface_name) handshakes = wg.get_interface_peers_latest_handshakes(interface_name)
transfers = wg.get_interface_peers_net_stats(interface_name) transfers = wg.get_interface_peers_net_stats(interface_name)
endpoints = wg.get_interface_peers_endpoints(interface_name) endpoints = wg.get_interface_peers_endpoints(interface_name)
allowed_ips = wg.get_interface_peers_allowed_ips( allowed_ips = wg.get_interface_peers_allowed_ips(interface_and_peer_data)
interface_and_peer_data, interface_name
)
keys = set() keys = set()
for x in [handshakes, transfers, endpoints, allowed_ips]: for x in [handshakes, transfers, endpoints, allowed_ips]:
keys.update(x.keys()) keys.update(x.keys())
@ -127,7 +132,11 @@ def wg_peer_data_to_db(interface_name, wg_conf_dir, dashboard_conf_file_path):
def update_db_and_get_peers( def update_db_and_get_peers(
interface_name, search, sort_t, wg_conf_dir, dashboard_conf_file_path interface_name: str,
search: str,
sort_t: str,
wg_conf_dir: str,
dashboard_conf_file_path: str,
): ):
""" """
Get all peers. Get all peers.
@ -162,7 +171,7 @@ def update_db_and_get_peers(
return result return result
def get_conf_list(wg_conf_dir): def get_conf_list(wg_conf_dir: str):
"""Get all wireguard interfaces with status. """Get all wireguard interfaces with status.
@return: Return a list of dicts with interfaces and its statuses @return: Return a list of dicts with interfaces and its statuses
@rtype: list @rtype: list
@ -188,7 +197,7 @@ def get_conf_list(wg_conf_dir):
return conf return conf
def f_check_key_match(private_key, public_key, interface_name): def f_check_key_match(private_key: str, public_key: str, interface_name: str):
""" """
Check if private key and public key match Check if private key and public key match
@param private_key: Private key @param private_key: Private key
@ -216,7 +225,7 @@ def f_check_key_match(private_key, public_key, interface_name):
return {"status": "success"} return {"status": "success"}
def check_repeat_allowed_ips(public_key, ip, interface_name): def check_repeat_allowed_ips(public_key: str, ip: str, interface_name: str):
""" """
Check if there are repeated IPs Check if there are repeated IPs
@param public_key: Public key of the peer @param public_key: Public key of the peer
@ -238,7 +247,7 @@ def check_repeat_allowed_ips(public_key, ip, interface_name):
return {"status": "success"} return {"status": "success"}
def f_available_ips(interface_name, wg_conf_dir): def f_available_ips(interface_name: str, wg_conf_dir: str):
""" """
Get a list of available IPs Get a list of available IPs
@param interface_name: Configuration Name @param interface_name: Configuration Name
@ -289,7 +298,7 @@ def ensure_subnet(ipv4: str, default_subnet: str = "24") -> str:
# Regex Match # Regex Match
def regex_match(regex, text): def regex_match(regex: str, text: str):
pattern = re.compile(regex) pattern = re.compile(regex)
return pattern.search(text) is not None return pattern.search(text) is not None

View File

@ -7,15 +7,21 @@ import db
import os import os
import ifcfg import ifcfg
import re import re
import util
def _is_comment_line(line: str) -> bool: def _is_comment_line(line: str) -> bool:
"""Returns true if the passed string is a comment""" """
Returns true if the passed string is a comment in an INI style file.
"""
line = line.strip() line = line.strip()
return line.startswith("#") or line.startswith(";") return line.startswith("#") or line.startswith(";")
def _parse_peer_or_interface(lines, i, limit): def _parse_peer_or_interface(lines: list[str], i: int, limit: int) -> dict:
"""
Parses a [Peer] or [Interface] section of an interface config file into a `dict`.
"""
data = {} data = {}
while i < limit: while i < limit:
line = lines[i].strip() line = lines[i].strip()
@ -30,23 +36,32 @@ def _parse_peer_or_interface(lines, i, limit):
def set_peer_options( def set_peer_options(
interface_name, public_key, allowed_ips, preshared_key_filename=None interface_name: str,
public_key: str,
allowed_ips: str,
preshared_key_filename: str = None,
): ):
"""
Sets the given options for the specified `interface_name` and peer (`public_key`)
"""
if preshared_key_filename: if preshared_key_filename:
status = subprocess.check_output( subprocess.check_output(
f"wg set {interface_name} peer {public_key} allowed-ips {allowed_ips} preshared-key {preshared_key_filename}", f"wg set {interface_name} peer {public_key} allowed-ips {allowed_ips} preshared-key {preshared_key_filename}",
shell=True, shell=True,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
else: else:
status = subprocess.check_output( subprocess.check_output(
f"wg set {interface_name} peer {public_key} allowed-ips {allowed_ips}", f"wg set {interface_name} peer {public_key} allowed-ips {allowed_ips}",
shell=True, shell=True,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
def remove_peer_from_interface(interface_name, public_key): def remove_peer_from_interface(interface_name: str, public_key: str):
"""
Removes the specified peer (`public_key`) from `interface_name`
"""
subprocess.check_output( subprocess.check_output(
f"wg set {interface_name} peer {public_key} remove", f"wg set {interface_name} peer {public_key} remove",
shell=True, shell=True,
@ -54,7 +69,7 @@ def remove_peer_from_interface(interface_name, public_key):
) )
def get_interface_listen_port(interface_name, base_dir): def get_interface_listen_port(interface_name: str, base_dir: str) -> str:
""" """
Get listen port number. Get listen port number.
@param interface_name: Name of WG interface @param interface_name: Name of WG interface
@ -80,7 +95,7 @@ def get_interface_listen_port(interface_name, base_dir):
return port return port
def get_interface_public_key(interface_name, base_dir): def get_interface_public_key(interface_name: str, base_dir: str) -> str:
""" """
Get public key for configuration. Get public key for configuration.
@param interface_name: Name of WG interface @param interface_name: Name of WG interface
@ -122,7 +137,7 @@ def get_interface_total_net_stats(interface_name):
return [total, upload_total, download_total] return [total, upload_total, download_total]
def get_interface_status(interface_name): def get_interface_status(interface_name: str) -> str:
""" """
Check if the configuration is running or not Check if the configuration is running or not
@param interface_name: @param interface_name:
@ -206,7 +221,7 @@ def read_interface_config_file(interface_name: str, base_dir: str) -> dict:
""" """
app.logger.debug(f"read_conf_file({interface_name})") app.logger.debug(f"read_conf_file({interface_name})")
config_file = os.path.join(base_dir, f"{interface_name}.conf") config_file = util.get_interface_file_path(interface_name, base_dir)
with open(config_file, "r", encoding="utf-8") as file_object: with open(config_file, "r", encoding="utf-8") as file_object:
file = list(file_object.readlines()) file = list(file_object.readlines())
result = {"Interface": {}, "Peers": []} result = {"Interface": {}, "Peers": []}
@ -229,7 +244,10 @@ def read_interface_config_file(interface_name: str, base_dir: str) -> dict:
def switch_interface(interface_name: str, base_dir: str): def switch_interface(interface_name: str, base_dir: str):
config_file = os.path.join(base_dir, f"{interface_name}.conf") """
Switches `interface_name` around (up if it is down, down if it is up)
"""
config_file = util.get_interface_file_path(interface_name, base_dir)
status = get_interface_status(interface_name) status = get_interface_status(interface_name)
if status == "running": if status == "running":
check = subprocess.check_output( check = subprocess.check_output(
@ -242,14 +260,20 @@ def switch_interface(interface_name: str, base_dir: str):
def enable_interface(interface_name: str, base_dir: str): def enable_interface(interface_name: str, base_dir: str):
config_file = os.path.join(base_dir, f"{interface_name}.conf") """
Brings `interface_name` up (enables it)
"""
config_file = util.get_interface_file_path(interface_name, base_dir)
check = subprocess.check_output( check = subprocess.check_output(
"wg-quick up " + config_file, shell=True, stderr=subprocess.STDOUT "wg-quick up " + config_file, shell=True, stderr=subprocess.STDOUT
) )
def disable_interface(interface_name: str, base_dir: str): def disable_interface(interface_name: str, base_dir: str):
config_file = os.path.join(base_dir, f"{interface_name}.conf") """
Brings `interface_name` down (disables it)
"""
config_file = util.get_interface_file_path(interface_name, base_dir)
check = subprocess.check_output( check = subprocess.check_output(
"wg-quick down " + config_file, shell=True, stderr=subprocess.STDOUT "wg-quick down " + config_file, shell=True, stderr=subprocess.STDOUT
) )
@ -297,7 +321,7 @@ def get_interface_peers_latest_handshakes(interface_name) -> dict:
return result return result
def get_interface_peers_net_stats(interface_name) -> dict: def get_interface_peers_net_stats(interface_name: str) -> dict:
""" """
Get transfer from all peers of a configuration Get transfer from all peers of a configuration
@param interface_name: Configuration name @param interface_name: Configuration name
@ -354,14 +378,17 @@ def get_interface_peers_net_stats(interface_name) -> dict:
return result return result
def quick_save_interface_config(interface_name, base_dir): def quick_save_interface_config(interface_name: str, base_dir: str):
config_file = os.path.join(base_dir, f"{interface_name}.conf") """
Executes `wg-quick save` for `interface_name`
"""
config_file = util.get_interface_file_path(interface_name, base_dir)
status = subprocess.check_output( status = subprocess.check_output(
"wg-quick save " + config_file, shell=True, stderr=subprocess.STDOUT "wg-quick save " + config_file, shell=True, stderr=subprocess.STDOUT
) )
def gen_public_key(private_key): def gen_public_key(private_key: str):
"""Generate the public key. """Generate the public key.
@param private_key: Private key @param private_key: Private key
@ -390,7 +417,7 @@ def gen_public_key(private_key):
} }
def get_interface_peers_endpoints(interface_name) -> dict: def get_interface_peers_endpoints(interface_name: str) -> dict:
""" """
Get endpoint from all peers of a configuration Get endpoint from all peers of a configuration
@param interface_name: Configuration name @param interface_name: Configuration name
@ -414,7 +441,7 @@ def get_interface_peers_endpoints(interface_name) -> dict:
return result return result
def get_interface_peers_allowed_ips(conf_peer_data, interface_name) -> dict: def get_interface_peers_allowed_ips(conf_peer_data: str) -> dict:
""" """
Get allowed ips from all peers of a configuration Get allowed ips from all peers of a configuration
@param conf_peer_data: Configuration peer data @param conf_peer_data: Configuration peer data