mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 07:10:09 +01:00
Finalizing everything.
This commit is contained in:
parent
e2e8222005
commit
258564208b
15
README.md
15
README.md
@ -18,16 +18,19 @@
|
||||
|
||||
- 🎉 **New Features**
|
||||
- **Moved from TinyDB to SQLite**: SQLite provide a better performance and loading speed when getting peers! Also avoided crashing the database due to **race condition**.
|
||||
- **Added Gunicorn WSGI Server**: This could provide more stable on handling HTTP request, and more flexibility in the future (such as HTTPS support). **BIG THANKS to @pgalonza :heart: **
|
||||
- **Add Peers by Bulk: ** User can add peers by bulk, just simply set the amount and click add.
|
||||
- **Delete Peers by Bulk**: User can delete peers by bulk, without deleting peers one by one.
|
||||
- **Download Peers in Zip**: User can download all *downloadable* peers in a zip.
|
||||
- **Added Pre-shared Key to peers:** Now each peer can add with a pre-shared key to enhance security. Previously added peers can add the pre-shared key through the peer setting button.
|
||||
- **Redirect Back to Previous Page:** The dashboard will now redirect you back to your previous page if the current session got timed out and you need to sign in again.
|
||||
|
||||
- 🪚 **Bug Fixed**
|
||||
- [IP Sorting range issues #99](https://github.com/donaldzou/WGDashboard/issues/99) [❤️ @barryboom]
|
||||
- [INvalid character written to tunnel json file #108](https://github.com/donaldzou/WGDashboard/issues/108) [❤️ @ ikidd]
|
||||
- [Add IPv6 #91](https://github.com/donaldzou/WGDashboard/pull/91) [❤️ @ pgalonza]
|
||||
- [Added MTU and PersistentKeepalive to QR code and download files #112](https://github.com/donaldzou/WGDashboard/pull/112) [:heart: @reafian]
|
||||
- **And many other bugs provided by our beloved users** :heart:
|
||||
- **🧐 Other Changes**
|
||||
- **Key generating moved to front-end**: No longer need to use the server's WireGuard to generate keys, thanks to the `wireguard.js` from the [official repository](https://git.zx2c4.com/wireguard-tools/tree/contrib/keygen-html/wireguard.js)!
|
||||
- **Peer transfer calculation**: each peer will now show all transfer amount (previously was only showing transfer amount from the last configuration start-up).
|
||||
@ -35,12 +38,15 @@
|
||||
- **`wgd.sh` finally can update itself**: So now user could update the whole dashboard from `wgd.sh`, with the `update` command.
|
||||
- **Minified JS and CSS files**: Although only a small changes on the file size, but I think is still a good practice to save a bit of bandwidth ;)
|
||||
|
||||
|
||||
*And many other small changes for performance and bug fixes! :laughing:*
|
||||
|
||||
> If you have any other brilliant ideas for this project, please shout it in here [#129](https://github.com/donaldzou/WGDashboard/issues/129) :heart:
|
||||
|
||||
<hr>
|
||||
|
||||
## Table of Content
|
||||
|
||||
|
||||
- [💡 Features](#-features)
|
||||
- [📝 Requirement](#-requirement)
|
||||
- [🛠 Install](#-install)
|
||||
@ -332,7 +338,14 @@ Endpoint = 0.0.0.0:51820
|
||||
|
||||
## ❓ How to update the dashboard?
|
||||
|
||||
#### **Please note for user who is using `v2.3.1` or below**
|
||||
|
||||
- For user who is using `v2.3.1` or below, please notice that all data that stored in the current database will **not** transfer to the new database. This is hard decision to move from TinyDB to SQLite. But SQLite does provide a thread-safe access and TinyDB doesn't. I couldn't find a safe way to transfer the data, so you need to do them manually... Sorry about that :pensive: . But I guess this would be a great start for future development :sunglasses:.
|
||||
|
||||
<hr>
|
||||
|
||||
1. Change your directory to `wgdashboard`
|
||||
|
||||
```shell
|
||||
cd wgdashboard
|
||||
```
|
||||
|
366
src/dashboard.py
366
src/dashboard.py
@ -17,6 +17,7 @@ import time
|
||||
import re
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from datetime import datetime, timedelta
|
||||
from operator import itemgetter
|
||||
# PIP installed library
|
||||
@ -65,7 +66,6 @@ def get_dashboard_conf():
|
||||
Get dashboard configuration
|
||||
@return: configparser.ConfigParser
|
||||
"""
|
||||
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(DASHBOARD_CONF)
|
||||
return config
|
||||
@ -76,7 +76,6 @@ def set_dashboard_conf(config):
|
||||
Write to configuration
|
||||
@param config: Input configuration
|
||||
"""
|
||||
|
||||
with open(DASHBOARD_CONF, "w", encoding='utf-8') as conf_object:
|
||||
config.write(conf_object)
|
||||
|
||||
@ -129,7 +128,6 @@ def get_conf_running_peer_number(config_name):
|
||||
return running
|
||||
|
||||
|
||||
# TODO use modules for working with ini(configparser or wireguard)
|
||||
# Read [Interface] section from configuration file
|
||||
def read_conf_file_interface(config_name):
|
||||
"""
|
||||
@ -154,10 +152,6 @@ def read_conf_file_interface(config_name):
|
||||
return data
|
||||
|
||||
|
||||
# TODO use modules for working with ini(configparser or wireguard)
|
||||
# Tried to use configparser but it does not support sections with the same name
|
||||
|
||||
|
||||
def read_conf_file(config_name):
|
||||
"""
|
||||
Get configurations from file of wireguard interface.
|
||||
@ -375,7 +369,6 @@ def get_all_peers_data(config_name):
|
||||
get_allowed_ip(conf_peer_data, config_name)
|
||||
|
||||
|
||||
# Search for peers
|
||||
def get_peers(config_name, search, sort_t):
|
||||
"""
|
||||
Get all peers.
|
||||
@ -428,7 +421,6 @@ def get_conf_pub_key(config_name):
|
||||
return ""
|
||||
|
||||
|
||||
# Get configuration listen port
|
||||
def get_conf_listen_port(config_name):
|
||||
"""
|
||||
Get listen port number.
|
||||
@ -452,8 +444,12 @@ def get_conf_listen_port(config_name):
|
||||
return port
|
||||
|
||||
|
||||
# Get configuration total data
|
||||
def get_conf_total_data(config_name):
|
||||
"""
|
||||
Get configuration's total amount of data
|
||||
@param config_name: Configuration name
|
||||
@return: list
|
||||
"""
|
||||
data = g.cur.execute("SELECT total_sent, total_receive, cumu_sent, cumu_receive FROM " + config_name)
|
||||
upload_total = 0
|
||||
download_total = 0
|
||||
@ -468,19 +464,21 @@ def get_conf_total_data(config_name):
|
||||
return [total, upload_total, download_total]
|
||||
|
||||
|
||||
# Get configuration status
|
||||
def get_conf_status(config_name):
|
||||
"""
|
||||
Check if the configuration is running or not
|
||||
@param config_name:
|
||||
@return: Return a string indicate the running status
|
||||
"""
|
||||
ifconfig = dict(ifcfg.interfaces().items())
|
||||
|
||||
return "running" if config_name in ifconfig.keys() else "stopped"
|
||||
|
||||
|
||||
# Get all configuration as a list
|
||||
def get_conf_list():
|
||||
"""Get all wireguard interfaces with status.
|
||||
|
||||
:return: Return a list of dicts with interfaces and its statuses
|
||||
:rtype: list
|
||||
@return: Return a list of dicts with interfaces and its statuses
|
||||
@rtype: list
|
||||
"""
|
||||
|
||||
conf = []
|
||||
@ -510,25 +508,13 @@ def get_conf_list():
|
||||
return conf
|
||||
|
||||
|
||||
# Generate private key
|
||||
def gen_private_key():
|
||||
subprocess.run('wg genkey > private_key.txt && wg pubkey < private_key.txt > public_key.txt', shell=True)
|
||||
with open('private_key.txt', encoding='utf-8') as file_object:
|
||||
private_key = file_object.readline().strip()
|
||||
with open('public_key.txt', encoding='utf-8') as file_object:
|
||||
public_key = file_object.readline().strip()
|
||||
data = {"private_key": private_key, "public_key": public_key}
|
||||
return data
|
||||
|
||||
|
||||
# Generate public key
|
||||
def gen_public_key(private_key):
|
||||
"""Generate the public key.
|
||||
|
||||
:param private_key: Pricate key
|
||||
:type private_key: str
|
||||
:return: Return dict with public key or error message
|
||||
:rtype: dict
|
||||
@param private_key: Private key
|
||||
@type private_key: str
|
||||
@return: Return dict with public key or error message
|
||||
@rtype: dict
|
||||
"""
|
||||
|
||||
with open('private_key.txt', 'w', encoding='utf-8') as file_object:
|
||||
@ -570,8 +556,14 @@ def f_check_key_match(private_key, public_key, config_name):
|
||||
return {'status': 'success'}
|
||||
|
||||
|
||||
# Check if there is repeated allowed IP
|
||||
def check_repeat_allowed_ip(public_key, ip, config_name):
|
||||
"""
|
||||
Check if there are repeated IPs
|
||||
@param public_key: Public key of the peer
|
||||
@param ip: IP of the peer
|
||||
@param config_name: configuration name
|
||||
@return: a JSON object
|
||||
"""
|
||||
peer = g.cur.execute("SELECT COUNT(*) FROM " + config_name + " WHERE id = ?", (public_key,)).fetchone()
|
||||
if peer[0] != 1:
|
||||
return {'status': 'failed', 'msg': 'Peer does not exist'}
|
||||
@ -586,6 +578,11 @@ def check_repeat_allowed_ip(public_key, ip, config_name):
|
||||
|
||||
|
||||
def f_available_ips(config_name):
|
||||
"""
|
||||
Get a list of available IPs
|
||||
@param config_name: Configuration Name
|
||||
@return: list
|
||||
"""
|
||||
config_interface = read_conf_file_interface(config_name)
|
||||
if "Address" in config_interface:
|
||||
existed = []
|
||||
@ -596,7 +593,6 @@ def f_available_ips(config_name):
|
||||
existed.append(ipaddress.ip_address(add))
|
||||
peers = g.cur.execute("SELECT allowed_ip FROM " + config_name).fetchall()
|
||||
for i in peers:
|
||||
print(i[0])
|
||||
add = i[0].split(",")
|
||||
for k in add:
|
||||
a, s = k.split("/")
|
||||
@ -620,6 +616,11 @@ Flask Functions
|
||||
|
||||
@app.teardown_request
|
||||
def close_DB(exception):
|
||||
"""
|
||||
Commit to the database for every request
|
||||
@param exception: Exception
|
||||
@return: None
|
||||
"""
|
||||
if hasattr(g, 'db'):
|
||||
g.db.commit()
|
||||
g.db.close()
|
||||
@ -628,6 +629,10 @@ def close_DB(exception):
|
||||
# Before request
|
||||
@app.before_request
|
||||
def auth_req():
|
||||
"""
|
||||
Action before every request
|
||||
@return: Redirect
|
||||
"""
|
||||
if getattr(g, 'db', None) is None:
|
||||
g.db = connect_db()
|
||||
g.cur = g.db.cursor()
|
||||
@ -647,7 +652,7 @@ def auth_req():
|
||||
else:
|
||||
session['message'] = ""
|
||||
conf.clear()
|
||||
return redirect(url_for("signin"))
|
||||
return redirect("/signin?redirect=" + str(request.url))
|
||||
else:
|
||||
if request.endpoint in ['signin', 'signout', 'auth', 'settings', 'update_acct', 'update_pwd',
|
||||
'update_app_ip_port', 'update_wg_conf_path']:
|
||||
@ -662,13 +667,11 @@ Sign In / Sign Out
|
||||
"""
|
||||
|
||||
|
||||
# Sign In
|
||||
@app.route('/signin', methods=['GET'])
|
||||
def signin():
|
||||
"""Sign in request.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Sign in request
|
||||
@return: template
|
||||
"""
|
||||
|
||||
message = ""
|
||||
@ -681,46 +684,43 @@ def signin():
|
||||
# Sign Out
|
||||
@app.route('/signout', methods=['GET'])
|
||||
def signout():
|
||||
"""Sign out request.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
|
||||
Sign out request
|
||||
@return: redirect back to sign in
|
||||
"""
|
||||
if "username" in session:
|
||||
session.pop("username")
|
||||
message = "Sign out successfully!"
|
||||
return render_template('signin.html', message=message)
|
||||
return redirect(url_for('signin'))
|
||||
|
||||
|
||||
# Authentication
|
||||
@app.route('/auth', methods=['POST'])
|
||||
def auth():
|
||||
"""Authentication request.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
|
||||
Authentication request
|
||||
@return: json object indicating verifying
|
||||
"""
|
||||
data = request.get_json()
|
||||
config = get_dashboard_conf()
|
||||
password = hashlib.sha256(request.form['password'].encode())
|
||||
password = hashlib.sha256(data['password'].encode())
|
||||
if password.hexdigest() == config["Account"]["password"] \
|
||||
and request.form['username'] == config["Account"]["username"]:
|
||||
session['username'] = request.form['username']
|
||||
and data['username'] == config["Account"]["username"]:
|
||||
session['username'] = data['username']
|
||||
config.clear()
|
||||
return redirect(url_for("index"))
|
||||
return jsonify({"status": True, "msg": ""})
|
||||
config.clear()
|
||||
return jsonify({"status": False, "msg": "Username or Password is incorrect."})
|
||||
|
||||
session['message'] = "Username or Password is incorrect."
|
||||
config.clear()
|
||||
return redirect(url_for("signin"))
|
||||
|
||||
"""
|
||||
Index Page
|
||||
"""
|
||||
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
def index():
|
||||
"""Index Page Related.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Index page related
|
||||
@return: Template
|
||||
"""
|
||||
msg = ""
|
||||
if "switch_msg" in session:
|
||||
@ -733,10 +733,9 @@ def index():
|
||||
# Setting Page
|
||||
@app.route('/settings', methods=['GET'])
|
||||
def settings():
|
||||
"""Setting Page Related.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Settings page related
|
||||
@return: Template
|
||||
"""
|
||||
message = ""
|
||||
status = ""
|
||||
@ -757,13 +756,11 @@ def settings():
|
||||
peer_remote_endpoint=config.get("Peers", "remote_endpoint"))
|
||||
|
||||
|
||||
# Update account username
|
||||
@app.route('/update_acct', methods=['POST'])
|
||||
def update_acct():
|
||||
"""Change account user name.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Change dashboard username
|
||||
@return: Redirect
|
||||
"""
|
||||
|
||||
if len(request.form['username']) == 0:
|
||||
@ -786,13 +783,12 @@ def update_acct():
|
||||
return redirect(url_for("settings"))
|
||||
|
||||
|
||||
# Update peer default settting
|
||||
# Update peer default setting
|
||||
@app.route('/update_peer_default_config', methods=['POST'])
|
||||
def update_peer_default_config():
|
||||
"""Change default configurations for peers.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Update new peers default setting
|
||||
@return: None
|
||||
"""
|
||||
|
||||
config = get_dashboard_conf()
|
||||
@ -860,10 +856,9 @@ def update_peer_default_config():
|
||||
# Update dashboard password
|
||||
@app.route('/update_pwd', methods=['POST'])
|
||||
def update_pwd():
|
||||
"""Change account password.
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Update dashboard password
|
||||
@return: Redirect
|
||||
"""
|
||||
|
||||
config = get_dashboard_conf()
|
||||
@ -894,10 +889,11 @@ def update_pwd():
|
||||
return redirect(url_for("settings"))
|
||||
|
||||
|
||||
# Update dashboard IP and port
|
||||
@app.route('/update_app_ip_port', methods=['POST'])
|
||||
def update_app_ip_port():
|
||||
"""Change port number of dashboard.
|
||||
"""
|
||||
Update dashboard ip and port
|
||||
@return: None
|
||||
"""
|
||||
|
||||
config = get_dashboard_conf()
|
||||
@ -905,13 +901,15 @@ def update_app_ip_port():
|
||||
config.set("Server", "app_port", request.form['app_port'])
|
||||
set_dashboard_conf(config)
|
||||
config.clear()
|
||||
os.system('bash wgd.sh restart')
|
||||
os.system('./wgd.sh restart')
|
||||
|
||||
|
||||
# Update WireGuard configuration file path
|
||||
@app.route('/update_wg_conf_path', methods=['POST'])
|
||||
def update_wg_conf_path():
|
||||
"""Change path to dashboard configuration.
|
||||
"""
|
||||
Update configuration path
|
||||
@return: None
|
||||
"""
|
||||
|
||||
config = get_dashboard_conf()
|
||||
@ -920,13 +918,14 @@ def update_wg_conf_path():
|
||||
config.clear()
|
||||
session['message'] = "WireGuard Configuration Path Update Successfully!"
|
||||
session['message_status'] = "success"
|
||||
os.system('bash wgd.sh restart')
|
||||
os.system('./wgd.sh restart')
|
||||
|
||||
|
||||
# Update configuration sorting
|
||||
@app.route('/update_dashboard_sort', methods=['POST'])
|
||||
def update_dashbaord_sort():
|
||||
"""Configuration Page Related
|
||||
"""
|
||||
Update configuration sorting
|
||||
@return: Boolean
|
||||
"""
|
||||
|
||||
config = get_dashboard_conf()
|
||||
@ -944,10 +943,10 @@ def update_dashbaord_sort():
|
||||
# Update configuration refresh interval
|
||||
@app.route('/update_dashboard_refresh_interval', methods=['POST'])
|
||||
def update_dashboard_refresh_interval():
|
||||
"""Change the refresh time.
|
||||
|
||||
:return: Return text with result
|
||||
:rtype: str
|
||||
"""
|
||||
Change the refresh time.
|
||||
@return: Return text with result
|
||||
@rtype: str
|
||||
"""
|
||||
|
||||
preset_interval = ["5000", "10000", "30000", "60000"]
|
||||
@ -964,12 +963,11 @@ def update_dashboard_refresh_interval():
|
||||
# Configuration Page
|
||||
@app.route('/configuration/<config_name>', methods=['GET'])
|
||||
def configuration(config_name):
|
||||
"""Show wireguard interface view.
|
||||
|
||||
:param config_name: Name of WG interface
|
||||
:type config_name: str
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Show wireguard interface view.
|
||||
@param config_name: Name of WG interface
|
||||
@type config_name: str
|
||||
@return: Template
|
||||
"""
|
||||
|
||||
config = get_dashboard_conf()
|
||||
@ -1004,12 +1002,11 @@ def configuration(config_name):
|
||||
# Get configuration details
|
||||
@app.route('/get_config/<config_name>', methods=['GET'])
|
||||
def get_conf(config_name):
|
||||
"""Get configuration setting of wireguard interface.
|
||||
|
||||
:param config_name: Name of WG interface
|
||||
:type config_name: str
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Get configuration setting of wireguard interface.
|
||||
@param config_name: Name of WG interface
|
||||
@type config_name: str
|
||||
@return: TODO
|
||||
"""
|
||||
|
||||
config_interface = read_conf_file_interface(config_name)
|
||||
@ -1050,12 +1047,11 @@ def get_conf(config_name):
|
||||
# Turn on / off a configuration
|
||||
@app.route('/switch/<config_name>', methods=['GET'])
|
||||
def switch(config_name):
|
||||
"""On/off the wireguard interface.
|
||||
|
||||
:param config_name: Name of WG interface
|
||||
:type config_name: str
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
On/off the wireguard interface.
|
||||
@param config_name: Name of WG interface
|
||||
@type config_name: str
|
||||
@return: redirects
|
||||
"""
|
||||
|
||||
status = get_conf_status(config_name)
|
||||
@ -1078,6 +1074,11 @@ def switch(config_name):
|
||||
|
||||
@app.route('/add_peer_bulk/<config_name>', methods=['POST'])
|
||||
def add_peer_bulk(config_name):
|
||||
"""
|
||||
Add peers by bulk
|
||||
@param config_name: Configuration Name
|
||||
@return: String
|
||||
"""
|
||||
data = request.get_json()
|
||||
keys = data['keys']
|
||||
endpoint_allowed_ip = data['endpoint_allowed_ip']
|
||||
@ -1138,9 +1139,13 @@ def add_peer_bulk(config_name):
|
||||
return exc.output.strip()
|
||||
|
||||
|
||||
# Add peer
|
||||
@app.route('/add_peer/<config_name>', methods=['POST'])
|
||||
def add_peer(config_name):
|
||||
"""
|
||||
Add Peers
|
||||
@param config_name: configuration name
|
||||
@return: string
|
||||
"""
|
||||
data = request.get_json()
|
||||
public_key = data['public_key']
|
||||
allowed_ips = data['allowed_ips']
|
||||
@ -1172,7 +1177,6 @@ def add_peer(config_name):
|
||||
if enable_preshared_key:
|
||||
now = str(datetime.now().strftime("%m%d%Y%H%M%S"))
|
||||
f_name = now + "_tmp_psk.txt"
|
||||
print(f_name)
|
||||
f = open(f_name, "w+")
|
||||
f.write(preshared_key)
|
||||
f.close()
|
||||
@ -1192,15 +1196,14 @@ def add_peer(config_name):
|
||||
return exc.output.strip()
|
||||
|
||||
|
||||
# Remove peer
|
||||
@app.route('/remove_peer/<config_name>', methods=['POST'])
|
||||
def remove_peer(config_name):
|
||||
"""Remove peer.
|
||||
|
||||
:param config_name: Name of WG interface
|
||||
:type config_name: str
|
||||
:return: Return result of action or recommendations
|
||||
:rtype: str
|
||||
"""
|
||||
Remove peer.
|
||||
@param config_name: Name of WG interface
|
||||
@type config_name: str
|
||||
@return: Return result of action or recommendations
|
||||
@rtype: str
|
||||
"""
|
||||
|
||||
if get_conf_status(config_name) == "stopped":
|
||||
@ -1231,15 +1234,14 @@ def remove_peer(config_name):
|
||||
return "true"
|
||||
|
||||
|
||||
# Save peer settings
|
||||
@app.route('/save_peer_setting/<config_name>', methods=['POST'])
|
||||
def save_peer_setting(config_name):
|
||||
"""Save peer configuration.
|
||||
"""
|
||||
Save peer configuration.
|
||||
|
||||
:param config_name: Name of WG interface
|
||||
:type config_name: str
|
||||
:return: Return status of action and text with recommendations
|
||||
:rtype: TODO
|
||||
@param config_name: Name of WG interface
|
||||
@type config_name: str
|
||||
@return: Return status of action and text with recommendations
|
||||
"""
|
||||
|
||||
data = request.get_json()
|
||||
@ -1296,12 +1298,12 @@ def save_peer_setting(config_name):
|
||||
# Get peer settings
|
||||
@app.route('/get_peer_data/<config_name>', methods=['POST'])
|
||||
def get_peer_name(config_name):
|
||||
"""Get peer settings.
|
||||
"""
|
||||
Get peer settings.
|
||||
|
||||
:param config_name: Name of WG interface
|
||||
:type config_name: str
|
||||
:return: Return settings of peer
|
||||
:rtype: TODO
|
||||
@param config_name: Name of WG interface
|
||||
@type config_name: str
|
||||
@return: Return settings of peer
|
||||
"""
|
||||
|
||||
data = request.get_json()
|
||||
@ -1321,41 +1323,14 @@ def available_ips(config_name):
|
||||
return jsonify(f_available_ips(config_name))
|
||||
|
||||
|
||||
# Generate a private key
|
||||
@app.route('/generate_peer', methods=['GET'])
|
||||
def generate_peer():
|
||||
"""Generate the private key for peer.
|
||||
|
||||
:return: Return dict with private, public and preshared keys
|
||||
:rtype: TODO
|
||||
"""
|
||||
|
||||
return jsonify(gen_private_key())
|
||||
|
||||
|
||||
# Generate a public key from a private key
|
||||
@app.route('/generate_public_key', methods=['POST'])
|
||||
def generate_public_key():
|
||||
"""Generate the public key.
|
||||
|
||||
:return: Return dict with public key or error message
|
||||
:rtype: TODO
|
||||
"""
|
||||
|
||||
data = request.get_json()
|
||||
private_key = data['private_key']
|
||||
return jsonify(gen_public_key(private_key))
|
||||
|
||||
|
||||
# Check if both key match
|
||||
@app.route('/check_key_match/<config_name>', methods=['POST'])
|
||||
def check_key_match(config_name):
|
||||
"""TODO
|
||||
|
||||
:param config_name: Name of WG interface
|
||||
:type config_name: str
|
||||
:return: Return dictionary with status
|
||||
:rtype: TODO
|
||||
"""
|
||||
Check key matches
|
||||
@param config_name: Name of WG interface
|
||||
@type config_name: str
|
||||
@return: Return dictionary with status
|
||||
"""
|
||||
|
||||
data = request.get_json()
|
||||
@ -1366,6 +1341,11 @@ def check_key_match(config_name):
|
||||
|
||||
@app.route("/qrcode/<config_name>", methods=['GET'])
|
||||
def generate_qrcode(config_name):
|
||||
"""
|
||||
Generate QRCode
|
||||
@param config_name: Configuration Name
|
||||
@return: Template containing QRcode img
|
||||
"""
|
||||
peer_id = request.args.get('id')
|
||||
get_peer = g.cur.execute(
|
||||
"SELECT private_key, allowed_ip, DNS, mtu, endpoint_allowed_ip, keepalive, preshared_key FROM "
|
||||
@ -1396,9 +1376,13 @@ def generate_qrcode(config_name):
|
||||
return redirect("/configuration/" + config_name)
|
||||
|
||||
|
||||
# Download all configuration file
|
||||
@app.route('/download_all/<config_name>', methods=['GET'])
|
||||
def download_all(config_name):
|
||||
"""
|
||||
Download all configuration
|
||||
@param config_name: Configuration Name
|
||||
@return: JSON Object
|
||||
"""
|
||||
get_peer = g.cur.execute(
|
||||
"SELECT private_key, allowed_ip, DNS, mtu, endpoint_allowed_ip, keepalive, preshared_key, name FROM "
|
||||
+ config_name + " WHERE private_key != ''").fetchall()
|
||||
@ -1445,6 +1429,11 @@ def download_all(config_name):
|
||||
# Download configuration file
|
||||
@app.route('/download/<config_name>', methods=['GET'])
|
||||
def download(config_name):
|
||||
"""
|
||||
Download one configuration
|
||||
@param config_name: Configuration name
|
||||
@return: JSON object
|
||||
"""
|
||||
peer_id = request.args.get('id')
|
||||
get_peer = g.cur.execute(
|
||||
"SELECT private_key, allowed_ip, DNS, mtu, endpoint_allowed_ip, keepalive, preshared_key, name FROM "
|
||||
@ -1491,15 +1480,15 @@ def download(config_name):
|
||||
return jsonify({"status": False, "filename": "", "content": ""})
|
||||
|
||||
|
||||
# Switch peer display mode
|
||||
@app.route('/switch_display_mode/<mode>', methods=['GET'])
|
||||
def switch_display_mode(mode):
|
||||
"""Change display view style.
|
||||
"""
|
||||
Change display view style.
|
||||
|
||||
:param mode: Mode name
|
||||
:type mode: str
|
||||
:return: Return text with result
|
||||
:rtype: str
|
||||
@param mode: Mode name
|
||||
@type mode: str
|
||||
@return: Return text with result
|
||||
@rtype: str
|
||||
"""
|
||||
|
||||
if mode in ['list', 'grid']:
|
||||
@ -1519,10 +1508,11 @@ Dashboard Tools Related
|
||||
# Get all IP for ping
|
||||
@app.route('/get_ping_ip', methods=['POST'])
|
||||
def get_ping_ip():
|
||||
"""Get ips for network testing.
|
||||
# TODO: convert return to json object
|
||||
|
||||
:return: TODO
|
||||
:rtype: TODO
|
||||
"""
|
||||
Get ips for network testing.
|
||||
@return: HTML containing a list of IPs
|
||||
"""
|
||||
|
||||
config = request.form['config']
|
||||
@ -1545,10 +1535,10 @@ def get_ping_ip():
|
||||
# Ping IP
|
||||
@app.route('/ping_ip', methods=['POST'])
|
||||
def ping_ip():
|
||||
"""Execute ping command.
|
||||
|
||||
:return: Return text with result
|
||||
:rtype: str
|
||||
"""
|
||||
Execute ping command.
|
||||
@return: Return text with result
|
||||
@rtype: str
|
||||
"""
|
||||
|
||||
try:
|
||||
@ -1573,10 +1563,11 @@ def ping_ip():
|
||||
# Traceroute IP
|
||||
@app.route('/traceroute_ip', methods=['POST'])
|
||||
def traceroute_ip():
|
||||
"""Execute ping traceroute command.
|
||||
"""
|
||||
Execute ping traceroute command.
|
||||
|
||||
:return: Return text with result
|
||||
:rtype: str
|
||||
@return: Return text with result
|
||||
@rtype: str
|
||||
"""
|
||||
|
||||
try:
|
||||
@ -1600,21 +1591,22 @@ Dashboard Initialization
|
||||
|
||||
|
||||
def init_dashboard():
|
||||
"""Create dashboard default configuration.
|
||||
"""
|
||||
Create dashboard default configuration.
|
||||
"""
|
||||
|
||||
# Set Default INI File
|
||||
if not os.path.isfile(DASHBOARD_CONF):
|
||||
open(DASHBOARD_CONF, "w+").close()
|
||||
config = get_dashboard_conf()
|
||||
# Defualt dashboard account setting
|
||||
# Default dashboard account setting
|
||||
if "Account" not in config:
|
||||
config['Account'] = {}
|
||||
if "username" not in config['Account']:
|
||||
config['Account']['username'] = 'admin'
|
||||
if "password" not in config['Account']:
|
||||
config['Account']['password'] = '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918'
|
||||
# Defualt dashboard server setting
|
||||
# Default dashboard server setting
|
||||
if "Server" not in config:
|
||||
config['Server'] = {}
|
||||
if 'wg_conf_path' not in config['Server']:
|
||||
@ -1651,12 +1643,14 @@ def init_dashboard():
|
||||
|
||||
|
||||
def check_update():
|
||||
"""Dashboard check update
|
||||
"""
|
||||
Dashboard check update
|
||||
|
||||
:return: Retunt text with result
|
||||
:rtype: str
|
||||
@return: Retunt text with result
|
||||
@rtype: str
|
||||
"""
|
||||
config = get_dashboard_conf()
|
||||
try:
|
||||
data = urllib.request.urlopen("https://api.github.com/repos/donaldzou/WGDashboard/releases").read()
|
||||
output = json.loads(data)
|
||||
release = []
|
||||
@ -1669,6 +1663,8 @@ def check_update():
|
||||
result = "true"
|
||||
|
||||
return result
|
||||
except urllib.error.HTTPError:
|
||||
return "false"
|
||||
|
||||
|
||||
"""
|
||||
|
@ -1,5 +1,4 @@
|
||||
Flask
|
||||
tinydb==4.5.2
|
||||
ifcfg
|
||||
icmplib
|
||||
flask-qrcode
|
||||
|
@ -16,11 +16,10 @@
|
||||
<div class="login-box" style="margin: auto !important;">
|
||||
<h1 class="text-center">Sign In</h1>
|
||||
<form style="margin-left: auto !important; margin-right: auto !important; max-width: 500px;" action="/auth" method="post">
|
||||
{% if message != ""%}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% if message != "" %}
|
||||
<div class="alert alert-warning" role="alert">You need to sign in first</div>
|
||||
{% endif %}
|
||||
<div class="alert alert-danger d-none" role="alert"></div>
|
||||
<div class="form-group">
|
||||
<label for="username" class="text-left" style="font-size: 1rem"><i class="bi bi-person-circle"></i> Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required>
|
||||
@ -37,20 +36,43 @@
|
||||
</body>
|
||||
{% include "footer.html" %}
|
||||
<script>
|
||||
$("button").on("click", function(){
|
||||
$("button").on("click", function(e){
|
||||
let req = $("input[required]");
|
||||
let check = true
|
||||
for (let i = 0; i < req.length; i++){
|
||||
if ($(req[i]).val().length === 0){
|
||||
$("button").html("Sign In");
|
||||
check = false;
|
||||
$("input[required]").addClass("is-invalid");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (check){
|
||||
$(this).html("Signing In...");
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
$(this).html("Signing In...").attr("disabled", "disabled");
|
||||
$.ajax({
|
||||
url: "/auth",
|
||||
method: "POST",
|
||||
headers:{"Content-Type": "application/json"},
|
||||
data: JSON.stringify({
|
||||
"username": $("#username").val(),
|
||||
"password": $("#password").val()
|
||||
})
|
||||
}).done(function(res){
|
||||
if (res.status === true){
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.get("redirect")){
|
||||
window.location.replace(urlParams.get("redirect"))
|
||||
}else{
|
||||
window.location.replace("/");
|
||||
}
|
||||
}else{
|
||||
$(".alert").html(res.msg).removeClass("d-none");
|
||||
$("button").html("Sign In").removeAttr("disabled");
|
||||
$("input[required]").addClass("is-invalid");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</html>
|
37
src/wgd.sh
37
src/wgd.sh
@ -4,6 +4,7 @@
|
||||
# Under Apache-2.0 License
|
||||
app_name="dashboard.py"
|
||||
app_official_name="WGDashboard"
|
||||
PID_FILE=./gunicorn.pid
|
||||
environment=$(if [[ $ENVIRONMENT ]]; then echo $ENVIRONMENT; else echo 'develop'; fi)
|
||||
if [[ $CONFIGURATION_PATH ]]; then
|
||||
cb_work_dir=$CONFIGURATION_PATH/letsencrypt/work-dir
|
||||
@ -66,18 +67,12 @@ install_wgd(){
|
||||
|
||||
|
||||
check_wgd_status(){
|
||||
if [[ $environment == 'production' ]]; then
|
||||
PID_FILE=./gunicorn.pid
|
||||
if test -f "$PID_FILE"; then
|
||||
if ps aux | grep -v grep | grep $(cat ./gunicorn.pid) > /dev/null; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
|
||||
else
|
||||
if ps aux | grep -v grep | grep '[p]ython3 '$app_name > /dev/null; then
|
||||
return 0
|
||||
@ -96,15 +91,8 @@ certbot_renew_ssl () {
|
||||
}
|
||||
|
||||
gunicorn_start () {
|
||||
# if [[ $SSL ]]; then
|
||||
# if [ ! -d $cb_config_dir ]; then
|
||||
# certbot_create_ssl
|
||||
# else
|
||||
# certbot_renew_ssl
|
||||
# fi
|
||||
# fi
|
||||
printf "%s\n" "$dashes"
|
||||
printf "| Starting WGDashboard in the background. |\n"
|
||||
printf "| Starting WGDashboard with Gunicorn in the background. |\n"
|
||||
if [ ! -d "log" ]; then
|
||||
mkdir "log"
|
||||
fi
|
||||
@ -112,15 +100,8 @@ gunicorn_start () {
|
||||
if [[ $USER == root ]]; then
|
||||
export PATH=$PATH:/usr/local/bin:$HOME/.local/bin
|
||||
fi
|
||||
# if [[ $SSL ]]; then
|
||||
# gunicorn --certfile $cb_config_dir/live/"$SERVERURL"/cert.pem \
|
||||
# --keyfile $cb_config_dir/live/"$SERVERURL"/privkey.pem \
|
||||
# --access-logfile log/access_"$d".log \
|
||||
# --error-logfile log/error_"$d".log 'dashboard:run_dashboard()'
|
||||
# else
|
||||
gunicorn --access-logfile log/access_"$d".log \
|
||||
--error-logfile log/error_"$d".log 'dashboard:run_dashboard()'
|
||||
# fi
|
||||
printf "| Log files is under log/ |\n"
|
||||
printf "%s\n" "$dashes"
|
||||
}
|
||||
@ -130,23 +111,11 @@ gunicorn_stop () {
|
||||
}
|
||||
|
||||
start_wgd () {
|
||||
if [[ $environment == 'production' ]]; then
|
||||
gunicorn_start
|
||||
else
|
||||
printf "%s\n" "$dashes"
|
||||
printf "| Starting WGDashboard in the background. |\n"
|
||||
if [ ! -d "log" ]
|
||||
then mkdir "log"
|
||||
fi
|
||||
d=$(date '+%Y%m%d%H%M%S')
|
||||
python3 "$app_name" > log/"$d".txt 2>&1 &
|
||||
printf "| Log files is under log/ |\n"
|
||||
printf "%s\n" "$dashes"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_wgd() {
|
||||
if [[ $environment == 'production' ]]; then
|
||||
if test -f "$PID_FILE"; then
|
||||
gunicorn_stop
|
||||
else
|
||||
kill "$(ps aux | grep "[p]ython3 $app_name" | awk '{print $2}')"
|
||||
|
Loading…
Reference in New Issue
Block a user