mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-04 08:16:17 +00:00
Compare commits
13 Commits
cleanup-da
...
docker-ent
Author | SHA1 | Date | |
---|---|---|---|
|
798580be5a | ||
|
602238d794 | ||
|
4d4a15740b | ||
|
524d50ee07 | ||
|
fc591b7fe8 | ||
|
c2f06193d0 | ||
|
f2ead12315 | ||
|
ca8700ac2a | ||
|
10a8d22efd | ||
|
fc3ec61373 | ||
|
094d1c0718 | ||
|
0d814ec03c | ||
|
5ccfe07e12 |
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@@ -8,24 +8,24 @@ updates:
|
|||||||
- package-ecosystem: "pip"
|
- package-ecosystem: "pip"
|
||||||
directory: "/src"
|
directory: "/src"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
directory: "/src/static/app"
|
directory: "/src/static/app"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/.github"
|
directory: "/.github"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/docker"
|
directory: "/docker"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
|
||||||
- package-ecosystem: "docker-compose"
|
- package-ecosystem: "docker-compose"
|
||||||
directory: "/docker"
|
directory: "/docker"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,7 +19,6 @@ node_modules/**
|
|||||||
*/proxy.js
|
*/proxy.js
|
||||||
src/static/app/proxy.js
|
src/static/app/proxy.js
|
||||||
.secrets
|
.secrets
|
||||||
*.pid
|
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
@@ -83,6 +83,8 @@ ensure_installation() {
|
|||||||
mkdir -p /data/db
|
mkdir -p /data/db
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mkdir "${WGDASH}/src/log"
|
||||||
|
|
||||||
if [ ! -d "${WGDASH}/src/db" ]; then
|
if [ ! -d "${WGDASH}/src/db" ]; then
|
||||||
ln -s /data/db "${WGDASH}/src/db"
|
ln -s /data/db "${WGDASH}/src/db"
|
||||||
fi
|
fi
|
||||||
@@ -100,12 +102,11 @@ ensure_installation() {
|
|||||||
. "${WGDASH}/src/venv/bin/activate"
|
. "${WGDASH}/src/venv/bin/activate"
|
||||||
|
|
||||||
# Use the bash interpreter to install WGDashboard according to the wgd.sh script.
|
# Use the bash interpreter to install WGDashboard according to the wgd.sh script.
|
||||||
/bin/bash ./wgd.sh install
|
# /bin/bash ./wgd.sh install
|
||||||
|
|
||||||
echo "Looks like the installation succeeded. Moving on."
|
echo "Looks like the installation succeeded. Moving on."
|
||||||
|
|
||||||
# Setup WireGuard if needed
|
# Setup WireGuard if needed
|
||||||
if [ ! -f "/etc/wireguard/wg0.conf" ]; then
|
if [ -z "$(ls -A /etc/wireguard)" ]; then
|
||||||
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
||||||
|
|
||||||
echo "Setting a secure private key."
|
echo "Setting a secure private key."
|
||||||
|
656
src/dashboard.py
656
src/dashboard.py
@@ -1,45 +1,27 @@
|
|||||||
# --- Standard library imports ---
|
|
||||||
import configparser
|
|
||||||
import hashlib
|
|
||||||
import ipaddress
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import random, shutil, sqlite3, configparser, hashlib, ipaddress, json, os, secrets, subprocess
|
||||||
import random
|
import time, re, uuid, bcrypt, psutil, pyotp, threading
|
||||||
import re
|
|
||||||
import secrets
|
|
||||||
import shutil
|
|
||||||
import sqlite3
|
|
||||||
import subprocess
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
import traceback
|
import traceback
|
||||||
import uuid
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from itertools import islice
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
# --- Third-party imports ---
|
|
||||||
import bcrypt
|
|
||||||
import psutil
|
|
||||||
import pyotp
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from flask import Flask, request, render_template, session, send_file, Response
|
|
||||||
from flask_cors import CORS
|
|
||||||
from flask.json.provider import DefaultJSONProvider
|
|
||||||
from icmplib import ping, traceroute
|
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from packaging import version
|
from flask import Flask, request, render_template, session, send_file
|
||||||
|
from flask_cors import CORS
|
||||||
|
from icmplib import ping, traceroute
|
||||||
|
from flask.json.provider import DefaultJSONProvider
|
||||||
|
from itertools import islice
|
||||||
|
|
||||||
from sqlalchemy import RowMapping
|
from sqlalchemy import RowMapping
|
||||||
|
|
||||||
# --- Local module imports ---
|
|
||||||
from client import createClientBlueprint
|
|
||||||
from modules.Utilities import (
|
from modules.Utilities import (
|
||||||
RegexMatch, StringToBoolean,
|
RegexMatch, StringToBoolean,
|
||||||
ValidateIPAddressesWithRange, ValidateDNSAddress,
|
ValidateIPAddressesWithRange, ValidateDNSAddress,
|
||||||
GenerateWireguardPublicKey, GenerateWireguardPrivateKey
|
GenerateWireguardPublicKey, GenerateWireguardPrivateKey
|
||||||
)
|
)
|
||||||
|
from packaging import version
|
||||||
from modules.Email import EmailSender
|
from modules.Email import EmailSender
|
||||||
from modules.DashboardLogger import DashboardLogger
|
from modules.DashboardLogger import DashboardLogger
|
||||||
from modules.PeerJob import PeerJob
|
from modules.PeerJob import PeerJob
|
||||||
@@ -49,15 +31,16 @@ from modules.PeerJobs import PeerJobs
|
|||||||
from modules.DashboardConfig import DashboardConfig
|
from modules.DashboardConfig import DashboardConfig
|
||||||
from modules.WireguardConfiguration import WireguardConfiguration
|
from modules.WireguardConfiguration import WireguardConfiguration
|
||||||
from modules.AmneziaWireguardConfiguration import AmneziaWireguardConfiguration
|
from modules.AmneziaWireguardConfiguration import AmneziaWireguardConfiguration
|
||||||
|
|
||||||
|
from client import createClientBlueprint
|
||||||
|
|
||||||
|
from logging.config import dictConfig
|
||||||
|
|
||||||
from modules.DashboardClients import DashboardClients
|
from modules.DashboardClients import DashboardClients
|
||||||
from modules.DashboardPlugins import DashboardPlugins
|
from modules.DashboardPlugins import DashboardPlugins
|
||||||
from modules.DashboardWebHooks import DashboardWebHooks
|
from modules.DashboardWebHooks import DashboardWebHooks
|
||||||
from modules.NewConfigurationTemplates import NewConfigurationTemplates
|
from modules.NewConfigurationTemplates import NewConfigurationTemplates
|
||||||
|
|
||||||
# --- Logging configuration ---
|
|
||||||
from logging.config import dictConfig
|
|
||||||
|
|
||||||
|
|
||||||
class CustomJsonEncoder(DefaultJSONProvider):
|
class CustomJsonEncoder(DefaultJSONProvider):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
super().__init__(app)
|
super().__init__(app)
|
||||||
@@ -72,6 +55,7 @@ class CustomJsonEncoder(DefaultJSONProvider):
|
|||||||
return super().default(self)
|
return super().default(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Response Object
|
Response Object
|
||||||
'''
|
'''
|
||||||
@@ -211,12 +195,12 @@ with app.app_context():
|
|||||||
EmailSender = EmailSender(DashboardConfig)
|
EmailSender = EmailSender(DashboardConfig)
|
||||||
AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations)
|
AllPeerShareLinks: PeerShareLinks = PeerShareLinks(DashboardConfig, WireguardConfigurations)
|
||||||
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations)
|
AllPeerJobs: PeerJobs = PeerJobs(DashboardConfig, WireguardConfigurations)
|
||||||
DashboardLogger = DashboardLogger()
|
DashboardLogger: DashboardLogger = DashboardLogger()
|
||||||
DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
|
||||||
DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
|
||||||
NewConfigurationTemplates = NewConfigurationTemplates()
|
NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates()
|
||||||
InitWireguardConfigurationsList(startup=True)
|
InitWireguardConfigurationsList(startup=True)
|
||||||
DashboardClients = DashboardClients(WireguardConfigurations)
|
DashboardClients: DashboardClients = DashboardClients(WireguardConfigurations)
|
||||||
app.register_blueprint(createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients))
|
app.register_blueprint(createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients))
|
||||||
|
|
||||||
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
|
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
|
||||||
@@ -229,103 +213,32 @@ _, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
|
|||||||
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
|
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
|
||||||
_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
|
_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
API Routes
|
API Routes
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def _enforce_session_auth():
|
|
||||||
"""Enforce session authentication for non-API key access."""
|
|
||||||
white_list = [
|
|
||||||
'/static/',
|
|
||||||
'validateAuthentication',
|
|
||||||
'authenticate',
|
|
||||||
'getDashboardConfiguration',
|
|
||||||
'getDashboardTheme',
|
|
||||||
'getDashboardVersion',
|
|
||||||
'sharePeer/get',
|
|
||||||
'isTotpEnabled',
|
|
||||||
'locale',
|
|
||||||
'/fileDownload',
|
|
||||||
'/client'
|
|
||||||
]
|
|
||||||
|
|
||||||
path_ok = (
|
|
||||||
("username" in session and session.get("role") == "admin")
|
|
||||||
or (f"{APP_PREFIX}/" == request.path or f"{APP_PREFIX}" == request.path)
|
|
||||||
or not all(sub not in request.path for sub in white_list)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not path_ok:
|
|
||||||
response = Flask.make_response(app, {
|
|
||||||
"status": False,
|
|
||||||
"message": "Unauthorized access.",
|
|
||||||
"data": None
|
|
||||||
})
|
|
||||||
response.content_type = "application/json"
|
|
||||||
response.status_code = 401
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def _login_with_token(key):
|
|
||||||
auth_token = hashlib.sha256(f"{key}{datetime.now()}".encode()).hexdigest()
|
|
||||||
session.update({'role': 'admin', 'username': auth_token})
|
|
||||||
session.permanent = True
|
|
||||||
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
|
||||||
resp.set_cookie("authToken", auth_token)
|
|
||||||
return resp
|
|
||||||
|
|
||||||
|
|
||||||
def _login_with_credentials(data):
|
|
||||||
username = data.get('username')
|
|
||||||
password = data.get('password')
|
|
||||||
totp_code = data.get('totp')
|
|
||||||
|
|
||||||
valid_password = bcrypt.checkpw(password.encode("utf-8"),
|
|
||||||
DashboardConfig.GetConfig("Account", "password")[1].encode("utf-8"))
|
|
||||||
totp_enabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]
|
|
||||||
totp_valid = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now() == totp_code if totp_enabled else True
|
|
||||||
|
|
||||||
if username == DashboardConfig.GetConfig("Account", "username")[1] and valid_password and totp_valid:
|
|
||||||
auth_token = hashlib.sha256(f"{username}{datetime.now()}".encode()).hexdigest()
|
|
||||||
session.update({'role': 'admin', 'username': auth_token})
|
|
||||||
session.permanent = True
|
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login success: {username}")
|
|
||||||
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
|
||||||
resp.set_cookie("authToken", auth_token)
|
|
||||||
return resp
|
|
||||||
|
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login failed: {username}")
|
|
||||||
msg = "Sorry, your username, password or OTP is incorrect." if totp_enabled else "Sorry, your username or password is incorrect."
|
|
||||||
return ResponseObject(False, msg)
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def auth_req():
|
def auth_req():
|
||||||
# Skip preflight requests
|
|
||||||
if request.method.lower() == 'options':
|
if request.method.lower() == 'options':
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
|
|
||||||
# Logging
|
|
||||||
if "api" in request.path:
|
if "api" in request.path:
|
||||||
log_message = str(request.args) if request.method.upper() == "GET" else f"Request Args: {str(request.args)} Body:{str(request.get_json())}"
|
if str(request.method) == "GET":
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=log_message)
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=str(request.args))
|
||||||
|
elif str(request.method) == "POST":
|
||||||
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Request Args: {str(request.args)} Body:{str(request.get_json())}")
|
||||||
|
|
||||||
authentication_required = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
|
||||||
headers = request.headers
|
|
||||||
|
|
||||||
if authentication_required:
|
authenticationRequired = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
||||||
api_key = headers.get('wg-dashboard-apikey')
|
d = request.headers
|
||||||
api_key_enabled = DashboardConfig.GetConfig("Server", "dashboard_api_key")[1]
|
if authenticationRequired:
|
||||||
|
apiKey = d.get('wg-dashboard-apikey')
|
||||||
# API key authentication
|
apiKeyEnabled = DashboardConfig.GetConfig("Server", "dashboard_api_key")[1]
|
||||||
if api_key and api_key_enabled:
|
if apiKey is not None and len(apiKey) > 0 and apiKeyEnabled:
|
||||||
api_key_exists = any(k.Key == api_key for k in DashboardConfig.DashboardAPIKeys)
|
apiKeyExist = len(list(filter(lambda x : x.Key == apiKey, DashboardConfig.DashboardAPIKeys))) == 1
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"API Key Access: {api_key_exists} - Key: {api_key}")
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"API Key Access: {('true' if apiKeyExist else 'false')} - Key: {apiKey}")
|
||||||
|
if not apiKeyExist:
|
||||||
if not api_key_exists:
|
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
response = Flask.make_response(app, {
|
response = Flask.make_response(app, {
|
||||||
"status": False,
|
"status": False,
|
||||||
@@ -335,49 +248,84 @@ def auth_req():
|
|||||||
response.content_type = "application/json"
|
response.content_type = "application/json"
|
||||||
response.status_code = 401
|
response.status_code = 401
|
||||||
return response
|
return response
|
||||||
|
|
||||||
DashboardConfig.APIAccessed = True
|
DashboardConfig.APIAccessed = True
|
||||||
else:
|
else:
|
||||||
DashboardConfig.APIAccessed = False
|
DashboardConfig.APIAccessed = False
|
||||||
_enforce_session_auth()
|
whiteList = [
|
||||||
|
'/static/', 'validateAuthentication', 'authenticate', 'getDashboardConfiguration',
|
||||||
|
'getDashboardTheme', 'getDashboardVersion', 'sharePeer/get', 'isTotpEnabled', 'locale',
|
||||||
|
'/fileDownload',
|
||||||
|
'/client'
|
||||||
|
]
|
||||||
|
|
||||||
|
if (("username" not in session or session.get("role") != "admin")
|
||||||
|
and (f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}/" != request.path
|
||||||
|
and f"{(APP_PREFIX if len(APP_PREFIX) > 0 else '')}" != request.path)
|
||||||
|
and len(list(filter(lambda x : x not in request.path, whiteList))) == len(whiteList)
|
||||||
|
):
|
||||||
|
response = Flask.make_response(app, {
|
||||||
|
"status": False,
|
||||||
|
"message": "Unauthorized access.",
|
||||||
|
"data": None
|
||||||
|
})
|
||||||
|
response.content_type = "application/json"
|
||||||
|
response.status_code = 401
|
||||||
|
return response
|
||||||
|
|
||||||
@app.route(f'{APP_PREFIX}/api/handshake', methods=["GET", "OPTIONS"])
|
@app.route(f'{APP_PREFIX}/api/handshake', methods=["GET", "OPTIONS"])
|
||||||
def API_Handshake():
|
def API_Handshake():
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
|
@app.get(f'{APP_PREFIX}/api/validateAuthentication')
|
||||||
def API_ValidateAuthentication():
|
def API_ValidateAuthentication():
|
||||||
token = request.cookies.get("authToken")
|
token = request.cookies.get("authToken")
|
||||||
auth_required = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
if DashboardConfig.GetConfig("Server", "auth_req")[1]:
|
||||||
|
if token is None or token == "" or "username" not in session or session["username"] != token:
|
||||||
if auth_required and (not token or "username" not in session or session["username"] != token):
|
return ResponseObject(False, "Invalid authentication.")
|
||||||
return ResponseObject(False, "Invalid authentication.",
|
|
||||||
status_code=401)
|
|
||||||
|
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/requireAuthentication')
|
@app.get(f'{APP_PREFIX}/api/requireAuthentication')
|
||||||
def API_RequireAuthentication():
|
def API_RequireAuthentication():
|
||||||
return ResponseObject(data=DashboardConfig.GetConfig("Server", "auth_req")[1])
|
return ResponseObject(data=DashboardConfig.GetConfig("Server", "auth_req")[1])
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/authenticate')
|
@app.post(f'{APP_PREFIX}/api/authenticate')
|
||||||
def API_AuthenticateLogin():
|
def API_AuthenticateLogin():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
auth_req = DashboardConfig.GetConfig("Server", "auth_req")[1]
|
if not DashboardConfig.GetConfig("Server", "auth_req")[1]:
|
||||||
|
|
||||||
if not auth_req:
|
|
||||||
return ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
return ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
||||||
|
|
||||||
# API key login
|
|
||||||
if DashboardConfig.APIAccessed:
|
if DashboardConfig.APIAccessed:
|
||||||
return _login_with_token(request.headers.get('wg-dashboard-apikey'))
|
authToken = hashlib.sha256(f"{request.headers.get('wg-dashboard-apikey')}{datetime.now()}".encode()).hexdigest()
|
||||||
|
session['role'] = 'admin'
|
||||||
|
session['username'] = authToken
|
||||||
|
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
||||||
|
resp.set_cookie("authToken", authToken)
|
||||||
|
session.permanent = True
|
||||||
|
return resp
|
||||||
|
valid = bcrypt.checkpw(data['password'].encode("utf-8"),
|
||||||
|
DashboardConfig.GetConfig("Account", "password")[1].encode("utf-8"))
|
||||||
|
totpEnabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]
|
||||||
|
totpValid = False
|
||||||
|
if totpEnabled:
|
||||||
|
totpValid = pyotp.TOTP(DashboardConfig.GetConfig("Account", "totp_key")[1]).now() == data['totp']
|
||||||
|
|
||||||
# User login
|
if (valid
|
||||||
return _login_with_credentials(data)
|
and data['username'] == DashboardConfig.GetConfig("Account", "username")[1]
|
||||||
|
and ((totpEnabled and totpValid) or not totpEnabled)
|
||||||
|
):
|
||||||
|
authToken = hashlib.sha256(f"{data['username']}{datetime.now()}".encode()).hexdigest()
|
||||||
|
session['role'] = 'admin'
|
||||||
|
session['username'] = authToken
|
||||||
|
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
|
||||||
|
resp.set_cookie("authToken", authToken)
|
||||||
|
session.permanent = True
|
||||||
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login success: {data['username']}")
|
||||||
|
return resp
|
||||||
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"Login failed: {data['username']}")
|
||||||
|
if totpEnabled:
|
||||||
|
return ResponseObject(False, "Sorry, your username, password or OTP is incorrect.")
|
||||||
|
else:
|
||||||
|
return ResponseObject(False, "Sorry, your username or password is incorrect.")
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/signout')
|
@app.get(f'{APP_PREFIX}/api/signout')
|
||||||
def API_SignOut():
|
def API_SignOut():
|
||||||
@@ -389,7 +337,7 @@ def API_SignOut():
|
|||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurations')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurations')
|
||||||
def API_getWireguardConfigurations():
|
def API_getWireguardConfigurations():
|
||||||
InitWireguardConfigurationsList()
|
InitWireguardConfigurationsList()
|
||||||
return ResponseObject(data=list(WireguardConfigurations.values()))
|
return ResponseObject(data=[wc for wc in WireguardConfigurations.values()])
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/newConfigurationTemplates')
|
@app.get(f'{APP_PREFIX}/api/newConfigurationTemplates')
|
||||||
def API_NewConfigurationTemplates():
|
def API_NewConfigurationTemplates():
|
||||||
@@ -402,255 +350,193 @@ def API_NewConfigurationTemplates_CreateTemplate():
|
|||||||
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/updateTemplate')
|
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/updateTemplate')
|
||||||
def API_NewConfigurationTemplates_UpdateTemplate():
|
def API_NewConfigurationTemplates_UpdateTemplate():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
template = data.get('Template')
|
template = data.get('Template', None)
|
||||||
|
|
||||||
if not template:
|
if not template:
|
||||||
return ResponseObject(False, "Please provide template",
|
return ResponseObject(False, "Please provide template")
|
||||||
status_code=400)
|
|
||||||
|
|
||||||
status, msg = NewConfigurationTemplates.UpdateTemplate(template)
|
status, msg = NewConfigurationTemplates.UpdateTemplate(template)
|
||||||
|
|
||||||
return ResponseObject(status, msg)
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/deleteTemplate')
|
@app.post(f'{APP_PREFIX}/api/newConfigurationTemplates/deleteTemplate')
|
||||||
def API_NewConfigurationTemplates_DeleteTemplate():
|
def API_NewConfigurationTemplates_DeleteTemplate():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
template = data.get('Template')
|
template = data.get('Template', None)
|
||||||
|
|
||||||
if not template:
|
if not template:
|
||||||
return ResponseObject(False, "Please provide template",
|
return ResponseObject(False, "Please provide template")
|
||||||
status_code=400)
|
|
||||||
|
|
||||||
status, msg = NewConfigurationTemplates.DeleteTemplate(template)
|
status, msg = NewConfigurationTemplates.DeleteTemplate(template)
|
||||||
|
|
||||||
return ResponseObject(status, msg)
|
return ResponseObject(status, msg)
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/addWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/addWireguardConfiguration')
|
||||||
def API_addWireguardConfiguration():
|
def API_addWireguardConfiguration():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
protocol = data.get("Protocol")
|
requiredKeys = [
|
||||||
|
"ConfigurationName", "Address", "ListenPort", "PrivateKey", "Protocol"
|
||||||
|
]
|
||||||
|
for i in requiredKeys:
|
||||||
|
if i not in data.keys():
|
||||||
|
return ResponseObject(False, "Please provide all required parameters.")
|
||||||
|
|
||||||
required_keys = {"ConfigurationName", "Address", "ListenPort", "PrivateKey", "Protocol"}
|
if data.get("Protocol") not in ProtocolsEnabled():
|
||||||
if not required_keys.issubset(data.keys()):
|
return ResponseObject(False, "Please provide a valid protocol: wg / awg.")
|
||||||
return ResponseObject(False, "Please provide all required parameters.", status_code=400)
|
|
||||||
|
|
||||||
if protocol not in ProtocolsEnabled():
|
# Check duplicate names, ports, address
|
||||||
return ResponseObject(False, "Please provide a valid protocol: wg / awg.", status_code=400)
|
for i in WireguardConfigurations.values():
|
||||||
|
if i.Name == data['ConfigurationName']:
|
||||||
|
return ResponseObject(False,
|
||||||
|
f"Already have a configuration with the name \"{data['ConfigurationName']}\"",
|
||||||
|
"ConfigurationName")
|
||||||
|
|
||||||
for cfg in WireguardConfigurations.values():
|
if str(i.ListenPort) == str(data["ListenPort"]):
|
||||||
duplicates = {
|
return ResponseObject(False,
|
||||||
"ConfigurationName": cfg.Name == data['ConfigurationName'],
|
f"Already have a configuration with the port \"{data['ListenPort']}\"",
|
||||||
"ListenPort": str(cfg.ListenPort) == str(data["ListenPort"]),
|
"ListenPort")
|
||||||
"Address": cfg.Address == data["Address"]
|
|
||||||
}
|
|
||||||
for key, is_duplicate in duplicates.items():
|
|
||||||
if is_duplicate:
|
|
||||||
return ResponseObject(
|
|
||||||
False,
|
|
||||||
f"Already have a configuration with the {key.lower()} \"{data[key]}\"",
|
|
||||||
key,
|
|
||||||
status_code=400
|
|
||||||
)
|
|
||||||
|
|
||||||
paths = {
|
if i.Address == data["Address"]:
|
||||||
|
return ResponseObject(False,
|
||||||
|
f"Already have a configuration with the address \"{data['Address']}\"",
|
||||||
|
"Address")
|
||||||
|
|
||||||
|
if "Backup" in data.keys():
|
||||||
|
path = {
|
||||||
"wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
"wg": DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||||
"awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
|
"awg": DashboardConfig.GetConfig("Server", "awg_conf_path")[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if "Backup" in data:
|
if (os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||||
backup_file = data["Backup"]
|
os.path.exists(os.path.join(path['wg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||||
protocol_detected = None
|
protocol = "wg"
|
||||||
for proto, base_path in paths.items():
|
elif (os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"])) and
|
||||||
conf_path = os.path.join(base_path, 'WGDashboard_Backup', backup_file)
|
os.path.exists(os.path.join(path['awg'], 'WGDashboard_Backup', data["Backup"].replace('.conf', '.sql')))):
|
||||||
sql_path = os.path.join(base_path, 'WGDashboard_Backup', backup_file.replace('.conf', '.sql'))
|
protocol = "awg"
|
||||||
if os.path.exists(conf_path) and os.path.exists(sql_path):
|
else:
|
||||||
protocol_detected = proto
|
return ResponseObject(False, "Backup does not exist")
|
||||||
break
|
|
||||||
|
|
||||||
if not protocol_detected:
|
|
||||||
return ResponseObject(False, "Backup does not exist", status_code=400)
|
|
||||||
|
|
||||||
shutil.copy(
|
shutil.copy(
|
||||||
os.path.join(paths[protocol_detected], 'WGDashboard_Backup', backup_file),
|
os.path.join(path[protocol], 'WGDashboard_Backup', data["Backup"]),
|
||||||
os.path.join(paths[protocol_detected], f'{data["ConfigurationName"]}.conf')
|
os.path.join(path[protocol], f'{data["ConfigurationName"]}.conf')
|
||||||
)
|
)
|
||||||
protocol = protocol_detected # Use backup protocol
|
WireguardConfigurations[data['ConfigurationName']] = (
|
||||||
|
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, data=data, name=data['ConfigurationName'])) if protocol == 'wg' else (
|
||||||
|
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data, name=data['ConfigurationName']))
|
||||||
else:
|
else:
|
||||||
conf_path = os.path.join(paths[protocol], f'{data["ConfigurationName"]}.conf')
|
WireguardConfigurations[data['ConfigurationName']] = (
|
||||||
if not os.path.exists(conf_path):
|
WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data)) if data.get('Protocol') == 'wg' else (
|
||||||
with open(conf_path, 'w') as f:
|
AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data=data))
|
||||||
f.write(
|
|
||||||
f"[Interface]\n"
|
|
||||||
f"Address = {data['Address']}\n"
|
|
||||||
f"ListenPort = {data['ListenPort']}\n"
|
|
||||||
f"PrivateKey = {data['PrivateKey']}\n"
|
|
||||||
)
|
|
||||||
os.chmod(conf_path, 0o600) # secure file permissions
|
|
||||||
|
|
||||||
ConfigClass = WireguardConfiguration if protocol == "wg" else AmneziaWireguardConfiguration
|
|
||||||
|
|
||||||
WireguardConfigurations[data['ConfigurationName']] = ConfigClass(
|
|
||||||
DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks,
|
|
||||||
data=data, name=data['ConfigurationName']
|
|
||||||
)
|
|
||||||
|
|
||||||
return ResponseObject()
|
return ResponseObject()
|
||||||
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration')
|
@app.get(f'{APP_PREFIX}/api/toggleWireguardConfiguration')
|
||||||
def API_toggleWireguardConfiguration():
|
def API_toggleWireguardConfiguration():
|
||||||
configuration_name = request.args.get('configurationName')
|
configurationName = request.args.get('configurationName')
|
||||||
|
if configurationName is None or len(
|
||||||
if not configuration_name or configuration_name not in WireguardConfigurations:
|
configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
|
||||||
return ResponseObject(False, "Please provide a valid configuration name",
|
return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
|
||||||
status_code=404)
|
toggleStatus, msg = WireguardConfigurations[configurationName].toggleConfiguration()
|
||||||
|
return ResponseObject(toggleStatus, msg, WireguardConfigurations[configurationName].Status)
|
||||||
target_configuration = WireguardConfigurations[configuration_name]
|
|
||||||
status, msg = target_configuration.toggleConfiguration()
|
|
||||||
configuration_status = target_configuration.Status
|
|
||||||
|
|
||||||
return ResponseObject(status, msg, configuration_status)
|
|
||||||
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updateWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/updateWireguardConfiguration')
|
||||||
def API_updateWireguardConfiguration():
|
def API_updateWireguardConfiguration():
|
||||||
data = request.get_json() or {}
|
data = request.get_json()
|
||||||
|
requiredKeys = ["Name"]
|
||||||
|
for i in requiredKeys:
|
||||||
|
if i not in data.keys():
|
||||||
|
return ResponseObject(False, "Please provide these following field: " + ", ".join(requiredKeys))
|
||||||
name = data.get("Name")
|
name = data.get("Name")
|
||||||
|
if name not in WireguardConfigurations.keys():
|
||||||
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
|
||||||
if not name:
|
status, msg = WireguardConfigurations[name].updateConfigurationSettings(data)
|
||||||
return ResponseObject(False, "Please provide the field: Name",
|
|
||||||
status_code=400)
|
|
||||||
|
|
||||||
if name not in WireguardConfigurations:
|
|
||||||
return ResponseObject(False, "Configuration does not exist",
|
|
||||||
status_code=404)
|
|
||||||
|
|
||||||
target_configuration = WireguardConfigurations[name]
|
|
||||||
status, msg = target_configuration.updateConfigurationSettings(data)
|
|
||||||
|
|
||||||
return ResponseObject(status, msg, target_configuration)
|
|
||||||
|
|
||||||
|
return ResponseObject(status, message=msg, data=WireguardConfigurations[name])
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationInfo')
|
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationInfo')
|
||||||
def API_updateWireguardConfigurationInfo():
|
def API_updateWireguardConfigurationInfo():
|
||||||
data = request.get_json() or {}
|
data = request.get_json()
|
||||||
name = data.get('Name')
|
name = data.get('Name')
|
||||||
key = data.get('Key')
|
key = data.get('Key')
|
||||||
value = data.get('Value')
|
value = data.get('Value')
|
||||||
|
if not all([data, key, name]):
|
||||||
if not all([name, key, value]): # Required values
|
return ResponseObject(status=False, message="Please provide configuration name, key and value")
|
||||||
return ResponseObject(False, "Please provide configuration name, key, and value")
|
if name not in WireguardConfigurations.keys():
|
||||||
|
|
||||||
if name not in WireguardConfigurations:
|
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
|
||||||
target_configuration = WireguardConfigurations[name]
|
status, msg, key = WireguardConfigurations[name].updateConfigurationInfo(key, value)
|
||||||
status, msg, key = target_configuration.updateConfigurationInfo(key, value)
|
|
||||||
|
|
||||||
return ResponseObject(status, msg, key)
|
|
||||||
|
|
||||||
|
return ResponseObject(status=status, message=msg, data=key)
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRawFile')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRawFile')
|
||||||
def API_getWireguardConfigurationRawFile():
|
def API_GetWireguardConfigurationRawFile():
|
||||||
configuration_name = request.args.get('configurationName')
|
configurationName = request.args.get('configurationName')
|
||||||
|
if configurationName is None or len(
|
||||||
if not configuration_name or configuration_name not in WireguardConfigurations:
|
configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
|
||||||
return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
|
return ResponseObject(False, "Please provide a valid configuration name", status_code=404)
|
||||||
|
|
||||||
config = WireguardConfigurations[configuration_name]
|
|
||||||
|
|
||||||
return ResponseObject(data={
|
return ResponseObject(data={
|
||||||
"path": config.configPath,
|
"path": WireguardConfigurations[configurationName].configPath,
|
||||||
"content": config.getRawConfigurationFile()
|
"content": WireguardConfigurations[configurationName].getRawConfigurationFile()
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationRawFile')
|
@app.post(f'{APP_PREFIX}/api/updateWireguardConfigurationRawFile')
|
||||||
def API_UpdateWireguardConfigurationRawFile():
|
def API_UpdateWireguardConfigurationRawFile():
|
||||||
data = request.get_json() or {}
|
data = request.get_json()
|
||||||
configuration_name = data.get('configurationName')
|
configurationName = data.get('configurationName')
|
||||||
raw_configuration = data.get('rawConfiguration')
|
rawConfiguration = data.get('rawConfiguration')
|
||||||
|
if configurationName is None or len(
|
||||||
if not configuration_name or configuration_name not in WireguardConfigurations:
|
configurationName) == 0 or configurationName not in WireguardConfigurations.keys():
|
||||||
return ResponseObject(False, "Please provide a valid configuration name")
|
return ResponseObject(False, "Please provide a valid configuration name")
|
||||||
|
if rawConfiguration is None or len(rawConfiguration) == 0:
|
||||||
if not raw_configuration:
|
|
||||||
return ResponseObject(False, "Please provide content")
|
return ResponseObject(False, "Please provide content")
|
||||||
|
|
||||||
config = WireguardConfigurations[configuration_name]
|
status, err = WireguardConfigurations[configurationName].updateRawConfigurationFile(rawConfiguration)
|
||||||
status, err = config.updateRawConfigurationFile(raw_configuration)
|
|
||||||
|
|
||||||
return ResponseObject(status, err)
|
return ResponseObject(status=status, message=err)
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfiguration')
|
||||||
def API_deleteWireguardConfiguration():
|
def API_deleteWireguardConfiguration():
|
||||||
data = request.get_json() or {}
|
data = request.get_json()
|
||||||
configuration_name = data.get("ConfigurationName")
|
if "ConfigurationName" not in data.keys() or data.get("ConfigurationName") is None or data.get("ConfigurationName") not in WireguardConfigurations.keys():
|
||||||
|
|
||||||
if not configuration_name or configuration_name not in WireguardConfigurations:
|
|
||||||
return ResponseObject(False, "Please provide the configuration name you want to delete", status_code=404)
|
return ResponseObject(False, "Please provide the configuration name you want to delete", status_code=404)
|
||||||
|
rp = WireguardConfigurations.pop(data.get("ConfigurationName"))
|
||||||
|
|
||||||
rp = WireguardConfigurations.pop(configuration_name)
|
|
||||||
status = rp.deleteConfiguration()
|
status = rp.deleteConfiguration()
|
||||||
|
|
||||||
if not status:
|
if not status:
|
||||||
WireguardConfigurations[configuration_name] = rp
|
WireguardConfigurations[data.get("ConfigurationName")] = rp
|
||||||
|
|
||||||
return ResponseObject(status)
|
return ResponseObject(status)
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/renameWireguardConfiguration')
|
@app.post(f'{APP_PREFIX}/api/renameWireguardConfiguration')
|
||||||
def API_renameWireguardConfiguration():
|
def API_renameWireguardConfiguration():
|
||||||
data = request.get_json() or {}
|
data = request.get_json()
|
||||||
|
keys = ["ConfigurationName", "NewConfigurationName"]
|
||||||
|
for k in keys:
|
||||||
|
if (k not in data.keys() or data.get(k) is None or len(data.get(k)) == 0 or
|
||||||
|
(k == "ConfigurationName" and data.get(k) not in WireguardConfigurations.keys())):
|
||||||
|
return ResponseObject(False, "Please provide the configuration name you want to rename", status_code=404)
|
||||||
|
|
||||||
old_name = data.get("ConfigurationName")
|
if data.get("NewConfigurationName") in WireguardConfigurations.keys():
|
||||||
new_name = data.get("NewConfigurationName")
|
return ResponseObject(False, "Configuration name already exist", status_code=400)
|
||||||
|
|
||||||
if not old_name or old_name not in WireguardConfigurations:
|
rc = WireguardConfigurations.pop(data.get("ConfigurationName"))
|
||||||
return ResponseObject(False, "Please provide a valid configuration name to rename", status_code=404)
|
|
||||||
|
|
||||||
if not new_name:
|
|
||||||
return ResponseObject(False, "Please provide a new configuration name", status=400)
|
|
||||||
|
|
||||||
if new_name in WireguardConfigurations:
|
|
||||||
return ResponseObject(False, "The configuration name already exists", status_code=400)
|
|
||||||
|
|
||||||
rc = WireguardConfigurations.pop(old_name)
|
|
||||||
status, message = rc.renameConfiguration(new_name)
|
|
||||||
|
|
||||||
|
status, message = rc.renameConfiguration(data.get("NewConfigurationName"))
|
||||||
if status:
|
if status:
|
||||||
if rc.Protocol == 'wg':
|
WireguardConfigurations[data.get("NewConfigurationName")] = (WireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")) if rc.Protocol == 'wg' else AmneziaWireguardConfiguration(DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, data.get("NewConfigurationName")))
|
||||||
ConfigClass = WireguardConfiguration
|
|
||||||
else:
|
else:
|
||||||
ConfigClass = AmneziaWireguardConfiguration
|
WireguardConfigurations[data.get("ConfigurationName")] = rc
|
||||||
|
|
||||||
WireguardConfigurations[new_name] = ConfigClass(
|
|
||||||
DashboardConfig, AllPeerJobs, AllPeerShareLinks, DashboardWebHooks, new_name
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
WireguardConfigurations[old_name] = rc
|
|
||||||
|
|
||||||
return ResponseObject(status, message)
|
return ResponseObject(status, message)
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRealtimeTraffic')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationRealtimeTraffic')
|
||||||
def API_getWireguardConfigurationRealtimeTraffic():
|
def API_getWireguardConfigurationRealtimeTraffic():
|
||||||
configuration_name = requests.args.get('configurationName')
|
configurationName = request.args.get('configurationName')
|
||||||
|
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
||||||
if not configuration_name or configuration_name not in WireguardConfigurations:
|
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
return ResponseObject(data=WireguardConfigurations[configurationName].getRealtimeTrafficUsage())
|
||||||
rt_traffic_usage = WireguardConfigurations[configuration_name]
|
|
||||||
return ResponseObject(data=rt_traffic_usage)
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/getWireguardConfigurationBackup')
|
||||||
def API_getWireguardConfigurationBackup():
|
def API_getWireguardConfigurationBackup():
|
||||||
configuration_name = request.args.get('configurationName')
|
configurationName = request.args.get('configurationName')
|
||||||
|
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
||||||
if not configuration_name or configuration_name not in WireguardConfigurations:
|
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
return ResponseObject(data=WireguardConfigurations[configurationName].getBackups())
|
||||||
target_configuration = WireguardConfigurations[configuration_name]
|
|
||||||
return ResponseObject(data=target_configuration.getBackups())
|
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getAllWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/getAllWireguardConfigurationBackup')
|
||||||
def API_getAllWireguardConfigurationBackup():
|
def API_getAllWireguardConfigurationBackup():
|
||||||
@@ -658,86 +544,47 @@ def API_getAllWireguardConfigurationBackup():
|
|||||||
"ExistingConfigurations": {},
|
"ExistingConfigurations": {},
|
||||||
"NonExistingConfigurations": {}
|
"NonExistingConfigurations": {}
|
||||||
}
|
}
|
||||||
|
existingConfiguration = WireguardConfigurations.keys()
|
||||||
existing_configurations = WireguardConfigurations.keys()
|
for i in existingConfiguration:
|
||||||
|
b = WireguardConfigurations[i].getBackups(True)
|
||||||
for single_conf in existing_configurations:
|
if len(b) > 0:
|
||||||
backups = WireguardConfigurations[single_conf].getBackups(True)
|
data['ExistingConfigurations'][i] = WireguardConfigurations[i].getBackups(True)
|
||||||
if len(backups) > 0:
|
|
||||||
data['ExistingConfigurations'][single_conf] = WireguardConfigurations[single_conf].getBackups(True)
|
|
||||||
|
|
||||||
for protocol in ProtocolsEnabled():
|
for protocol in ProtocolsEnabled():
|
||||||
config_path_info = DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")
|
directory = os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup')
|
||||||
configuration_path = config_path_info[1]
|
if os.path.exists(directory):
|
||||||
backup_directory = os.path.join(configuration_path, 'WGDashboard_Backup')
|
files = [(file, os.path.getctime(os.path.join(directory, file)))
|
||||||
|
for file in os.listdir(directory) if os.path.isfile(os.path.join(directory, file))]
|
||||||
|
files.sort(key=lambda x: x[1], reverse=True)
|
||||||
|
|
||||||
if not os.path.exists(backup_directory):
|
for f, ct in files:
|
||||||
continue
|
if RegexMatch(r"^(.*)_(.*)\.(conf)$", f):
|
||||||
|
s = re.search(r"^(.*)_(.*)\.(conf)$", f)
|
||||||
|
name = s.group(1)
|
||||||
|
if name not in existingConfiguration:
|
||||||
|
if name not in data['NonExistingConfigurations'].keys():
|
||||||
|
data['NonExistingConfigurations'][name] = []
|
||||||
|
|
||||||
backup_files = []
|
date = s.group(2)
|
||||||
for file_name in os.listdir(backup_directory):
|
d = {
|
||||||
full_file_path = os.path.join(backup_directory, file_name)
|
|
||||||
if os.path.isfile(full_file_path):
|
|
||||||
creation_time = os.path.getctime(full_file_path)
|
|
||||||
backup_files.append((file_name, creation_time))
|
|
||||||
|
|
||||||
backup_files.sort(key=lambda file_info: file_info[1], reverse=True)
|
|
||||||
|
|
||||||
for file_name, creation_time in backup_files:
|
|
||||||
pattern = r"^(.*)_(.*)\.conf$"
|
|
||||||
match_result = re.match(pattern, file_name)
|
|
||||||
|
|
||||||
if not match_result:
|
|
||||||
continue
|
|
||||||
|
|
||||||
configuration_name = match_result.group(1)
|
|
||||||
backup_date = match_result.group(2)
|
|
||||||
|
|
||||||
if configuration_name in existing_configurations:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if 'NonExistingConfigurations' not in data:
|
|
||||||
data['NonExistingConfigurations'] = {}
|
|
||||||
|
|
||||||
if configuration_name not in data['NonExistingConfigurations']:
|
|
||||||
data['NonExistingConfigurations'][configuration_name] = []
|
|
||||||
|
|
||||||
configuration_file_path = os.path.join(backup_directory, file_name)
|
|
||||||
with open(configuration_file_path, 'r') as configuration_file:
|
|
||||||
configuration_content = configuration_file.read()
|
|
||||||
|
|
||||||
backup_data = {
|
|
||||||
"protocol": protocol,
|
"protocol": protocol,
|
||||||
"filename": file_name,
|
"filename": f,
|
||||||
"backupDate": backup_date,
|
"backupDate": date,
|
||||||
"content": configuration_content
|
"content": open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f), 'r').read()
|
||||||
}
|
}
|
||||||
|
if f.replace(".conf", ".sql") in list(os.listdir(directory)):
|
||||||
sql_file_name = file_name.replace(".conf", ".sql")
|
d['database'] = True
|
||||||
sql_file_path = os.path.join(backup_directory, sql_file_name)
|
d['databaseContent'] = open(os.path.join(DashboardConfig.GetConfig("Server", f"{protocol}_conf_path")[1], 'WGDashboard_Backup', f.replace(".conf", ".sql")), 'r').read()
|
||||||
|
data['NonExistingConfigurations'][name].append(d)
|
||||||
if os.path.isfile(sql_file_path):
|
|
||||||
with open(sql_file_path, 'r') as sql_file:
|
|
||||||
sql_content = sql_file.read()
|
|
||||||
|
|
||||||
backup_data["database"] = True
|
|
||||||
backup_data["databaseContent"] = sql_content
|
|
||||||
|
|
||||||
data['NonExistingConfigurations'][configuration_name].append(backup_data)
|
|
||||||
|
|
||||||
return ResponseObject(data=data)
|
return ResponseObject(data=data)
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup')
|
@app.get(f'{APP_PREFIX}/api/createWireguardConfigurationBackup')
|
||||||
def API_createWireguardConfigurationBackup():
|
def API_createWireguardConfigurationBackup():
|
||||||
configuration_name = request.args.get('configurationName')
|
configurationName = request.args.get('configurationName')
|
||||||
|
if configurationName is None or configurationName not in WireguardConfigurations.keys():
|
||||||
if not configuration_name or configuration_name not in WireguardConfigurations:
|
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
return ResponseObject(status=WireguardConfigurations[configurationName].backupConfigurationFile()[0],
|
||||||
conf_backup_file = WireguardConfigurations[configuration_name].backupConfigurationFile()[0]
|
data=WireguardConfigurations[configurationName].getBackups())
|
||||||
conf_backups = WireguardConfigurations[configuration_name].getBackups()
|
|
||||||
|
|
||||||
return ResponseObject(status=conf_backup_file,data=conf_backups)
|
|
||||||
|
|
||||||
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfigurationBackup')
|
@app.post(f'{APP_PREFIX}/api/deleteWireguardConfigurationBackup')
|
||||||
def API_deleteWireguardConfigurationBackup():
|
def API_deleteWireguardConfigurationBackup():
|
||||||
@@ -769,19 +616,18 @@ def API_downloadWireguardConfigurationBackup():
|
|||||||
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
@app.post(f'{APP_PREFIX}/api/restoreWireguardConfigurationBackup')
|
||||||
def API_restoreWireguardConfigurationBackup():
|
def API_restoreWireguardConfigurationBackup():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
configuration_name = data['ConfigurationName']
|
|
||||||
backup_file_name = data['BackupFileName']
|
|
||||||
|
|
||||||
if ("ConfigurationName" not in data.keys() or
|
if ("ConfigurationName" not in data.keys() or
|
||||||
"BackupFileName" not in data.keys() or
|
"BackupFileName" not in data.keys() or
|
||||||
len(data['ConfigurationName']) == 0 or
|
len(data['ConfigurationName']) == 0 or
|
||||||
len(data['BackupFileName']) == 0):
|
len(data['BackupFileName']) == 0):
|
||||||
return ResponseObject(False,"Please provide ConfigurationName and BackupFileName in body", status_code=400)
|
return ResponseObject(False,
|
||||||
|
"Please provide ConfigurationName and BackupFileName in body", status_code=400)
|
||||||
if configuration_name not in WireguardConfigurations.keys():
|
configurationName = data['ConfigurationName']
|
||||||
|
backupFileName = data['BackupFileName']
|
||||||
|
if configurationName not in WireguardConfigurations.keys():
|
||||||
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
return ResponseObject(False, "Configuration does not exist", status_code=404)
|
||||||
|
|
||||||
status = WireguardConfigurations[configuration_name].restoreBackup(backup_file_name)
|
status = WireguardConfigurations[configurationName].restoreBackup(backupFileName)
|
||||||
return ResponseObject(status=status, message=(None if status else 'Restore backup failed'))
|
return ResponseObject(status=status, message=(None if status else 'Restore backup failed'))
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/getDashboardConfiguration')
|
@app.get(f'{APP_PREFIX}/api/getDashboardConfiguration')
|
||||||
@@ -1523,6 +1369,31 @@ def API_Welcome_Finish():
|
|||||||
DashboardConfig.SetConfig("Other", "welcome_session", False)
|
DashboardConfig.SetConfig("Other", "welcome_session", False)
|
||||||
return ResponseObject()
|
return ResponseObject()
|
||||||
|
|
||||||
|
class Locale:
|
||||||
|
def __init__(self):
|
||||||
|
self.localePath = './static/locales/'
|
||||||
|
self.activeLanguages = {}
|
||||||
|
with open(os.path.join(f"{self.localePath}supported_locales.json"), "r") as f:
|
||||||
|
self.activeLanguages = sorted(json.loads(''.join(f.readlines())), key=lambda x : x['lang_name'])
|
||||||
|
|
||||||
|
def getLanguage(self) -> dict | None:
|
||||||
|
currentLanguage = DashboardConfig.GetConfig("Server", "dashboard_language")[1]
|
||||||
|
if currentLanguage == "en":
|
||||||
|
return None
|
||||||
|
if os.path.exists(os.path.join(f"{self.localePath}{currentLanguage}.json")):
|
||||||
|
with open(os.path.join(f"{self.localePath}{currentLanguage}.json"), "r") as f:
|
||||||
|
return dict(json.loads(''.join(f.readlines())))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def updateLanguage(self, lang_id):
|
||||||
|
if not os.path.exists(os.path.join(f"{self.localePath}{lang_id}.json")):
|
||||||
|
DashboardConfig.SetConfig("Server", "dashboard_language", "en-US")
|
||||||
|
else:
|
||||||
|
DashboardConfig.SetConfig("Server", "dashboard_language", lang_id)
|
||||||
|
|
||||||
|
Locale = Locale()
|
||||||
|
|
||||||
@app.get(f'{APP_PREFIX}/api/locale')
|
@app.get(f'{APP_PREFIX}/api/locale')
|
||||||
def API_Locale_CurrentLang():
|
def API_Locale_CurrentLang():
|
||||||
return ResponseObject(data=Locale.getLanguage())
|
return ResponseObject(data=Locale.getLanguage())
|
||||||
@@ -1607,31 +1478,6 @@ def API_SystemStatus():
|
|||||||
def API_ProtocolsEnabled():
|
def API_ProtocolsEnabled():
|
||||||
return ResponseObject(data=ProtocolsEnabled())
|
return ResponseObject(data=ProtocolsEnabled())
|
||||||
|
|
||||||
class Locale:
|
|
||||||
def __init__(self):
|
|
||||||
self.localePath = './static/locales/'
|
|
||||||
self.activeLanguages = {}
|
|
||||||
with open(os.path.join(f"{self.localePath}supported_locales.json"), "r") as f:
|
|
||||||
self.activeLanguages = sorted(json.loads(''.join(f.readlines())), key=lambda x : x['lang_name'])
|
|
||||||
|
|
||||||
def getLanguage(self) -> dict | None:
|
|
||||||
currentLanguage = DashboardConfig.GetConfig("Server", "dashboard_language")[1]
|
|
||||||
if currentLanguage == "en":
|
|
||||||
return None
|
|
||||||
if os.path.exists(os.path.join(f"{self.localePath}{currentLanguage}.json")):
|
|
||||||
with open(os.path.join(f"{self.localePath}{currentLanguage}.json"), "r") as f:
|
|
||||||
return dict(json.loads(''.join(f.readlines())))
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def updateLanguage(self, lang_id):
|
|
||||||
if not os.path.exists(os.path.join(f"{self.localePath}{lang_id}.json")):
|
|
||||||
DashboardConfig.SetConfig("Server", "dashboard_language", "en-US")
|
|
||||||
else:
|
|
||||||
DashboardConfig.SetConfig("Server", "dashboard_language", lang_id)
|
|
||||||
|
|
||||||
Locale = Locale()
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
OIDC Controller
|
OIDC Controller
|
||||||
'''
|
'''
|
||||||
|
@@ -184,7 +184,7 @@ class PeerJobs:
|
|||||||
f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed."
|
f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {c.Name} failed {job.Action}ed.")
|
current_app.logger.warning(f"Somehow can't find this peer {job.Peer} from {job.Configuration} failed {job.Action}ed.")
|
||||||
self.JobLogger.log(job.JobID, False,
|
self.JobLogger.log(job.JobID, False,
|
||||||
f"Somehow can't find this peer {job.Peer} from {job.Configuration} failed {job.Action}ed."
|
f"Somehow can't find this peer {job.Peer} from {job.Configuration} failed {job.Action}ed."
|
||||||
)
|
)
|
||||||
|
753
src/static/app/package-lock.json
generated
753
src/static/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@volar/language-server": "2.4.23",
|
"@volar/language-server": "2.4.23",
|
||||||
"@vue/language-server": "3.0.7",
|
"@vue/language-server": "3.0.8",
|
||||||
"@vuepic/vue-datepicker": "^11.0.2",
|
"@vuepic/vue-datepicker": "^11.0.2",
|
||||||
"@vueuse/core": "^13.5.0",
|
"@vueuse/core": "^13.5.0",
|
||||||
"@vueuse/shared": "^13.5.0",
|
"@vueuse/shared": "^13.5.0",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"is-cidr": "^5.0.3",
|
"is-cidr": "^5.0.3",
|
||||||
"npm": "^10.5.0",
|
"npm": "^11.6.0",
|
||||||
"ol": "^10.2.1",
|
"ol": "^10.2.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"pinia-plugin-persistedstate": "^4.5.0",
|
"pinia-plugin-persistedstate": "^4.5.0",
|
||||||
@@ -35,12 +35,12 @@
|
|||||||
"qrcodejs": "^1.0.0",
|
"qrcodejs": "^1.0.0",
|
||||||
"simple-code-editor": "^2.0.9",
|
"simple-code-editor": "^2.0.9",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.22",
|
||||||
"vue-chartjs": "^5.3.0",
|
"vue-chartjs": "^5.3.0",
|
||||||
"vue-router": "^4.2.5"
|
"vue-router": "^4.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"vite": "^7.1.6"
|
"vite": "^7.1.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
src/wgd.sh
41
src/wgd.sh
@@ -464,47 +464,6 @@ stop_wgd() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============= Docker Functions =============
|
|
||||||
startwgd_docker() {
|
|
||||||
_checkWireguard
|
|
||||||
printf "[WGDashboard][Docker] WireGuard configuration started\n"
|
|
||||||
{ date; start_core ; printf "\n\n"; } >> ./log/install.txt
|
|
||||||
gunicorn_start
|
|
||||||
}
|
|
||||||
|
|
||||||
start_core() {
|
|
||||||
# Re-assign config_files to ensure it includes any newly created configurations
|
|
||||||
local config_files=$(find /etc/wireguard -type f -name "*.conf")
|
|
||||||
|
|
||||||
# Set file permissions
|
|
||||||
find /etc/wireguard -type f -name "*.conf" -exec chmod 600 {} \;
|
|
||||||
find "$iptable_dir" -type f -name "*.sh" -exec chmod +x {} \;
|
|
||||||
|
|
||||||
# Start WireGuard for each config file
|
|
||||||
for file in $config_files; do
|
|
||||||
config_name=$(basename "$file" ".conf")
|
|
||||||
wg-quick up "$config_name"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
newconf_wgd() {
|
|
||||||
local wg_port_listen=$wg_port
|
|
||||||
local wg_addr_range=$wg_net
|
|
||||||
private_key=$(wg genkey)
|
|
||||||
public_key=$(echo "$private_key" | wg pubkey)
|
|
||||||
cat <<EOF >"/etc/wireguard/wg0.conf"
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = $private_key
|
|
||||||
Address = $wg_addr_range
|
|
||||||
ListenPort = $wg_port_listen
|
|
||||||
SaveConfig = true
|
|
||||||
PostUp = /opt/wireguarddashboard/src/iptable-rules/postup.sh
|
|
||||||
PreDown = /opt/wireguarddashboard/src/iptable-rules/postdown.sh
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# ============= Docker Functions =============
|
|
||||||
|
|
||||||
start_wgd_debug() {
|
start_wgd_debug() {
|
||||||
_checkWireguard
|
_checkWireguard
|
||||||
printf "[WGDashboard] Starting WGDashboard in the foreground.\n"
|
printf "[WGDashboard] Starting WGDashboard in the foreground.\n"
|
||||||
|
Reference in New Issue
Block a user