Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 50 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,73 @@
> [!TIP]
> 🎉 I'm excited to announce that WGDashboard is officially listed on DigitalOcean's Marketplace! For more information, please visit [Host WGDashboard & WireGuard with DigitalOcean](https://donaldzou.dev/WGDashboard-Documentation/host-wgdashboard-wireguard-with-digitalocean.html) for more information!

> [!NOTE]
> **Help Wanted 🎉**: Localizing WGDashboard to other languages! If you're willing to help, please visit https://github.com/donaldzou/WGDashboard/issues/397. Many thanks!

<hr>

<p align="center">
<img alt="WGDashboard" src="./src/static/app/public/img/logo.png" width="128">
<img alt="WGDashboard" src="./src/static/app/public/img/logo.png" width="128"/>
</p>

<h1 align="center">
WGDashboard
</h1>

<p align="center">
<img src="https://forthebadge.com/images/badges/made-with-python.svg"/>
<img src="https://forthebadge.com/images/badges/made-with-javascript.svg"/>
<img src="https://forthebadge.com/images/badges/license-mit.svg"/>
</p>
<h1 align="center">WGDashboard</h1>

<p align="center">
<img src="https://forthebadge.com/images/badges/made-with-python.svg">
<img src="https://forthebadge.com/images/badges/made-with-javascript.svg">
<img src="https://forthebadge.com/images/badges/license-mit.svg">
<img src="https://forthebadge.com/images/badges/built-with-love.svg"/>
</p>

<p align="center">
<img src="https://forthebadge.com/images/badges/built-with-love.svg">
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest">
<img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard"/>
</a>
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard">
<img src="https://wakatime.com/badge/github/donaldzou/WGDashboard.svg" alt="wakatime"/>
</a>
<a href="https://hits.seeyoufarm.com">
<img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&count_bg=%2379C83D&title_bg=%23555555&icon=github.svg&icon_color=%23E7E7E7&title=Visitor&edge_flat=false"/>
</a>
<img src="https://img.shields.io/docker/pulls/donaldzou/wgdashboard?logo=docker&label=Docker%20Image%20Pulls&labelColor=ffffff"/>
</p>

<p align="center">
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard"></a>
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/github/donaldzou/WGDashboard.svg" alt="wakatime"></a>
<a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&count_bg=%2379C83D&title_bg=%23555555&icon=github.svg&icon_color=%23E7E7E7&title=Visitor&edge_flat=false"/></a>
Monitoring WireGuard is not convenient, in most case, you'll need to login to your server and type <code>wg show</code>. That's why this project is being created, to view and manage all WireGuard configurations in a easy way.
</p>
<p align="center">Monitoring WireGuard is not convenient, in most case, you'll need to login to your server and type <code>wg show</code>. That's why this project is being created, to view and manage all WireGuard configurations in a easy way.</p>
<p align="center">With all these awesome features, while keeping it <b>easy to install and use</b></p>

<p align="center"><b><i>This project is not affiliate to the official WireGuard Project</i></b></p>
<p align="center">
With all these awesome features, while keeping it <b>easy to install and use</b>
</p>

<p align="center">
<b><i>This project is not affiliate to the official WireGuard Project</i></b>
</p>

<p align="center">
Join our Discord Server for quick help, or you wanna chat about this project!
</p>

<p align="center">
<a align="center" href="https://discord.gg/72TwzjeuWm">
<img src="https://img.shields.io/discord/1276818723637956628?labelColor=ffffff&style=for-the-badge&logo=discord&label=Discord"/>
</a>
</p>

<p align="center">
<a align="center" href="https://discord.gg/72TwzjeuWm"><img src="https://img.shields.io/discord/1276818723637956628?labelColor=ffffff&style=for-the-badge&logo=discord&label=Discord"></a>
</p>
Alternatively, you can also reach out at our Matrix.org Chatroom :)
</p>

<p align="center">
<a href="https://app.element.io/#/room/#wgd:matrix.org">
Matrix.org Chatroom
</a>
</p>

<hr>

Expand Down
141 changes: 138 additions & 3 deletions src/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from datetime import datetime, timedelta
from typing import Any
from jinja2 import Template
from flask_socketio import SocketIO, emit, disconnect
import functools
from flask import Flask, request, render_template, session, send_file
from json import JSONEncoder
from flask_cors import CORS
Expand Down Expand Up @@ -35,6 +37,7 @@
WG_CONF_PATH = None
UPDATE = None
app = Flask("WGDashboard", template_folder=os.path.abspath("./static/app/dist"))
socketio = SocketIO(app, cors_allowed_origins='*')
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
app.secret_key = secrets.token_urlsafe(32)

Expand All @@ -58,7 +61,15 @@ def ResponseObject(status=True, message=None, data=None) -> Flask.response_class
"data": data
})
response.content_type = "application/json"
return response
return response

def ResponseObjectSocket(status=True, message=None, data=None) -> Flask.response_class:
response = {
"status": status,
"message": message,
"data": data
}
return response



Expand Down Expand Up @@ -1982,6 +1993,16 @@ def sqlUpdate(statement: str, paramters: tuple = ()) -> sqlite3.Cursor:
API Routes
'''

@socketio.on('connect')
def handle_connect():
if "username" not in session:
print('disconnected')
disconnect()
else:
print('connected')



@app.before_request
def auth_req():
if request.method.lower() == 'options':
Expand Down Expand Up @@ -2796,6 +2817,122 @@ def API_ping_getAllPeersIpAddress():

import requests

def getIPAddressGeolocation(ipAddress):
try:
print("Requesting: " + ipAddress)
r = requests.get(f"http://ip-api.com/json/{ipAddress}?field=city")
return r.json()
except Exception as e:
return None

# This will be a modified version of icmplib.traceroute for socket
@socketio.on('traceroute')
def SocketIO_Traceroute(json):
data = dict(json)
if 'ipAddress' in data.keys():
from icmplib.utils import is_hostname, is_ipv6_address, is_ipv4_address, resolve, unique_identifier
from icmplib.models import ICMPRequest, Hop
from icmplib.sockets import ICMPv6Socket, ICMPv4Socket
from icmplib.exceptions import TimeExceeded, ICMPLibError
address = data.get('ipAddress')

try:
if is_hostname(address):
address = resolve(address)[0]
if is_ipv6_address(address):
_Socket = ICMPv6Socket
else:
_Socket = ICMPv4Socket
except Exception as e:
emit('tracerouteResponse', ResponseObjectSocket(False, str(e)))
return
id = unique_identifier()
ttl = 1
host_reached = False
hops = []
max_hops = 30
timeout = 2
with _Socket() as sock:
while not host_reached and ttl <= max_hops:
reply = None
packets_sent = 0
rtts = []
for sequence in range(2):
request = ICMPRequest(destination=address, id=id, sequence=sequence, ttl=ttl)
try:
sock.send(request)
packets_sent += 1
reply = sock.receive(request, timeout)
rtt = (reply.time - request.time) * 1000
rtts.append(rtt)
reply.raise_for_status()
host_reached = True
except TimeExceeded:
time.sleep(0.05)
except ICMPLibError:
break
if reply:
hop = Hop(address=reply.source, packets_sent=packets_sent, rtts=rtts, distance=ttl)
hops.append(hop)
result = {
"hop": hop.distance,
"ip": hop.address,
"avg_rtt": hop.avg_rtt,
"min_rtt": hop.min_rtt,
"max_rtt": hop.max_rtt,
"geo": getIPAddressGeolocation(hop.address)
}

else:
result = {
"hop": ttl,
"ip": "*",
"avg_rtt": "*",
"min_rtt": "*",
"max_rtt": "*",
"geo": None
}
emit('tracerouteResponse', ResponseObjectSocket(True, data=result))
ttl += 1
emit('tracerouteResponseEnd')

else:
emit('tracerouteResponse', ResponseObjectSocket(False, "Please provide IP Address/Hostname"))


@socketio.on("ping")
def SocketIO_Ping(json):
data = dict(json)
geo = {}
if 'ipAddress' in data.keys() and 'count' in data.keys() and len(data['ipAddress']) > 0 and data['count'] > 0:
ip = data['ipAddress']
count = data['count']
for i in range(count):
print(ip, count)
try:
result = ping(ip, count=1, source=None)
except Exception as e:
emit('pingResponse', ResponseObjectSocket(False, str(e)))
if result.address not in geo.keys():
geo[result.address] = getIPAddressGeolocation(result.address)
data = {
"address": result.address,
"is_alive": result.is_alive,
"min_rtt": result.min_rtt,
"avg_rtt": result.avg_rtt,
"max_rtt": result.max_rtt,
"package_sent": result.packets_sent,
"package_received": result.packets_received,
"package_loss": result.packet_loss,
"geo": geo[result.address]
}
emit('pingResponse', ResponseObjectSocket(True, data=data))
time.sleep(1)
emit('pingResponseEnd')
else:
emit('pingResponse', ResponseObjectSocket(False, "Please provide IP Address and Ping Count"))


@app.get(f'{APP_PREFIX}/api/ping/execute')
def API_ping_execute():
if "ipAddress" in request.args.keys() and "count" in request.args.keys():
Expand All @@ -2816,8 +2953,6 @@ def API_ping_execute():
"package_loss": result.packet_loss,
"geo": None
}


try:
r = requests.get(f"http://ip-api.com/json/{result.address}?field=city")
data['geo'] = r.json()
Expand Down
3 changes: 2 additions & 1 deletion src/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ flask-cors
icmplib
gunicorn
requests
tcconfig
tcconfig
flask_socketio

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading