2025-01-19 14:04:59 +08:00
import random , shutil , sqlite3 , configparser , hashlib , ipaddress , json , os , secrets , subprocess
2024-11-25 22:11:51 +08:00
import time , re , urllib . error , uuid , bcrypt , psutil , pyotp , threading
2025-01-13 16:47:15 +08:00
from uuid import uuid4
2024-12-26 00:06:37 +08:00
from zipfile import ZipFile
2021-12-28 22:53:51 +03:00
from datetime import datetime , timedelta
2024-06-18 03:40:25 +08:00
from typing import Any
2025-01-13 16:47:15 +08:00
from jinja2 import Template
2025-01-19 14:04:59 +08:00
from flask import Flask , request , render_template , session , send_file
2024-06-18 03:16:42 +08:00
from json import JSONEncoder
2024-07-31 02:27:44 -04:00
from flask_cors import CORS
2021-12-26 02:26:39 +03:00
from icmplib import ping , traceroute
2024-06-18 03:16:42 +08:00
from flask . json . provider import DefaultJSONProvider
2025-02-16 17:42:32 +08:00
from itertools import islice
2024-11-25 22:11:51 +08:00
from Utilities import (
2025-01-19 14:04:59 +08:00
RegexMatch , GetRemoteEndpoint , StringToBoolean ,
ValidateIPAddressesWithRange , ValidateDNSAddress ,
2024-11-25 22:11:51 +08:00
GenerateWireguardPublicKey , GenerateWireguardPrivateKey
)
2025-02-13 23:04:27 -09:00
from packaging import version
2025-01-19 14:04:59 +08:00
from modules . Email import EmailSender
from modules . Log import Log
from modules . DashboardLogger import DashboardLogger
from modules . PeerJobLogger import PeerJobLogger
2025-01-22 15:46:04 +08:00
from modules . PeerJob import PeerJob
2025-02-08 15:45:09 +08:00
from modules . SystemStatus import SystemStatus
2025-05-08 19:03:26 +08:00
from modules . PeerShareLinks import PeerShareLinks
2025-05-08 19:05:46 +08:00
from modules . DashboardAPIKey import DashboardAPIKey
2025-02-08 15:45:09 +08:00
SystemStatus = SystemStatus ( )
2022-02-11 09:35:58 -05:00
2025-05-07 18:40:24 +08:00
from sqlalchemy_utils import database_exists , create_database
import sqlalchemy as db
2025-05-02 14:34:23 +08:00
DASHBOARD_VERSION = ' v4.2.3 '
2022-02-11 09:35:58 -05:00
2024-06-18 03:16:42 +08:00
CONFIGURATION_PATH = os . getenv ( ' CONFIGURATION_PATH ' , ' . ' )
DB_PATH = os . path . join ( CONFIGURATION_PATH , ' db ' )
2022-01-01 02:57:59 +03:00
if not os . path . isdir ( DB_PATH ) :
os . mkdir ( DB_PATH )
2024-06-18 03:16:42 +08:00
DASHBOARD_CONF = os . path . join ( CONFIGURATION_PATH , ' wg-dashboard.ini ' )
2021-12-28 22:53:51 +03:00
UPDATE = None
2024-11-07 10:37:11 +08:00
app = Flask ( " WGDashboard " , template_folder = os . path . abspath ( " ./static/app/dist " ) )
2021-12-25 14:44:14 -05:00
app . config [ ' SEND_FILE_MAX_AGE_DEFAULT ' ] = 5206928
2024-06-18 03:16:42 +08:00
app . secret_key = secrets . token_urlsafe ( 32 )
2024-08-14 01:17:47 -04:00
2024-06-18 03:16:42 +08:00
class CustomJsonEncoder ( DefaultJSONProvider ) :
def __init__ ( self , app ) :
super ( ) . __init__ ( app )
def default ( self , o ) :
2025-01-19 14:04:59 +08:00
if callable ( getattr ( o , " toJson " , None ) ) :
2024-06-18 03:16:42 +08:00
return o . toJson ( )
2025-03-12 00:44:36 +08:00
return super ( ) . default ( self )
2024-06-18 03:16:42 +08:00
app . json = CustomJsonEncoder ( app )
2024-11-25 22:11:51 +08:00
'''
Response Object
'''
2025-04-19 02:54:47 +08:00
def ResponseObject ( status = True , message = None , data = None , status_code = 200 ) - > Flask . response_class :
2024-11-25 22:11:51 +08:00
response = Flask . make_response ( app , {
" status " : status ,
" message " : message ,
" data " : data
} )
2025-04-19 02:54:47 +08:00
response . status_code = status_code
2024-11-25 22:11:51 +08:00
response . content_type = " application/json "
2025-01-19 14:04:59 +08:00
return response
2025-01-08 18:09:05 +08:00
2024-11-25 22:11:51 +08:00
"""
Peer Jobs
"""
2024-06-18 03:16:42 +08:00
class PeerJobs :
def __init__ ( self ) :
self . Jobs : list [ PeerJob ] = [ ]
2025-05-10 18:16:29 +08:00
self . engine = db . create_engine ( DashboardConfig . getConnectionString ( ' wgdashboard_job ' ) )
self . metadata = db . MetaData ( )
self . peerJobTable = db . Table ( ' PeerJobs ' , self . metadata ,
db . Column ( ' JobID ' , db . String , nullable = False , primary_key = True ) ,
db . Column ( ' Configuration ' , db . String , nullable = False ) ,
db . Column ( ' Peer ' , db . String , nullable = False ) ,
db . Column ( ' Field ' , db . String , nullable = False ) ,
db . Column ( ' Operator ' , db . String , nullable = False ) ,
db . Column ( ' Value ' , db . String , nullable = False ) ,
db . Column ( ' CreationDate ' , ( db . DATETIME if DashboardConfig . GetConfig ( " Database " , " type " ) [ 1 ] == ' sqlite ' else db . TIMESTAMP ) , nullable = False ) ,
db . Column ( ' ExpireDate ' , ( db . DATETIME if DashboardConfig . GetConfig ( " Database " , " type " ) [ 1 ] == ' sqlite ' else db . TIMESTAMP ) ) ,
db . Column ( ' Action ' , db . String , nullable = False ) ,
)
self . metadata . create_all ( self . engine )
# self.jobdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_job.db'),
# check_same_thread=False)
# self.jobdb.row_factory = sqlite3.Row
# self.__createPeerJobsDatabase()
2024-06-18 03:16:42 +08:00
self . __getJobs ( )
def __getJobs ( self ) :
2024-06-25 23:02:13 +08:00
self . Jobs . clear ( )
2025-05-10 18:16:29 +08:00
with self . engine . connect ( ) as conn :
# jobdbCursor = self.jobdb.cursor()
# jobs = jobdbCursor.execute("SELECT * FROM PeerJobs WHERE ExpireDate IS NULL").fetchall()
jobs = conn . execute ( self . peerJobTable . select ( ) . where (
self . peerJobTable . columns . ExpireDate == None
) ) . mappings ( ) . fetchall ( )
print ( jobs )
2024-07-29 18:40:07 -04:00
for job in jobs :
2024-08-20 00:02:00 -07:00
self . Jobs . append ( PeerJob (
2024-07-29 18:40:07 -04:00
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
2024-08-20 00:02:00 -07:00
def getAllJobs ( self , configuration : str = None ) :
if configuration is not None :
2025-05-10 18:16:29 +08:00
with self . engine . connect ( ) as conn :
jobs = conn . execute ( self . peerJobTable . select ( ) . where (
self . peerJobTable . columns . Configuration == configuration
) ) . mappings ( ) . fetchall ( )
# jobdbCursor = self.jobdb.cursor()
# jobs = jobdbCursor.execute(
# f"SELECT * FROM PeerJobs WHERE Configuration = ?", (configuration, )).fetchall()
2024-08-20 00:02:00 -07:00
j = [ ]
for job in jobs :
j . append ( PeerJob (
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
return j
2024-07-29 18:40:07 -04:00
return [ ]
2024-06-18 03:16:42 +08:00
2025-05-10 18:16:29 +08:00
# def __createPeerJobsDatabase(self):
# with self.jobdb:
# jobdbCursor = self.jobdb.cursor()
#
# existingTable = jobdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
# existingTable = [t['name'] for t in existingTable]
#
# if "PeerJobs" not in existingTable:
# jobdbCursor.execute('''
# CREATE TABLE PeerJobs (JobID VARCHAR NOT NULL, Configuration VARCHAR NOT NULL, Peer VARCHAR NOT NULL,
# Field VARCHAR NOT NULL, Operator VARCHAR NOT NULL, Value VARCHAR NOT NULL, CreationDate DATETIME,
# ExpireDate DATETIME, Action VARCHAR NOT NULL, PRIMARY KEY (JobID))
# ''')
# self.jobdb.commit()
2024-06-18 03:16:42 +08:00
def toJson ( self ) :
return [ x . toJson ( ) for x in self . Jobs ]
def searchJob ( self , Configuration : str , Peer : str ) :
return list ( filter ( lambda x : x . Configuration == Configuration and x . Peer == Peer , self . Jobs ) )
2025-04-19 02:54:47 +08:00
def searchJobById ( self , JobID ) :
return list ( filter ( lambda x : x . JobID == JobID , self . Jobs ) )
2024-06-25 23:02:13 +08:00
def saveJob ( self , Job : PeerJob ) - > tuple [ bool , list ] | tuple [ bool , str ] :
2025-05-10 18:16:29 +08:00
import traceback
2024-06-25 23:02:13 +08:00
try :
2025-05-10 18:16:29 +08:00
with self . engine . begin ( ) as conn :
# jobdbCursor = self.jobdb.cursor()
# if len(self.searchJobById(Job.JobID)) == 0:
# jobdbCursor.execute('''
# INSERT INTO PeerJobs VALUES (?, ?, ?, ?, ?, ?, strftime('%Y-%m-%d %H:%M:%S','now'), NULL, ?)
# ''', (Job.JobID, Job.Configuration, Job.Peer, Job.Field, Job.Operator, Job.Value, Job.Action,))
# JobLogger.log(Job.JobID, Message=f"Job is created if {Job.Field} {Job.Operator} {Job.Value} then {Job.Action}")
# else:
# currentJob = jobdbCursor.execute('SELECT * FROM PeerJobs WHERE JobID = ?', (Job.JobID, )).fetchone()
# if currentJob is not None:
# jobdbCursor.execute('''
# UPDATE PeerJobs SET Field = ?, Operator = ?, Value = ?, Action = ? WHERE JobID = ?
# ''', (Job.Field, Job.Operator, Job.Value, Job.Action, Job.JobID))
# JobLogger.log(Job.JobID,
# Message=f"Job is updated from if {currentJob['Field']} {currentJob['Operator']} {currentJob['value']} then {currentJob['Action']}; to if {Job.Field} {Job.Operator} {Job.Value} then {Job.Action}")
#
# self.jobdb.commit()
currentJob = self . searchJobById ( Job . JobID )
if len ( currentJob ) == 0 :
conn . execute (
self . peerJobTable . insert ( ) . values (
{
" JobID " : Job . JobID ,
" Configuration " : Job . Configuration ,
" Peer " : Job . Peer ,
" Field " : Job . Field ,
" Operator " : Job . Operator ,
" Value " : Job . Value ,
" CreationDate " : datetime . now ( ) ,
" ExpireDate " : None ,
" Action " : Job . Action
}
)
)
2024-08-20 00:02:00 -07:00
JobLogger . log ( Job . JobID , Message = f " Job is created if { Job . Field } { Job . Operator } { Job . Value } then { Job . Action } " )
else :
2025-05-10 18:16:29 +08:00
conn . execute (
self . peerJobTable . update ( ) . values ( {
" Field " : Job . Field ,
" Operator " : Job . Operator ,
" Value " : Job . Value ,
" Action " : Job . Action
} ) . where ( self . peerJobTable . columns . JobID == Job . JobID )
)
JobLogger . log ( Job . JobID , Message = f " Job is updated from if { currentJob [ 0 ] . Field } { currentJob [ 0 ] . Operator } { currentJob [ 0 ] . Value } then { currentJob [ 0 ] . Action } ; to if { Job . Field } { Job . Operator } { Job . Value } then { Job . Action } " )
2025-04-19 02:54:47 +08:00
2025-05-10 18:16:29 +08:00
self . __getJobs ( )
2024-07-01 00:58:02 +08:00
return True , list (
filter ( lambda x : x . Configuration == Job . Configuration and x . Peer == Job . Peer and x . JobID == Job . JobID ,
self . Jobs ) )
2024-06-25 23:02:13 +08:00
except Exception as e :
2025-05-10 18:16:29 +08:00
traceback . print_exc ( )
2024-06-25 23:02:13 +08:00
return False , str ( e )
2025-05-10 18:16:29 +08:00
def deleteJob ( self , Job : PeerJob ) - > tuple [ bool , None ] | tuple [ bool , str ] :
2024-07-01 00:58:02 +08:00
try :
2025-05-10 18:16:29 +08:00
if len ( self . searchJobById ( Job . JobID ) ) == 0 :
2024-07-01 00:58:02 +08:00
return False , " Job does not exist "
2025-05-10 18:16:29 +08:00
with self . engine . begin ( ) as conn :
# jobdbCursor = self.jobdb.cursor()
# jobdbCursor.execute('''
# UPDATE PeerJobs SET ExpireDate = strftime('%Y-%m-%d %H:%M:%S','now') WHERE JobID = ?
# ''', (Job.JobID,))
# self.jobdb.commit()
conn . execute (
self . peerJobTable . update ( ) . values (
{
" ExpireDate " : datetime . now ( )
}
) . where ( self . peerJobTable . columns . JobID == Job . JobID )
)
JobLogger . log ( Job . JobID , Message = f " Job is removed due to being deleted or finshed. " )
2024-07-01 00:58:02 +08:00
self . __getJobs ( )
2025-05-10 18:16:29 +08:00
return True , None
2024-07-01 00:58:02 +08:00
except Exception as e :
return False , str ( e )
2024-11-06 18:36:55 +08:00
2025-04-20 02:52:22 +08:00
def updateJobConfigurationName ( self , ConfigurationName : str , NewConfigurationName : str ) - > tuple [ bool , str ] | tuple [ bool , None ] :
2024-11-06 18:36:55 +08:00
try :
2025-05-10 18:16:29 +08:00
with self . engine . begin ( ) as conn :
# jobdbCursor = self.jobdb.cursor()
# jobdbCursor.execute('''
# UPDATE PeerJobs SET Configuration = ? WHERE Configuration = ?
# ''', (NewConfigurationName, ConfigurationName, ))
# self.jobdb.commit()
conn . execute (
self . peerJobTable . update ( ) . values ( {
" Configuration " : NewConfigurationName
} ) . where ( self . peerJobTable . columns . Configuration == ConfigurationName )
)
2024-11-06 18:36:55 +08:00
self . __getJobs ( )
2025-04-20 02:52:22 +08:00
return True , None
2024-11-06 18:36:55 +08:00
except Exception as e :
return False , str ( e )
2024-07-01 00:58:02 +08:00
def runJob ( self ) :
needToDelete = [ ]
2025-05-11 00:04:37 +08:00
self . __getJobs ( )
2024-07-01 00:58:02 +08:00
for job in self . Jobs :
c = WireguardConfigurations . get ( job . Configuration )
if c is not None :
f , fp = c . searchPeer ( job . Peer )
if f :
if job . Field in [ " total_receive " , " total_sent " , " total_data " ] :
s = job . Field . split ( " _ " ) [ 1 ]
x : float = getattr ( fp , f " total_ { s } " ) + getattr ( fp , f " cumu_ { s } " )
y : float = float ( job . Value )
else :
x : datetime = datetime . now ( )
2024-08-07 00:37:05 -04:00
y : datetime = datetime . strptime ( job . Value , " % Y- % m- %d % H: % M: % S " )
2024-07-01 00:58:02 +08:00
runAction : bool = self . __runJob_Compare ( x , y , job . Operator )
if runAction :
2024-07-27 18:51:43 -04:00
s = False
2024-07-01 00:58:02 +08:00
if job . Action == " restrict " :
2024-07-27 18:51:43 -04:00
s = c . restrictPeers ( [ fp . id ] ) . get_json ( )
2024-07-01 00:58:02 +08:00
elif job . Action == " delete " :
2024-07-27 18:51:43 -04:00
s = c . deletePeers ( [ fp . id ] ) . get_json ( )
if s [ ' status ' ] is True :
JobLogger . log ( job . JobID , s [ " status " ] ,
f " Peer { fp . id } from { c . Name } is successfully { job . Action } ed. "
)
needToDelete . append ( job )
else :
JobLogger . log ( job . JobID , s [ " status " ] ,
f " Peer { fp . id } from { c . Name } failed { job . Action } ed. "
)
2024-11-06 18:36:55 +08:00
else :
2025-01-19 14:04:59 +08:00
JobLogger . log ( job . JobID , False ,
f " Somehow can ' t find this peer { job . Peer } from { c . Name } failed { job . Action } ed. "
)
2024-11-06 18:36:55 +08:00
else :
2025-01-19 14:04:59 +08:00
JobLogger . log ( job . JobID , False ,
f " Somehow can ' t find this peer { job . Peer } from { job . Configuration } failed { job . Action } ed. "
)
2024-07-01 00:58:02 +08:00
for j in needToDelete :
self . deleteJob ( j )
def __runJob_Compare ( self , x : float | datetime , y : float | datetime , operator : str ) :
if operator == " eq " :
return x == y
if operator == " neq " :
return x != y
if operator == " lgt " :
return x > y
if operator == " lst " :
return x < y
2024-11-25 22:11:51 +08:00
"""
WireGuard Configuration
"""
2024-06-18 03:16:42 +08:00
class WireguardConfiguration :
class InvalidConfigurationFileException ( Exception ) :
def __init__ ( self , m ) :
self . message = m
def __str__ ( self ) :
return self . message
2024-12-02 15:09:54 +08:00
def __init__ ( self , name : str = None , data : dict = None , backup : dict = None , startup : bool = False , wg : bool = True ) :
2024-10-29 14:57:29 +08:00
2024-08-25 15:14:09 +08:00
2025-03-11 17:52:53 +08:00
self . __parser : configparser . ConfigParser = configparser . RawConfigParser ( strict = False )
2024-06-18 03:16:42 +08:00
self . __parser . optionxform = str
2024-08-15 18:26:20 -04:00
self . __configFileModifiedTime = None
2024-10-29 14:57:29 +08:00
2024-06-18 03:16:42 +08:00
self . Status : bool = False
self . Name : str = " "
self . PrivateKey : str = " "
self . PublicKey : str = " "
2025-03-12 00:44:36 +08:00
2024-06-18 03:16:42 +08:00
self . ListenPort : str = " "
self . Address : str = " "
self . DNS : str = " "
self . Table : str = " "
self . MTU : str = " "
self . PreUp : str = " "
self . PostUp : str = " "
self . PreDown : str = " "
self . PostDown : str = " "
self . SaveConfig : bool = True
2024-10-16 17:44:49 +08:00
self . Name = name
2024-12-02 15:09:54 +08:00
self . Protocol = " wg " if wg else " awg "
2024-12-02 17:36:37 +08:00
self . configPath = os . path . join ( self . __getProtocolPath ( ) , f ' { self . Name } .conf ' ) if wg else os . path . join ( DashboardConfig . GetConfig ( " Server " , " awg_conf_path " ) [ 1 ] , f ' { self . Name } .conf ' )
2024-10-25 23:34:07 +08:00
2024-06-18 03:16:42 +08:00
if name is not None :
2024-10-25 23:34:07 +08:00
if data is not None and " Backup " in data . keys ( ) :
db = self . __importDatabase (
os . path . join (
2024-12-02 15:09:54 +08:00
self . __getProtocolPath ( ) ,
2024-10-25 23:34:07 +08:00
' WGDashboard_Backup ' ,
data [ " Backup " ] . replace ( " .conf " , " .sql " ) ) )
else :
2024-12-02 17:36:37 +08:00
self . createDatabase ( )
2024-10-25 23:34:07 +08:00
2024-10-16 17:44:49 +08:00
self . __parseConfigurationFile ( )
2024-10-25 23:34:07 +08:00
self . __initPeersList ( )
2021-07-02 13:23:04 -04:00
else :
2024-06-18 03:16:42 +08:00
self . Name = data [ " ConfigurationName " ]
2024-12-02 17:36:37 +08:00
self . configPath = os . path . join ( self . __getProtocolPath ( ) , f ' { self . Name } .conf ' )
2024-10-25 23:34:07 +08:00
2024-06-18 03:16:42 +08:00
for i in dir ( self ) :
if str ( i ) in data . keys ( ) :
if isinstance ( getattr ( self , i ) , bool ) :
2024-11-25 22:11:51 +08:00
setattr ( self , i , StringToBoolean ( data [ i ] ) )
2024-06-18 03:16:42 +08:00
else :
setattr ( self , i , str ( data [ i ] ) )
2024-10-25 23:34:07 +08:00
2024-06-18 03:16:42 +08:00
self . __parser [ " Interface " ] = {
" PrivateKey " : self . PrivateKey ,
" Address " : self . Address ,
" ListenPort " : self . ListenPort ,
2025-03-11 17:52:53 +08:00
" PreUp " : f " { self . PreUp } " ,
" PreDown " : f " { self . PreDown } " ,
" PostUp " : f " { self . PostUp } " ,
" PostDown " : f " { self . PostDown } " ,
2024-06-18 03:16:42 +08:00
" SaveConfig " : " true "
}
2024-10-25 23:34:07 +08:00
2024-12-05 01:50:31 +08:00
if self . Protocol == ' awg ' :
self . __parser [ " Interface " ] [ " Jc " ] = self . Jc
self . __parser [ " Interface " ] [ " Jc " ] = self . Jc
self . __parser [ " Interface " ] [ " Jmin " ] = self . Jmin
self . __parser [ " Interface " ] [ " Jmax " ] = self . Jmax
self . __parser [ " Interface " ] [ " S1 " ] = self . S1
self . __parser [ " Interface " ] [ " S2 " ] = self . S2
self . __parser [ " Interface " ] [ " H1 " ] = self . H1
self . __parser [ " Interface " ] [ " H2 " ] = self . H2
self . __parser [ " Interface " ] [ " H3 " ] = self . H3
self . __parser [ " Interface " ] [ " H4 " ] = self . H4
2024-10-25 23:34:07 +08:00
if " Backup " not in data . keys ( ) :
2024-12-02 17:36:37 +08:00
self . createDatabase ( )
with open ( self . configPath , " w+ " ) as configFile :
2024-10-25 23:34:07 +08:00
self . __parser . write ( configFile )
2024-12-04 17:50:16 +08:00
print ( f " [WGDashboard] Configuration file { self . configPath } created " )
2024-10-25 23:34:07 +08:00
self . __initPeersList ( )
2024-12-31 11:02:52 +08:00
if not os . path . exists ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' ) ) :
os . mkdir ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' ) )
2024-10-29 14:57:29 +08:00
print ( f " [WGDashboard] Initialized Configuration: { name } " )
2024-10-31 23:28:30 +08:00
if self . getAutostartStatus ( ) and not self . getStatus ( ) and startup :
2024-10-29 14:57:29 +08:00
self . toggleConfiguration ( )
print ( f " [WGDashboard] Autostart Configuration: { name } " )
2024-11-25 22:11:51 +08:00
2024-12-02 15:09:54 +08:00
def __getProtocolPath ( self ) :
return DashboardConfig . GetConfig ( " Server " , " wg_conf_path " ) [ 1 ] if self . Protocol == " wg " \
else DashboardConfig . GetConfig ( " Server " , " awg_conf_path " ) [ 1 ]
2024-10-16 17:44:49 +08:00
def __initPeersList ( self ) :
self . Peers : list [ Peer ] = [ ]
2024-06-18 03:16:42 +08:00
self . getPeersList ( )
2024-08-19 16:50:00 -04:00
self . getRestrictedPeersList ( )
2024-12-06 20:27:04 +08:00
def getRawConfigurationFile ( self ) :
return open ( self . configPath , ' r ' ) . read ( )
def updateRawConfigurationFile ( self , newRawConfiguration ) :
backupStatus , backup = self . backupConfigurationFile ( )
if not backupStatus :
return False , " Cannot create backup "
if self . Status :
self . toggleConfiguration ( )
with open ( self . configPath , ' w ' ) as f :
f . write ( newRawConfiguration )
status , err = self . toggleConfiguration ( )
if not status :
restoreStatus = self . restoreBackup ( backup [ ' filename ' ] )
print ( f " Restore status: { restoreStatus } " )
self . toggleConfiguration ( )
return False , err
return True , None
2024-10-16 17:44:49 +08:00
def __parseConfigurationFile ( self ) :
2024-12-02 17:36:37 +08:00
with open ( self . configPath , ' r ' ) as f :
2024-11-26 21:27:32 +08:00
original = [ l . rstrip ( " \n " ) for l in f . readlines ( ) ]
try :
start = original . index ( " [Interface] " )
# Clean
for i in range ( start , len ( original ) ) :
if original [ i ] == " [Peer] " :
break
split = re . split ( r ' \ s*= \ s* ' , original [ i ] , 1 )
if len ( split ) == 2 :
key = split [ 0 ]
if key in dir ( self ) :
if isinstance ( getattr ( self , key ) , bool ) :
setattr ( self , key , False )
else :
setattr ( self , key , " " )
# Set
for i in range ( start , len ( original ) ) :
if original [ i ] == " [Peer] " :
break
split = re . split ( r ' \ s*= \ s* ' , original [ i ] , 1 )
if len ( split ) == 2 :
key = split [ 0 ]
value = split [ 1 ]
if key in dir ( self ) :
if isinstance ( getattr ( self , key ) , bool ) :
setattr ( self , key , StringToBoolean ( value ) )
else :
if len ( getattr ( self , key ) ) > 0 :
setattr ( self , key , f " { getattr ( self , key ) } , { value } " )
else :
setattr ( self , key , value )
except ValueError as e :
raise self . InvalidConfigurationFileException (
2024-12-02 17:36:37 +08:00
" [Interface] section not found in " + self . configPath )
2024-11-26 21:27:32 +08:00
if self . PrivateKey :
self . PublicKey = self . __getPublicKey ( )
self . Status = self . getStatus ( )
2024-10-17 00:04:26 +08:00
def __dropDatabase ( self ) :
2025-05-02 20:11:11 +08:00
existingTables = [ self . Name , f ' { self . Name } _restrict_access ' , f ' { self . Name } _transfer ' , f ' { self . Name } _deleted ' ]
# existingTables = sqlSelect(f"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '{self.Name}%'").fetchall()
2024-10-17 00:04:26 +08:00
for t in existingTables :
2025-05-02 20:11:11 +08:00
sqlUpdate ( " DROP TABLE ' %s ' " % t )
2024-10-17 00:04:26 +08:00
2025-05-02 20:11:11 +08:00
# existingTables = sqlSelect(f"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '{self.Name}%'").fetchall()
2024-06-18 03:16:42 +08:00
2024-12-02 17:36:37 +08:00
def createDatabase ( self , dbName = None ) :
2024-11-06 18:36:55 +08:00
if dbName is None :
dbName = self . Name
2024-08-20 00:02:00 -07:00
existingTables = sqlSelect ( " SELECT name FROM sqlite_master WHERE type= ' table ' " ) . fetchall ( )
2024-06-18 03:16:42 +08:00
existingTables = [ t [ ' name ' ] for t in existingTables ]
2024-11-06 18:36:55 +08:00
if dbName not in existingTables :
2024-08-20 00:02:00 -07:00
sqlUpdate (
2022-01-12 19:53:36 -05:00
"""
2024-08-11 19:20:42 -04:00
CREATE TABLE ' %s ' (
2022-01-04 16:32:23 -05:00
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
2022-03-21 22:33:19 -04:00
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
PRIMARY KEY ( id )
)
2024-11-06 18:36:55 +08:00
""" % d bName
2024-06-18 03:16:42 +08:00
)
2024-11-06 18:36:55 +08:00
if f ' { dbName } _restrict_access ' not in existingTables :
2024-08-20 00:02:00 -07:00
sqlUpdate (
2024-06-18 03:16:42 +08:00
"""
2024-08-11 19:20:42 -04:00
CREATE TABLE ' %s _restrict_access ' (
2022-03-21 22:33:19 -04:00
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
2022-01-04 16:32:23 -05:00
PRIMARY KEY ( id )
)
2024-11-06 18:36:55 +08:00
""" % d bName
2024-06-18 03:16:42 +08:00
)
2024-11-06 18:36:55 +08:00
if f ' { dbName } _transfer ' not in existingTables :
2024-08-20 00:02:00 -07:00
sqlUpdate (
2024-06-18 03:16:42 +08:00
"""
2024-08-11 19:20:42 -04:00
CREATE TABLE ' %s _transfer ' (
2024-06-18 03:16:42 +08:00
id VARCHAR NOT NULL , total_receive FLOAT NULL ,
2022-04-05 21:39:47 -04:00
total_sent FLOAT NULL , total_data FLOAT NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , time DATETIME
)
2024-11-06 18:36:55 +08:00
""" % d bName
2024-06-18 03:16:42 +08:00
)
2024-11-06 18:36:55 +08:00
if f ' { dbName } _deleted ' not in existingTables :
2024-08-20 00:02:00 -07:00
sqlUpdate (
2024-06-18 03:16:42 +08:00
"""
2024-08-11 19:20:42 -04:00
CREATE TABLE ' %s _deleted ' (
2024-06-18 03:16:42 +08:00
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
PRIMARY KEY ( id )
)
2024-11-06 18:36:55 +08:00
""" % d bName
2024-06-18 03:16:42 +08:00
)
2024-10-17 00:04:26 +08:00
def __dumpDatabase ( self ) :
for line in sqldb . iterdump ( ) :
if ( line . startswith ( f " INSERT INTO \" { self . Name } \" " )
or line . startswith ( f ' INSERT INTO " { self . Name } _restrict_access " ' )
or line . startswith ( f ' INSERT INTO " { self . Name } _transfer " ' )
or line . startswith ( f ' INSERT INTO " { self . Name } _deleted " ' )
) :
yield line
2024-10-25 23:34:07 +08:00
def __importDatabase ( self , sqlFilePath ) - > bool :
self . __dropDatabase ( )
2024-12-02 17:36:37 +08:00
self . createDatabase ( )
2024-10-25 23:34:07 +08:00
if not os . path . exists ( sqlFilePath ) :
return False
with open ( sqlFilePath , ' r ' ) as f :
for l in f . readlines ( ) :
l = l . rstrip ( " \n " )
if len ( l ) > 0 :
sqlUpdate ( l )
return True
2024-06-18 03:16:42 +08:00
def __getPublicKey ( self ) - > str :
2024-11-25 22:11:51 +08:00
return GenerateWireguardPublicKey ( self . PrivateKey ) [ 1 ]
2024-06-18 03:16:42 +08:00
def getStatus ( self ) - > bool :
self . Status = self . Name in psutil . net_if_addrs ( ) . keys ( )
return self . Status
2024-10-29 14:57:29 +08:00
def getAutostartStatus ( self ) :
s , d = DashboardConfig . GetConfig ( " WireGuardConfiguration " , " autostart " )
return self . Name in d
2024-06-18 03:16:42 +08:00
2024-12-03 02:34:45 +08:00
def getRestrictedPeers ( self ) :
2024-06-18 03:16:42 +08:00
self . RestrictedPeers = [ ]
2024-08-20 00:02:00 -07:00
restricted = sqlSelect ( " SELECT * FROM ' %s _restrict_access ' " % self . Name ) . fetchall ( )
2024-06-18 03:16:42 +08:00
for i in restricted :
self . RestrictedPeers . append ( Peer ( i , self ) )
2024-08-25 15:14:09 +08:00
def configurationFileChanged ( self ) :
2024-12-02 17:36:37 +08:00
mt = os . path . getmtime ( self . configPath )
2024-08-25 15:14:09 +08:00
changed = self . __configFileModifiedTime is None or self . __configFileModifiedTime != mt
self . __configFileModifiedTime = mt
return changed
2024-12-02 17:36:37 +08:00
def getPeers ( self ) :
2024-08-25 15:14:09 +08:00
if self . configurationFileChanged ( ) :
self . Peers = [ ]
2024-12-02 17:36:37 +08:00
with open ( self . configPath , ' r ' ) as configFile :
2024-08-25 15:14:09 +08:00
p = [ ]
pCounter = - 1
content = configFile . read ( ) . split ( ' \n ' )
try :
peerStarts = content . index ( " [Peer] " )
content = content [ peerStarts : ]
for i in content :
2024-11-25 22:11:51 +08:00
if not RegexMatch ( " #(.*) " , i ) and not RegexMatch ( " ;(.*) " , i ) :
2024-08-25 15:14:09 +08:00
if i == " [Peer] " :
pCounter + = 1
p . append ( { } )
p [ pCounter ] [ " name " ] = " "
else :
if len ( i ) > 0 :
split = re . split ( r ' \ s*= \ s* ' , i , 1 )
if len ( split ) == 2 :
p [ pCounter ] [ split [ 0 ] ] = split [ 1 ]
2024-11-25 22:11:51 +08:00
if RegexMatch ( " #Name# = (.*) " , i ) :
2024-08-25 15:14:09 +08:00
split = re . split ( r ' \ s*= \ s* ' , i , 1 )
if len ( split ) == 2 :
p [ pCounter ] [ " name " ] = split [ 1 ]
2024-08-15 17:45:54 -04:00
2024-08-25 15:14:09 +08:00
for i in p :
if " PublicKey " in i . keys ( ) :
checkIfExist = sqlSelect ( " SELECT * FROM ' %s ' WHERE id = ? " % self . Name ,
( ( i [ ' PublicKey ' ] ) , ) ) . fetchone ( )
if checkIfExist is None :
newPeer = {
" id " : i [ ' PublicKey ' ] ,
" private_key " : " " ,
" DNS " : DashboardConfig . GetConfig ( " Peers " , " peer_global_DNS " ) [ 1 ] ,
" endpoint_allowed_ip " : DashboardConfig . GetConfig ( " Peers " , " peer_endpoint_allowed_ip " ) [
1 ] ,
" name " : i . get ( " name " ) ,
" total_receive " : 0 ,
" total_sent " : 0 ,
" total_data " : 0 ,
" endpoint " : " N/A " ,
" status " : " stopped " ,
" latest_handshake " : " N/A " ,
" allowed_ip " : i . get ( " AllowedIPs " , " N/A " ) ,
" cumu_receive " : 0 ,
" cumu_sent " : 0 ,
" cumu_data " : 0 ,
" traffic " : [ ] ,
" mtu " : DashboardConfig . GetConfig ( " Peers " , " peer_mtu " ) [ 1 ] ,
" keepalive " : DashboardConfig . GetConfig ( " Peers " , " peer_keep_alive " ) [ 1 ] ,
" remote_endpoint " : DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] ,
" preshared_key " : i [ " PresharedKey " ] if " PresharedKey " in i . keys ( ) else " "
}
sqlUpdate (
"""
INSERT INTO ' %s '
VALUES ( : id , : private_key , : DNS , : endpoint_allowed_ip , : name , : total_receive , : total_sent ,
: total_data , : endpoint , : status , : latest_handshake , : allowed_ip , : cumu_receive , : cumu_sent ,
: cumu_data , : mtu , : keepalive , : remote_endpoint , : preshared_key ) ;
""" % s elf.Name
, newPeer )
self . Peers . append ( Peer ( newPeer , self ) )
else :
sqlUpdate ( " UPDATE ' %s ' SET allowed_ip = ? WHERE id = ? " % self . Name ,
( i . get ( " AllowedIPs " , " N/A " ) , i [ ' PublicKey ' ] , ) )
self . Peers . append ( Peer ( checkIfExist , self ) )
except Exception as e :
2024-09-05 16:17:56 +08:00
if __name__ == ' __main__ ' :
print ( f " [WGDashboard] { self . Name } Error: { str ( e ) } " )
2024-08-25 15:59:48 +08:00
else :
self . Peers . clear ( )
checkIfExist = sqlSelect ( " SELECT * FROM ' %s ' " % self . Name ) . fetchall ( )
for i in checkIfExist :
self . Peers . append ( Peer ( i , self ) )
2024-08-08 23:27:13 -04:00
2025-02-10 16:17:00 +08:00
def addPeers ( self , peers : list ) - > tuple [ bool , dict ] :
result = {
" message " : None ,
" peers " : [ ]
}
2024-11-06 20:58:53 +08:00
try :
for i in peers :
newPeer = {
" id " : i [ ' id ' ] ,
" private_key " : i [ ' private_key ' ] ,
" DNS " : i [ ' DNS ' ] ,
" endpoint_allowed_ip " : i [ ' endpoint_allowed_ip ' ] ,
" name " : i [ ' name ' ] ,
" total_receive " : 0 ,
" total_sent " : 0 ,
" total_data " : 0 ,
" endpoint " : " N/A " ,
" status " : " stopped " ,
" latest_handshake " : " N/A " ,
" allowed_ip " : i . get ( " allowed_ip " , " N/A " ) ,
" cumu_receive " : 0 ,
" cumu_sent " : 0 ,
" cumu_data " : 0 ,
" traffic " : [ ] ,
" mtu " : i [ ' mtu ' ] ,
" keepalive " : i [ ' keepalive ' ] ,
" remote_endpoint " : DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] ,
" preshared_key " : i [ " preshared_key " ]
}
sqlUpdate (
"""
INSERT INTO ' %s '
VALUES ( : id , : private_key , : DNS , : endpoint_allowed_ip , : name , : total_receive , : total_sent ,
: total_data , : endpoint , : status , : latest_handshake , : allowed_ip , : cumu_receive , : cumu_sent ,
: cumu_data , : mtu , : keepalive , : remote_endpoint , : preshared_key ) ;
""" % s elf.Name
, newPeer )
for p in peers :
2024-11-15 13:45:14 +08:00
presharedKeyExist = len ( p [ ' preshared_key ' ] ) > 0
rd = random . Random ( )
uid = str ( uuid . UUID ( int = rd . getrandbits ( 128 ) , version = 4 ) )
if presharedKeyExist :
with open ( uid , " w+ " ) as f :
f . write ( p [ ' preshared_key ' ] )
2024-12-02 16:34:26 +08:00
subprocess . check_output ( f " { self . Protocol } set { self . Name } peer { p [ ' id ' ] } allowed-ips { p [ ' allowed_ip ' ] . replace ( ' ' , ' ' ) } { f ' preshared-key { uid } ' if presharedKeyExist else ' ' } " ,
2024-11-06 20:58:53 +08:00
shell = True , stderr = subprocess . STDOUT )
2024-11-15 13:45:14 +08:00
if presharedKeyExist :
os . remove ( uid )
2024-11-06 20:58:53 +08:00
subprocess . check_output (
2024-12-02 16:34:26 +08:00
f " { self . Protocol } -quick save { self . Name } " , shell = True , stderr = subprocess . STDOUT )
2024-11-06 20:58:53 +08:00
self . getPeersList ( )
2025-02-10 16:17:00 +08:00
for p in peers :
p = self . searchPeer ( p [ ' id ' ] )
if p [ 0 ] :
result [ ' peers ' ] . append ( p [ 1 ] )
return True , result
2024-11-06 20:58:53 +08:00
except Exception as e :
2025-02-10 16:17:00 +08:00
result [ ' message ' ] = str ( e )
return False , result
2024-08-08 23:27:13 -04:00
2024-06-18 03:16:42 +08:00
def searchPeer ( self , publicKey ) :
for i in self . Peers :
if i . id == publicKey :
return True , i
return False , None
def allowAccessPeers ( self , listOfPublicKeys ) :
2024-08-08 23:27:13 -04:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-18 03:16:42 +08:00
for i in listOfPublicKeys :
2024-08-20 00:02:00 -07:00
p = sqlSelect ( " SELECT * FROM ' %s _restrict_access ' WHERE id = ? " % self . Name , ( i , ) ) . fetchone ( )
2024-06-18 03:16:42 +08:00
if p is not None :
2025-01-19 20:52:04 +08:00
sqlUpdate ( " INSERT INTO ' %s ' SELECT * FROM ' %s _restrict_access ' WHERE id = ? "
2024-06-18 03:16:42 +08:00
% ( self . Name , self . Name , ) , ( p [ ' id ' ] , ) )
2024-08-20 00:02:00 -07:00
sqlUpdate ( " DELETE FROM ' %s _restrict_access ' WHERE id = ? "
2024-06-18 03:16:42 +08:00
% self . Name , ( p [ ' id ' ] , ) )
2024-09-17 14:42:25 +08:00
presharedKeyExist = len ( p [ ' preshared_key ' ] ) > 0
rd = random . Random ( )
2024-11-15 13:45:14 +08:00
uid = str ( uuid . UUID ( int = rd . getrandbits ( 128 ) , version = 4 ) )
2024-09-17 14:42:25 +08:00
if presharedKeyExist :
2024-11-15 13:45:14 +08:00
with open ( uid , " w+ " ) as f :
2024-09-17 14:42:25 +08:00
f . write ( p [ ' preshared_key ' ] )
2024-12-02 16:34:26 +08:00
subprocess . check_output ( f " { self . Protocol } set { self . Name } peer { p [ ' id ' ] } allowed-ips { p [ ' allowed_ip ' ] . replace ( ' ' , ' ' ) } { f ' preshared-key { uid } ' if presharedKeyExist else ' ' } " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
2024-11-15 13:45:14 +08:00
if presharedKeyExist : os . remove ( uid )
2024-06-18 03:16:42 +08:00
else :
return ResponseObject ( False , " Failed to allow access of peer " + i )
if not self . __wgSave ( ) :
return ResponseObject ( False , " Failed to save configuration through WireGuard " )
2024-12-02 17:36:37 +08:00
self . getPeers ( )
2024-11-03 17:36:03 +08:00
return ResponseObject ( True , " Allow access successfully " )
2024-06-18 03:16:42 +08:00
def restrictPeers ( self , listOfPublicKeys ) :
numOfRestrictedPeers = 0
numOfFailedToRestrictPeers = 0
2024-08-08 23:27:13 -04:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-18 03:16:42 +08:00
for p in listOfPublicKeys :
found , pf = self . searchPeer ( p )
if found :
try :
2024-12-02 16:34:26 +08:00
subprocess . check_output ( f " { self . Protocol } set { self . Name } peer { pf . id } remove " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
2025-01-19 20:52:04 +08:00
sqlUpdate ( " INSERT INTO ' %s _restrict_access ' SELECT * FROM ' %s ' WHERE id = ? " %
2024-06-18 03:16:42 +08:00
( self . Name , self . Name , ) , ( pf . id , ) )
2024-08-20 00:02:00 -07:00
sqlUpdate ( " UPDATE ' %s _restrict_access ' SET status = ' stopped ' WHERE id = ? " %
2024-06-18 03:16:42 +08:00
( self . Name , ) , ( pf . id , ) )
2024-08-20 00:02:00 -07:00
sqlUpdate ( " DELETE FROM ' %s ' WHERE id = ? " % self . Name , ( pf . id , ) )
2024-06-18 03:16:42 +08:00
numOfRestrictedPeers + = 1
except Exception as e :
numOfFailedToRestrictPeers + = 1
if not self . __wgSave ( ) :
return ResponseObject ( False , " Failed to save configuration through WireGuard " )
2024-12-02 17:36:37 +08:00
self . getPeers ( )
2024-06-18 03:16:42 +08:00
if numOfRestrictedPeers == len ( listOfPublicKeys ) :
return ResponseObject ( True , f " Restricted { numOfRestrictedPeers } peer(s) " )
return ResponseObject ( False ,
f " Restricted { numOfRestrictedPeers } peer(s) successfully. Failed to restrict { numOfFailedToRestrictPeers } peer(s) " )
2025-05-11 00:04:37 +08:00
2024-06-18 03:16:42 +08:00
def deletePeers ( self , listOfPublicKeys ) :
numOfDeletedPeers = 0
numOfFailedToDeletePeers = 0
2024-08-08 23:27:13 -04:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-18 03:16:42 +08:00
for p in listOfPublicKeys :
found , pf = self . searchPeer ( p )
if found :
try :
2024-12-02 16:34:26 +08:00
subprocess . check_output ( f " { self . Protocol } set { self . Name } peer { pf . id } remove " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
2024-08-20 00:02:00 -07:00
sqlUpdate ( " DELETE FROM ' %s ' WHERE id = ? " % self . Name , ( pf . id , ) )
2024-06-18 03:16:42 +08:00
numOfDeletedPeers + = 1
except Exception as e :
numOfFailedToDeletePeers + = 1
if not self . __wgSave ( ) :
return ResponseObject ( False , " Failed to save configuration through WireGuard " )
2024-12-02 17:36:37 +08:00
self . getPeers ( )
2025-05-02 11:52:39 +02:00
if numOfDeletedPeers == 0 and numOfFailedToDeletePeers == 0 :
return ResponseObject ( False , " No peer(s) to delete found " , responseCode = 404 )
2024-06-18 03:16:42 +08:00
if numOfDeletedPeers == len ( listOfPublicKeys ) :
return ResponseObject ( True , f " Deleted { numOfDeletedPeers } peer(s) " )
return ResponseObject ( False ,
f " Deleted { numOfDeletedPeers } peer(s) successfully. Failed to delete { numOfFailedToDeletePeers } peer(s) " )
def __wgSave ( self ) - > tuple [ bool , str ] | tuple [ bool , None ] :
try :
2024-12-02 16:34:26 +08:00
subprocess . check_output ( f " { self . Protocol } -quick save { self . Name } " , shell = True , stderr = subprocess . STDOUT )
2024-06-18 03:16:42 +08:00
return True , None
except subprocess . CalledProcessError as e :
return False , str ( e )
2021-08-14 23:30:05 -04:00
2024-06-18 03:16:42 +08:00
def getPeersLatestHandshake ( self ) :
2024-08-08 23:27:13 -04:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-18 03:16:42 +08:00
try :
2024-12-02 16:34:26 +08:00
latestHandshake = subprocess . check_output ( f " { self . Protocol } show { self . Name } latest-handshakes " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
except subprocess . CalledProcessError :
return " stopped "
latestHandshake = latestHandshake . decode ( " UTF-8 " ) . split ( )
2022-03-29 15:11:50 -04:00
count = 0
2024-06-18 03:16:42 +08:00
now = datetime . now ( )
time_delta = timedelta ( minutes = 2 )
for _ in range ( int ( len ( latestHandshake ) / 2 ) ) :
minus = now - datetime . fromtimestamp ( int ( latestHandshake [ count + 1 ] ) )
if minus < time_delta :
status = " running "
else :
status = " stopped "
if int ( latestHandshake [ count + 1 ] ) > 0 :
2024-08-25 15:59:48 +08:00
sqlUpdate ( " UPDATE ' %s ' SET latest_handshake = ?, status = ? WHERE id= ? " % self . Name
2024-06-18 03:16:42 +08:00
, ( str ( minus ) . split ( " . " , maxsplit = 1 ) [ 0 ] , status , latestHandshake [ count ] , ) )
2022-03-29 15:11:50 -04:00
else :
2024-08-25 15:59:48 +08:00
sqlUpdate ( " UPDATE ' %s ' SET latest_handshake = ' No Handshake ' , status = ? WHERE id= ? " % self . Name
2024-06-18 03:16:42 +08:00
, ( status , latestHandshake [ count ] , ) )
count + = 2
2024-08-15 18:26:20 -04:00
2024-06-18 03:16:42 +08:00
def getPeersTransfer ( self ) :
2024-08-08 23:27:13 -04:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-18 03:16:42 +08:00
try :
2024-12-02 16:34:26 +08:00
data_usage = subprocess . check_output ( f " { self . Protocol } show { self . Name } transfer " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
data_usage = data_usage . decode ( " UTF-8 " ) . split ( " \n " )
data_usage = [ p . split ( " \t " ) for p in data_usage ]
for i in range ( len ( data_usage ) ) :
if len ( data_usage [ i ] ) == 3 :
2024-08-20 00:02:00 -07:00
cur_i = sqlSelect (
2024-08-11 19:20:42 -04:00
" SELECT total_receive, total_sent, cumu_receive, cumu_sent, status FROM ' %s ' WHERE id= ? "
2024-06-18 03:16:42 +08:00
% self . Name , ( data_usage [ i ] [ 0 ] , ) ) . fetchone ( )
if cur_i is not None :
2024-08-15 18:26:20 -04:00
cur_i = dict ( cur_i )
2024-06-18 03:16:42 +08:00
total_sent = cur_i [ ' total_sent ' ]
total_receive = cur_i [ ' total_receive ' ]
2024-08-09 16:09:08 -04:00
cur_total_sent = float ( data_usage [ i ] [ 2 ] ) / ( 1024 * * 3 )
cur_total_receive = float ( data_usage [ i ] [ 1 ] ) / ( 1024 * * 3 )
2024-06-18 03:16:42 +08:00
cumulative_receive = cur_i [ ' cumu_receive ' ] + total_receive
cumulative_sent = cur_i [ ' cumu_sent ' ] + total_sent
if total_sent < = cur_total_sent and total_receive < = cur_total_receive :
total_sent = cur_total_sent
total_receive = cur_total_receive
else :
2024-08-20 00:02:00 -07:00
sqlUpdate (
2024-08-11 19:20:42 -04:00
" UPDATE ' %s ' SET cumu_receive = ?, cumu_sent = ?, cumu_data = ? WHERE id = ? " %
2024-08-09 16:09:08 -04:00
self . Name , ( cumulative_receive , cumulative_sent ,
2024-08-09 22:16:38 -04:00
cumulative_sent + cumulative_receive ,
2024-06-18 03:16:42 +08:00
data_usage [ i ] [ 0 ] , ) )
total_sent = 0
total_receive = 0
_ , p = self . searchPeer ( data_usage [ i ] [ 0 ] )
2024-08-09 16:09:08 -04:00
if p . total_receive != total_receive or p . total_sent != total_sent :
2024-08-20 00:02:00 -07:00
sqlUpdate (
2024-08-11 19:20:42 -04:00
" UPDATE ' %s ' SET total_receive = ?, total_sent = ?, total_data = ? WHERE id = ? "
2024-08-09 16:09:08 -04:00
% self . Name , ( total_receive , total_sent ,
total_receive + total_sent , data_usage [ i ] [ 0 ] , ) )
2024-06-18 03:16:42 +08:00
except Exception as e :
2024-08-15 18:26:20 -04:00
print ( f " [WGDashboard] { self . Name } Error: { str ( e ) } { str ( e . __traceback__ ) } " )
2024-06-18 03:16:42 +08:00
def getPeersEndpoint ( self ) :
2024-08-08 23:27:13 -04:00
if not self . getStatus ( ) :
self . toggleConfiguration ( )
2024-06-18 03:16:42 +08:00
try :
2024-12-02 16:34:26 +08:00
data_usage = subprocess . check_output ( f " { self . Protocol } show { self . Name } endpoints " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
except subprocess . CalledProcessError :
return " stopped "
data_usage = data_usage . decode ( " UTF-8 " ) . split ( )
count = 0
for _ in range ( int ( len ( data_usage ) / 2 ) ) :
2024-09-05 14:51:00 +08:00
sqlUpdate ( " UPDATE ' %s ' SET endpoint = ? WHERE id = ? " % self . Name
2024-06-18 03:16:42 +08:00
, ( data_usage [ count + 1 ] , data_usage [ count ] , ) )
count + = 2
def toggleConfiguration ( self ) - > [ bool , str ] :
self . getStatus ( )
if self . Status :
2022-01-06 15:17:43 -05:00
try :
2024-12-02 16:34:26 +08:00
check = subprocess . check_output ( f " { self . Protocol } -quick down { self . Name } " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
except subprocess . CalledProcessError as exc :
return False , str ( exc . output . strip ( ) . decode ( " utf-8 " ) )
else :
try :
2024-12-02 16:34:26 +08:00
check = subprocess . check_output ( f " { self . Protocol } -quick up { self . Name } " , shell = True , stderr = subprocess . STDOUT )
2024-06-18 03:16:42 +08:00
except subprocess . CalledProcessError as exc :
return False , str ( exc . output . strip ( ) . decode ( " utf-8 " ) )
2024-11-26 21:27:32 +08:00
self . __parseConfigurationFile ( )
2024-06-18 03:16:42 +08:00
self . getStatus ( )
return True , None
def getPeersList ( self ) :
2024-12-02 17:36:37 +08:00
self . getPeers ( )
2024-06-18 03:16:42 +08:00
return self . Peers
def getRestrictedPeersList ( self ) - > list :
2024-12-03 02:34:45 +08:00
self . getRestrictedPeers ( )
2024-06-18 03:16:42 +08:00
return self . RestrictedPeers
def toJson ( self ) :
self . Status = self . getStatus ( )
return {
" Status " : self . Status ,
" Name " : self . Name ,
" PrivateKey " : self . PrivateKey ,
" PublicKey " : self . PublicKey ,
" Address " : self . Address ,
" ListenPort " : self . ListenPort ,
" PreUp " : self . PreUp ,
" PreDown " : self . PreDown ,
" PostUp " : self . PostUp ,
" PostDown " : self . PostDown ,
2024-08-09 16:09:55 -04:00
" SaveConfig " : self . SaveConfig ,
2024-08-09 16:09:23 -04:00
" DataUsage " : {
" Total " : sum ( list ( map ( lambda x : x . cumu_data + x . total_data , self . Peers ) ) ) ,
" Sent " : sum ( list ( map ( lambda x : x . cumu_sent + x . total_sent , self . Peers ) ) ) ,
" Receive " : sum ( list ( map ( lambda x : x . cumu_receive + x . total_receive , self . Peers ) ) )
} ,
2024-10-05 16:10:36 +08:00
" ConnectedPeers " : len ( list ( filter ( lambda x : x . status == " running " , self . Peers ) ) ) ,
2024-12-02 16:34:26 +08:00
" TotalPeers " : len ( self . Peers ) ,
2025-04-24 18:51:26 +03:30
" Protocol " : self . Protocol ,
" Table " : self . Table ,
2024-06-18 03:16:42 +08:00
}
2024-10-04 16:58:47 +08:00
2024-12-06 20:27:04 +08:00
def backupConfigurationFile ( self ) - > tuple [ bool , dict [ str , str ] ] :
2024-12-02 15:09:54 +08:00
if not os . path . exists ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' ) ) :
os . mkdir ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' ) )
2024-10-17 00:04:26 +08:00
time = datetime . now ( ) . strftime ( " % Y % m %d % H % M % S " )
2024-10-15 00:30:20 +08:00
shutil . copy (
2024-12-02 17:36:37 +08:00
self . configPath ,
2024-12-02 15:09:54 +08:00
os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , f ' { self . Name } _ { time } .conf ' )
2024-10-15 00:30:20 +08:00
)
2024-12-02 15:09:54 +08:00
with open ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , f ' { self . Name } _ { time } .sql ' ) , ' w+ ' ) as f :
2024-10-17 00:04:26 +08:00
for l in self . __dumpDatabase ( ) :
f . write ( l + " \n " )
2024-10-16 17:44:49 +08:00
2024-12-06 20:27:04 +08:00
return True , {
" filename " : f ' { self . Name } _ { time } .conf ' ,
" backupDate " : datetime . now ( ) . strftime ( " % Y % m %d % H % M % S " )
}
2024-12-30 20:30:09 +08:00
2024-10-25 00:19:27 +08:00
def getBackups ( self , databaseContent : bool = False ) - > list [ dict [ str : str , str : str , str : str ] ] :
2024-10-15 00:30:20 +08:00
backups = [ ]
2024-12-02 15:09:54 +08:00
directory = os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' )
2024-10-15 00:30:20 +08:00
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 )
for f , ct in files :
2024-11-25 22:11:51 +08:00
if RegexMatch ( f " ^( { self . Name } )_(.*) \\ .(conf)$ " , f ) :
2024-11-07 18:33:39 +08:00
s = re . search ( f " ^( { self . Name } )_(.*) \\ .(conf)$ " , f )
2024-10-15 00:30:20 +08:00
date = s . group ( 2 )
2024-10-25 00:19:27 +08:00
d = {
2024-10-15 00:30:20 +08:00
" filename " : f ,
" backupDate " : date ,
2024-12-02 15:09:54 +08:00
" content " : open ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , f ) , ' r ' ) . read ( )
2024-10-25 00:19:27 +08:00
}
if f . replace ( " .conf " , " .sql " ) in list ( os . listdir ( directory ) ) :
d [ ' database ' ] = True
if databaseContent :
2024-12-02 15:09:54 +08:00
d [ ' databaseContent ' ] = open ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , f . replace ( " .conf " , " .sql " ) ) , ' r ' ) . read ( )
2024-10-25 00:19:27 +08:00
backups . append ( d )
2024-10-15 00:30:20 +08:00
return backups
2024-10-16 17:44:49 +08:00
def restoreBackup ( self , backupFileName : str ) - > bool :
backups = list ( map ( lambda x : x [ ' filename ' ] , self . getBackups ( ) ) )
if backupFileName not in backups :
return False
if self . Status :
self . toggleConfiguration ( )
2024-12-02 15:09:54 +08:00
target = os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , backupFileName )
targetSQL = os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , backupFileName . replace ( " .conf " , " .sql " ) )
2024-10-16 17:44:49 +08:00
if not os . path . exists ( target ) :
return False
targetContent = open ( target , ' r ' ) . read ( )
try :
2024-12-02 17:36:37 +08:00
with open ( self . configPath , ' w ' ) as f :
2024-10-16 17:44:49 +08:00
f . write ( targetContent )
except Exception as e :
return False
self . __parseConfigurationFile ( )
2024-10-17 00:04:26 +08:00
self . __dropDatabase ( )
2024-10-25 23:34:07 +08:00
self . __importDatabase ( targetSQL )
2024-10-16 17:44:49 +08:00
self . __initPeersList ( )
return True
def deleteBackup ( self , backupFileName : str ) - > bool :
backups = list ( map ( lambda x : x [ ' filename ' ] , self . getBackups ( ) ) )
if backupFileName not in backups :
return False
try :
2024-12-02 15:09:54 +08:00
os . remove ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , backupFileName ) )
2024-10-16 17:44:49 +08:00
except Exception as e :
return False
return True
2024-10-15 00:30:20 +08:00
2025-03-16 00:42:40 +08:00
def downloadBackup ( self , backupFileName : str ) - > tuple [ bool , str ] | tuple [ bool , None ] :
2024-12-26 00:06:37 +08:00
backup = list ( filter ( lambda x : x [ ' filename ' ] == backupFileName , self . getBackups ( ) ) )
if len ( backup ) == 0 :
return False , None
zip = f ' { str ( uuid . UUID ( int = random . Random ( ) . getrandbits ( 128 ) , version = 4 ) ) } .zip '
with ZipFile ( os . path . join ( ' download ' , zip ) , ' w ' ) as zipF :
zipF . write (
os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , backup [ 0 ] [ ' filename ' ] ) ,
os . path . basename ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , backup [ 0 ] [ ' filename ' ] ) )
)
if backup [ 0 ] [ ' database ' ] :
zipF . write (
os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , backup [ 0 ] [ ' filename ' ] . replace ( ' .conf ' , ' .sql ' ) ) ,
os . path . basename ( os . path . join ( self . __getProtocolPath ( ) , ' WGDashboard_Backup ' , backup [ 0 ] [ ' filename ' ] . replace ( ' .conf ' , ' .sql ' ) ) )
)
return True , zip
2024-10-04 16:58:47 +08:00
def updateConfigurationSettings ( self , newData : dict ) - > tuple [ bool , str ] :
if self . Status :
self . toggleConfiguration ( )
original = [ ]
dataChanged = False
2024-12-02 17:36:37 +08:00
with open ( self . configPath , ' r ' ) as f :
2024-11-26 21:27:32 +08:00
original = [ l . rstrip ( " \n " ) for l in f . readlines ( ) ]
2025-04-24 18:51:26 +03:30
allowEdit = [ " Address " , " PreUp " , " PostUp " , " PreDown " , " PostDown " , " ListenPort " , " Table " ]
2024-12-05 01:50:31 +08:00
if self . Protocol == ' awg ' :
allowEdit + = [ " Jc " , " Jmin " , " Jmax " , " S1 " , " S2 " , " H1 " , " H2 " , " H3 " , " H4 " ]
2024-10-04 16:58:47 +08:00
start = original . index ( " [Interface] " )
2024-11-26 21:27:32 +08:00
try :
end = original . index ( " [Peer] " )
except ValueError as e :
end = len ( original )
new = [ " [Interface] " ]
peerFound = False
for line in range ( start , end ) :
2024-10-04 16:58:47 +08:00
split = re . split ( r ' \ s*= \ s* ' , original [ line ] , 1 )
if len ( split ) == 2 :
2024-11-26 21:27:32 +08:00
if split [ 0 ] not in allowEdit :
new . append ( original [ line ] )
for key in allowEdit :
2024-12-05 01:50:31 +08:00
new . insert ( 1 , f " { key } = { str ( newData [ key ] ) . strip ( ) } " )
2024-11-26 21:27:32 +08:00
new . append ( " " )
for line in range ( end , len ( original ) ) :
new . append ( original [ line ] )
2024-10-15 00:30:20 +08:00
self . backupConfigurationFile ( )
2024-12-02 17:36:37 +08:00
with open ( self . configPath , ' w ' ) as f :
2024-11-26 21:27:32 +08:00
f . write ( " \n " . join ( new ) )
2024-10-04 16:58:47 +08:00
status , msg = self . toggleConfiguration ( )
if not status :
return False , msg
2024-12-30 20:30:09 +08:00
for i in allowEdit :
if isinstance ( getattr ( self , i ) , bool ) :
setattr ( self , i , _strToBool ( newData [ i ] ) )
else :
setattr ( self , i , str ( newData [ i ] ) )
2024-10-04 16:58:47 +08:00
return True , " "
2024-10-25 00:19:27 +08:00
def deleteConfiguration ( self ) :
if self . getStatus ( ) :
self . toggleConfiguration ( )
2024-12-02 17:36:37 +08:00
os . remove ( self . configPath )
2024-10-25 00:19:27 +08:00
self . __dropDatabase ( )
return True
2024-11-06 18:36:55 +08:00
def renameConfiguration ( self , newConfigurationName ) - > tuple [ bool , str ] :
if newConfigurationName in WireguardConfigurations . keys ( ) :
return False , " Configuration name already exist "
try :
if self . getStatus ( ) :
self . toggleConfiguration ( )
2024-12-02 17:36:37 +08:00
self . createDatabase ( newConfigurationName )
2024-11-06 18:36:55 +08:00
sqlUpdate ( f ' INSERT INTO " { newConfigurationName } " SELECT * FROM " { self . Name } " ' )
sqlUpdate ( f ' INSERT INTO " { newConfigurationName } _restrict_access " SELECT * FROM " { self . Name } _restrict_access " ' )
sqlUpdate ( f ' INSERT INTO " { newConfigurationName } _deleted " SELECT * FROM " { self . Name } _deleted " ' )
sqlUpdate ( f ' INSERT INTO " { newConfigurationName } _transfer " SELECT * FROM " { self . Name } _transfer " ' )
AllPeerJobs . updateJobConfigurationName ( self . Name , newConfigurationName )
shutil . copy (
2024-12-02 17:36:37 +08:00
self . configPath ,
2024-12-02 15:09:54 +08:00
os . path . join ( self . __getProtocolPath ( ) , f ' { newConfigurationName } .conf ' )
2024-11-06 18:36:55 +08:00
)
self . deleteConfiguration ( )
except Exception as e :
return False , str ( e )
return True , None
2024-11-25 22:11:51 +08:00
2025-02-16 17:42:32 +08:00
def getNumberOfAvailableIP ( self ) :
2024-11-25 22:11:51 +08:00
if len ( self . Address ) < 0 :
return False , None
2025-02-16 17:42:32 +08:00
existedAddress = set ( )
availableAddress = { }
for p in self . Peers + self . getRestrictedPeersList ( ) :
peerAllowedIP = p . allowed_ip . split ( ' , ' )
for pip in peerAllowedIP :
ppip = pip . strip ( ) . split ( ' / ' )
if len ( ppip ) == 2 :
2024-11-25 22:11:51 +08:00
try :
2025-02-16 17:42:32 +08:00
check = ipaddress . ip_network ( ppip [ 0 ] )
existedAddress . add ( check )
except Exception as e :
2024-12-26 00:06:37 +08:00
print ( f " [WGDashboard] Error: { self . Name } peer { p . id } have invalid ip " )
2025-02-16 17:42:32 +08:00
configurationAddresses = self . Address . split ( ' , ' )
for ca in configurationAddresses :
ca = ca . strip ( )
caSplit = ca . split ( ' / ' )
try :
if len ( caSplit ) == 2 :
network = ipaddress . ip_network ( ca , False )
existedAddress . add ( ipaddress . ip_network ( caSplit [ 0 ] ) )
availableAddress [ ca ] = network . num_addresses
for p in existedAddress :
2025-02-16 17:43:22 +08:00
if p . version == network . version and p . subnet_of ( network ) :
2025-02-16 23:07:16 +08:00
availableAddress [ ca ] - = 1
2025-02-16 17:42:32 +08:00
except Exception as e :
print ( e )
print ( f " [WGDashboard] Error: Failed to parse IP address { ca } from { self . Name } " )
return True , availableAddress
2025-02-16 23:07:16 +08:00
def getAvailableIP ( self , threshold = 255 ) :
2025-02-16 17:42:32 +08:00
if len ( self . Address ) < 0 :
return False , None
existedAddress = set ( )
availableAddress = { }
for p in self . Peers + self . getRestrictedPeersList ( ) :
peerAllowedIP = p . allowed_ip . split ( ' , ' )
for pip in peerAllowedIP :
ppip = pip . strip ( ) . split ( ' / ' )
if len ( ppip ) == 2 :
try :
check = ipaddress . ip_network ( ppip [ 0 ] )
2025-02-17 15:25:33 +08:00
existedAddress . add ( check . compressed )
2025-02-16 17:42:32 +08:00
except Exception as e :
print ( f " [WGDashboard] Error: { self . Name } peer { p . id } have invalid ip " )
configurationAddresses = self . Address . split ( ' , ' )
for ca in configurationAddresses :
ca = ca . strip ( )
caSplit = ca . split ( ' / ' )
try :
if len ( caSplit ) == 2 :
network = ipaddress . ip_network ( ca , False )
existedAddress . add ( ipaddress . ip_network ( caSplit [ 0 ] ) . compressed )
2025-02-16 23:07:16 +08:00
if threshold == - 1 :
availableAddress [ ca ] = filter ( lambda ip : ip not in existedAddress ,
map ( lambda iph : ipaddress . ip_network ( iph ) . compressed , network . hosts ( ) ) )
else :
availableAddress [ ca ] = list ( islice ( filter ( lambda ip : ip not in existedAddress ,
map ( lambda iph : ipaddress . ip_network ( iph ) . compressed , network . hosts ( ) ) ) , threshold ) )
2025-02-16 17:42:32 +08:00
except Exception as e :
print ( e )
print ( f " [WGDashboard] Error: Failed to parse IP address { ca } from { self . Name } " )
2025-02-16 23:07:16 +08:00
print ( " Generated IP " )
2024-11-25 22:11:51 +08:00
return True , availableAddress
2024-12-30 20:30:09 +08:00
def getRealtimeTrafficUsage ( self ) :
stats = psutil . net_io_counters ( pernic = True , nowrap = True )
if self . Name in stats . keys ( ) :
stat = stats [ self . Name ]
recv1 = stat . bytes_recv
sent1 = stat . bytes_sent
time . sleep ( 1 )
stats = psutil . net_io_counters ( pernic = True , nowrap = True )
if self . Name in stats . keys ( ) :
stat = stats [ self . Name ]
recv2 = stat . bytes_recv
sent2 = stat . bytes_sent
net_in = round ( ( recv2 - recv1 ) / 1024 / 1024 , 3 )
net_out = round ( ( sent2 - sent1 ) / 1024 / 1024 , 3 )
return {
" sent " : net_out ,
" recv " : net_in
}
else :
return { " sent " : 0 , " recv " : 0 }
else :
return { " sent " : 0 , " recv " : 0 }
2024-12-02 15:09:54 +08:00
"""
AmneziaWG Configuration
"""
class AmneziaWireguardConfiguration ( WireguardConfiguration ) :
def __init__ ( self , name : str = None , data : dict = None , backup : dict = None , startup : bool = False ) :
self . Jc = 0
self . Jmin = 0
self . Jmax = 0
self . S1 = 0
self . S2 = 0
self . H1 = 1
self . H2 = 2
self . H3 = 3
self . H4 = 4
super ( ) . __init__ ( name , data , backup , startup , wg = False )
2024-12-02 16:34:26 +08:00
def toJson ( self ) :
self . Status = self . getStatus ( )
return {
" Status " : self . Status ,
" Name " : self . Name ,
" PrivateKey " : self . PrivateKey ,
" PublicKey " : self . PublicKey ,
" Address " : self . Address ,
" ListenPort " : self . ListenPort ,
" PreUp " : self . PreUp ,
" PreDown " : self . PreDown ,
" PostUp " : self . PostUp ,
" PostDown " : self . PostDown ,
" SaveConfig " : self . SaveConfig ,
" DataUsage " : {
" Total " : sum ( list ( map ( lambda x : x . cumu_data + x . total_data , self . Peers ) ) ) ,
" Sent " : sum ( list ( map ( lambda x : x . cumu_sent + x . total_sent , self . Peers ) ) ) ,
" Receive " : sum ( list ( map ( lambda x : x . cumu_receive + x . total_receive , self . Peers ) ) )
} ,
" ConnectedPeers " : len ( list ( filter ( lambda x : x . status == " running " , self . Peers ) ) ) ,
" TotalPeers " : len ( self . Peers ) ,
" Protocol " : self . Protocol ,
" Jc " : self . Jc ,
" Jmin " : self . Jmin ,
" Jmax " : self . Jmax ,
" S1 " : self . S1 ,
" S2 " : self . S2 ,
" H1 " : self . H1 ,
" H2 " : self . H2 ,
" H3 " : self . H3 ,
" H4 " : self . H4
}
2024-12-02 17:36:37 +08:00
def createDatabase ( self , dbName = None ) :
if dbName is None :
dbName = self . Name
existingTables = sqlSelect ( " SELECT name FROM sqlite_master WHERE type= ' table ' " ) . fetchall ( )
existingTables = [ t [ ' name ' ] for t in existingTables ]
if dbName not in existingTables :
sqlUpdate (
"""
CREATE TABLE ' %s ' (
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL , advanced_security VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
PRIMARY KEY ( id )
)
""" % d bName
)
if f ' { dbName } _restrict_access ' not in existingTables :
sqlUpdate (
"""
CREATE TABLE ' %s _restrict_access ' (
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL , advanced_security VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
PRIMARY KEY ( id )
)
""" % d bName
)
if f ' { dbName } _transfer ' not in existingTables :
sqlUpdate (
"""
CREATE TABLE ' %s _transfer ' (
id VARCHAR NOT NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , time DATETIME
)
""" % d bName
)
if f ' { dbName } _deleted ' not in existingTables :
sqlUpdate (
"""
CREATE TABLE ' %s _deleted ' (
id VARCHAR NOT NULL , private_key VARCHAR NULL , DNS VARCHAR NULL , advanced_security VARCHAR NULL ,
endpoint_allowed_ip VARCHAR NULL , name VARCHAR NULL , total_receive FLOAT NULL ,
total_sent FLOAT NULL , total_data FLOAT NULL , endpoint VARCHAR NULL ,
status VARCHAR NULL , latest_handshake VARCHAR NULL , allowed_ip VARCHAR NULL ,
cumu_receive FLOAT NULL , cumu_sent FLOAT NULL , cumu_data FLOAT NULL , mtu INT NULL ,
keepalive INT NULL , remote_endpoint VARCHAR NULL , preshared_key VARCHAR NULL ,
PRIMARY KEY ( id )
)
""" % d bName
)
def getPeers ( self ) :
if self . configurationFileChanged ( ) :
self . Peers = [ ]
with open ( self . configPath , ' r ' ) as configFile :
p = [ ]
pCounter = - 1
content = configFile . read ( ) . split ( ' \n ' )
try :
peerStarts = content . index ( " [Peer] " )
content = content [ peerStarts : ]
for i in content :
if not RegexMatch ( " #(.*) " , i ) and not RegexMatch ( " ;(.*) " , i ) :
if i == " [Peer] " :
pCounter + = 1
p . append ( { } )
p [ pCounter ] [ " name " ] = " "
else :
if len ( i ) > 0 :
split = re . split ( r ' \ s*= \ s* ' , i , 1 )
if len ( split ) == 2 :
p [ pCounter ] [ split [ 0 ] ] = split [ 1 ]
if RegexMatch ( " #Name# = (.*) " , i ) :
split = re . split ( r ' \ s*= \ s* ' , i , 1 )
if len ( split ) == 2 :
p [ pCounter ] [ " name " ] = split [ 1 ]
for i in p :
if " PublicKey " in i . keys ( ) :
checkIfExist = sqlSelect ( " SELECT * FROM ' %s ' WHERE id = ? " % self . Name ,
( ( i [ ' PublicKey ' ] ) , ) ) . fetchone ( )
if checkIfExist is None :
newPeer = {
" id " : i [ ' PublicKey ' ] ,
" advanced_security " : i . get ( ' AdvancedSecurity ' , ' off ' ) ,
" private_key " : " " ,
" DNS " : DashboardConfig . GetConfig ( " Peers " , " peer_global_DNS " ) [ 1 ] ,
" endpoint_allowed_ip " : DashboardConfig . GetConfig ( " Peers " , " peer_endpoint_allowed_ip " ) [
1 ] ,
" name " : i . get ( " name " ) ,
" total_receive " : 0 ,
" total_sent " : 0 ,
" total_data " : 0 ,
" endpoint " : " N/A " ,
" status " : " stopped " ,
" latest_handshake " : " N/A " ,
" allowed_ip " : i . get ( " AllowedIPs " , " N/A " ) ,
" cumu_receive " : 0 ,
" cumu_sent " : 0 ,
" cumu_data " : 0 ,
" traffic " : [ ] ,
" mtu " : DashboardConfig . GetConfig ( " Peers " , " peer_mtu " ) [ 1 ] ,
" keepalive " : DashboardConfig . GetConfig ( " Peers " , " peer_keep_alive " ) [ 1 ] ,
" remote_endpoint " : DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] ,
" preshared_key " : i [ " PresharedKey " ] if " PresharedKey " in i . keys ( ) else " "
}
sqlUpdate (
"""
INSERT INTO ' %s '
VALUES ( : id , : private_key , : DNS , : advanced_security , : endpoint_allowed_ip , : name , : total_receive , : total_sent ,
: total_data , : endpoint , : status , : latest_handshake , : allowed_ip , : cumu_receive , : cumu_sent ,
: cumu_data , : mtu , : keepalive , : remote_endpoint , : preshared_key ) ;
""" % s elf.Name
, newPeer )
2024-12-03 02:34:45 +08:00
self . Peers . append ( AmneziaWGPeer ( newPeer , self ) )
2024-12-02 17:36:37 +08:00
else :
sqlUpdate ( " UPDATE ' %s ' SET allowed_ip = ? WHERE id = ? " % self . Name ,
( i . get ( " AllowedIPs " , " N/A " ) , i [ ' PublicKey ' ] , ) )
2024-12-03 02:34:45 +08:00
self . Peers . append ( AmneziaWGPeer ( checkIfExist , self ) )
2024-12-02 17:36:37 +08:00
except Exception as e :
if __name__ == ' __main__ ' :
print ( f " [WGDashboard] { self . Name } Error: { str ( e ) } " )
else :
self . Peers . clear ( )
checkIfExist = sqlSelect ( " SELECT * FROM ' %s ' " % self . Name ) . fetchall ( )
for i in checkIfExist :
2024-12-03 02:34:45 +08:00
self . Peers . append ( AmneziaWGPeer ( i , self ) )
2025-02-12 20:07:37 +08:00
def addPeers ( self , peers : list ) - > tuple [ bool , dict ] :
result = {
" message " : None ,
" peers " : [ ]
}
2024-12-03 02:34:45 +08:00
try :
for i in peers :
newPeer = {
" id " : i [ ' id ' ] ,
" private_key " : i [ ' private_key ' ] ,
" DNS " : i [ ' DNS ' ] ,
" endpoint_allowed_ip " : i [ ' endpoint_allowed_ip ' ] ,
" name " : i [ ' name ' ] ,
" total_receive " : 0 ,
" total_sent " : 0 ,
" total_data " : 0 ,
" endpoint " : " N/A " ,
" status " : " stopped " ,
" latest_handshake " : " N/A " ,
" allowed_ip " : i . get ( " allowed_ip " , " N/A " ) ,
" cumu_receive " : 0 ,
" cumu_sent " : 0 ,
" cumu_data " : 0 ,
" traffic " : [ ] ,
" mtu " : i [ ' mtu ' ] ,
" keepalive " : i [ ' keepalive ' ] ,
" remote_endpoint " : DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] ,
" preshared_key " : i [ " preshared_key " ] ,
" advanced_security " : i [ ' advanced_security ' ]
}
sqlUpdate (
"""
INSERT INTO ' %s '
VALUES ( : id , : private_key , : DNS , : advanced_security , : endpoint_allowed_ip , : name , : total_receive , : total_sent ,
: total_data , : endpoint , : status , : latest_handshake , : allowed_ip , : cumu_receive , : cumu_sent ,
: cumu_data , : mtu , : keepalive , : remote_endpoint , : preshared_key ) ;
""" % s elf.Name
, newPeer )
for p in peers :
presharedKeyExist = len ( p [ ' preshared_key ' ] ) > 0
rd = random . Random ( )
uid = str ( uuid . UUID ( int = rd . getrandbits ( 128 ) , version = 4 ) )
if presharedKeyExist :
with open ( uid , " w+ " ) as f :
f . write ( p [ ' preshared_key ' ] )
subprocess . check_output (
2025-05-01 22:40:53 +08:00
f " { self . Protocol } set { self . Name } peer { p [ ' id ' ] } allowed-ips { p [ ' allowed_ip ' ] . replace ( ' ' , ' ' ) } { f ' preshared-key { uid } ' if presharedKeyExist else ' ' } " ,
2024-12-03 02:34:45 +08:00
shell = True , stderr = subprocess . STDOUT )
if presharedKeyExist :
os . remove ( uid )
subprocess . check_output (
f " { self . Protocol } -quick save { self . Name } " , shell = True , stderr = subprocess . STDOUT )
self . getPeersList ( )
2025-02-12 20:07:37 +08:00
for p in peers :
p = self . searchPeer ( p [ ' id ' ] )
if p [ 0 ] :
result [ ' peers ' ] . append ( p [ 1 ] )
return True , result
2024-12-03 02:34:45 +08:00
except Exception as e :
2025-02-12 20:07:37 +08:00
result [ ' message ' ] = str ( e )
return False , result
2024-12-03 02:34:45 +08:00
def getRestrictedPeers ( self ) :
self . RestrictedPeers = [ ]
restricted = sqlSelect ( " SELECT * FROM ' %s _restrict_access ' " % self . Name ) . fetchall ( )
for i in restricted :
self . RestrictedPeers . append ( AmneziaWGPeer ( i , self ) )
2024-12-02 17:36:37 +08:00
2024-11-25 22:11:51 +08:00
"""
Peer
"""
2024-06-18 03:16:42 +08:00
class Peer :
def __init__ ( self , tableData , configuration : WireguardConfiguration ) :
self . configuration = configuration
self . id = tableData [ " id " ]
self . private_key = tableData [ " private_key " ]
self . DNS = tableData [ " DNS " ]
self . endpoint_allowed_ip = tableData [ " endpoint_allowed_ip " ]
self . name = tableData [ " name " ]
self . total_receive = tableData [ " total_receive " ]
self . total_sent = tableData [ " total_sent " ]
self . total_data = tableData [ " total_data " ]
self . endpoint = tableData [ " endpoint " ]
self . status = tableData [ " status " ]
self . latest_handshake = tableData [ " latest_handshake " ]
self . allowed_ip = tableData [ " allowed_ip " ]
self . cumu_receive = tableData [ " cumu_receive " ]
self . cumu_sent = tableData [ " cumu_sent " ]
self . cumu_data = tableData [ " cumu_data " ]
self . mtu = tableData [ " mtu " ]
self . keepalive = tableData [ " keepalive " ]
self . remote_endpoint = tableData [ " remote_endpoint " ]
self . preshared_key = tableData [ " preshared_key " ]
self . jobs : list [ PeerJob ] = [ ]
2024-08-06 10:17:14 -04:00
self . ShareLink : list [ PeerShareLink ] = [ ]
2024-06-18 03:16:42 +08:00
self . getJobs ( )
2024-08-06 10:17:14 -04:00
self . getShareLink ( )
2024-06-18 03:16:42 +08:00
def toJson ( self ) :
2024-06-25 23:02:13 +08:00
self . getJobs ( )
2024-08-06 10:17:14 -04:00
self . getShareLink ( )
2024-06-18 03:16:42 +08:00
return self . __dict__
def __repr__ ( self ) :
return str ( self . toJson ( ) )
def updatePeer ( self , name : str , private_key : str ,
preshared_key : str ,
dns_addresses : str , allowed_ip : str , endpoint_allowed_ip : str , mtu : int ,
keepalive : int ) - > ResponseObject :
2024-08-08 23:27:13 -04:00
if not self . configuration . getStatus ( ) :
self . configuration . toggleConfiguration ( )
2024-06-18 03:16:42 +08:00
existingAllowedIps = [ item for row in list (
map ( lambda x : [ q . strip ( ) for q in x . split ( ' , ' ) ] ,
map ( lambda y : y . allowed_ip ,
list ( filter ( lambda k : k . id != self . id , self . configuration . getPeersList ( ) ) ) ) ) ) for item in row ]
if allowed_ip in existingAllowedIps :
2024-09-22 16:33:22 +08:00
return ResponseObject ( False , " Allowed IP already taken by another peer " )
2024-11-25 22:11:51 +08:00
if not ValidateIPAddressesWithRange ( endpoint_allowed_ip ) :
2024-09-22 16:33:22 +08:00
return ResponseObject ( False , f " Endpoint Allowed IPs format is incorrect " )
2024-11-25 22:11:51 +08:00
if len ( dns_addresses ) > 0 and not ValidateDNSAddress ( dns_addresses ) :
2024-09-22 16:33:22 +08:00
return ResponseObject ( False , f " DNS format is incorrect " )
2024-06-18 03:16:42 +08:00
if mtu < 0 or mtu > 1460 :
2024-09-22 16:33:22 +08:00
return ResponseObject ( False , " MTU format is not correct " )
2024-06-18 03:16:42 +08:00
if keepalive < 0 :
2024-09-22 16:33:22 +08:00
return ResponseObject ( False , " Persistent Keepalive format is not correct " )
2024-06-18 03:16:42 +08:00
if len ( private_key ) > 0 :
2024-11-25 22:11:51 +08:00
pubKey = GenerateWireguardPublicKey ( private_key )
2024-06-18 03:16:42 +08:00
if not pubKey [ 0 ] or pubKey [ 1 ] != self . id :
2024-09-22 16:33:22 +08:00
return ResponseObject ( False , " Private key does not match with the public key " )
2024-06-18 03:16:42 +08:00
try :
2024-09-24 22:54:18 +08:00
rd = random . Random ( )
2024-11-15 13:45:14 +08:00
uid = str ( uuid . UUID ( int = rd . getrandbits ( 128 ) , version = 4 ) )
2024-09-24 22:54:18 +08:00
pskExist = len ( preshared_key ) > 0
if pskExist :
2024-11-15 13:45:14 +08:00
with open ( uid , " w+ " ) as f :
2024-06-18 03:16:42 +08:00
f . write ( preshared_key )
2024-09-24 22:54:18 +08:00
newAllowedIPs = allowed_ip . replace ( " " , " " )
2024-06-18 03:16:42 +08:00
updateAllowedIp = subprocess . check_output (
2024-12-07 21:59:03 +08:00
f " { self . configuration . Protocol } set { self . configuration . Name } peer { self . id } allowed-ips { newAllowedIPs } { f ' preshared-key { uid } ' if pskExist else ' preshared-key /dev/null ' } " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
2024-09-24 22:54:18 +08:00
2025-01-24 00:01:29 +08:00
if pskExist : os . remove ( uid )
2024-06-18 03:16:42 +08:00
if len ( updateAllowedIp . decode ( ) . strip ( " \n " ) ) != 0 :
return ResponseObject ( False ,
2024-09-22 16:44:36 +08:00
" Update peer failed when updating Allowed IPs " )
2024-12-02 16:34:26 +08:00
saveConfig = subprocess . check_output ( f " { self . configuration . Protocol } -quick save { self . configuration . Name } " ,
2024-06-18 03:16:42 +08:00
shell = True , stderr = subprocess . STDOUT )
if f " wg showconf { self . configuration . Name } " not in saveConfig . decode ( ) . strip ( ' \n ' ) :
return ResponseObject ( False ,
2024-09-22 16:44:36 +08:00
" Update peer failed when saving the configuration " )
2024-08-20 00:02:00 -07:00
sqlUpdate (
2024-08-11 19:20:42 -04:00
''' UPDATE ' %s ' SET name = ?, private_key = ?, DNS = ?, endpoint_allowed_ip = ?, mtu = ?,
2024-06-18 03:16:42 +08:00
keepalive = ? , preshared_key = ? WHERE id = ? ''' % s elf.configuration.Name,
( name , private_key , dns_addresses , endpoint_allowed_ip , mtu ,
keepalive , preshared_key , self . id , )
)
return ResponseObject ( )
except subprocess . CalledProcessError as exc :
return ResponseObject ( False , exc . output . decode ( " UTF-8 " ) . strip ( ) )
2021-08-14 17:13:16 -04:00
2024-06-18 03:16:42 +08:00
def downloadPeer ( self ) - > dict [ str , str ] :
filename = self . name
if len ( filename ) == 0 :
filename = " UntitledPeer "
filename = " " . join ( filename . split ( ' ' ) )
2025-01-16 18:36:06 +08:00
filename = f " { filename } "
2024-06-18 03:16:42 +08:00
illegal_filename = [ " . " , " , " , " / " , " ? " , " < " , " > " , " \\ " , " : " , " * " , ' | ' ' \" ' , " com1 " , " com2 " , " com3 " ,
" com4 " , " com5 " , " com6 " , " com7 " , " com8 " , " com9 " , " lpt1 " , " lpt2 " , " lpt3 " , " lpt4 " ,
" lpt5 " , " lpt6 " , " lpt7 " , " lpt8 " , " lpt9 " , " con " , " nul " , " prn " ]
for i in illegal_filename :
filename = filename . replace ( i , " " )
2025-01-16 18:36:06 +08:00
finalFilename = " "
for i in filename :
2025-02-12 20:07:37 +08:00
if re . match ( " ^[a-zA-Z0-9_=+.-]$ " , i ) :
2025-01-16 18:36:06 +08:00
finalFilename + = i
2024-06-18 03:16:42 +08:00
peerConfiguration = f ''' [Interface]
PrivateKey = { self . private_key }
Address = { self . allowed_ip }
MTU = { str ( self . mtu ) }
'''
if len ( self . DNS ) > 0 :
peerConfiguration + = f " DNS = { self . DNS } \n "
2024-12-03 02:34:45 +08:00
2024-06-18 03:16:42 +08:00
peerConfiguration + = f '''
[ Peer ]
PublicKey = { self . configuration . PublicKey }
AllowedIPs = { self . endpoint_allowed_ip }
Endpoint = { DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] } : { self . configuration . ListenPort }
PersistentKeepalive = { str ( self . keepalive ) }
'''
if len ( self . preshared_key ) > 0 :
peerConfiguration + = f " PresharedKey = { self . preshared_key } \n "
return {
2025-01-16 18:36:06 +08:00
" fileName " : finalFilename ,
2024-06-18 03:16:42 +08:00
" file " : peerConfiguration
}
2022-01-02 14:44:27 -05:00
2024-06-18 03:16:42 +08:00
def getJobs ( self ) :
self . jobs = AllPeerJobs . searchJob ( self . configuration . Name , self . id )
2024-08-06 10:17:14 -04:00
def getShareLink ( self ) :
self . ShareLink = AllPeerShareLinks . getLink ( self . configuration . Name , self . id )
2024-08-08 23:27:13 -04:00
def resetDataUsage ( self , type ) :
try :
if type == " total " :
2024-08-20 00:02:00 -07:00
sqlUpdate ( " UPDATE ' %s ' SET total_data = 0, cumu_data = 0, total_receive = 0, cumu_receive = 0, total_sent = 0, cumu_sent = 0 WHERE id = ? " % self . configuration . Name , ( self . id , ) )
2025-03-07 00:37:01 +08:00
self . total_data = 0
self . total_receive = 0
self . total_sent = 0
self . cumu_data = 0
self . cumu_sent = 0
self . cumu_receive = 0
2024-08-08 23:27:13 -04:00
elif type == " receive " :
2024-08-20 00:02:00 -07:00
sqlUpdate ( " UPDATE ' %s ' SET total_receive = 0, cumu_receive = 0 WHERE id = ? " % self . configuration . Name , ( self . id , ) )
2025-03-07 00:37:01 +08:00
self . cumu_receive = 0
self . total_receive = 0
2024-08-08 23:27:13 -04:00
elif type == " sent " :
2024-08-20 00:02:00 -07:00
sqlUpdate ( " UPDATE ' %s ' SET total_sent = 0, cumu_sent = 0 WHERE id = ? " % self . configuration . Name , ( self . id , ) )
2025-03-07 00:37:01 +08:00
self . cumu_sent = 0
self . total_sent = 0
2024-08-08 23:27:13 -04:00
else :
return False
except Exception as e :
2025-03-07 00:37:01 +08:00
print ( e )
2024-08-08 23:27:13 -04:00
return False
2025-03-07 00:52:51 +08:00
2024-08-08 23:27:13 -04:00
return True
2024-12-03 02:34:45 +08:00
class AmneziaWGPeer ( Peer ) :
def __init__ ( self , tableData , configuration : AmneziaWireguardConfiguration ) :
self . advanced_security = tableData [ " advanced_security " ]
super ( ) . __init__ ( tableData , configuration )
def downloadPeer ( self ) - > dict [ str , str ] :
filename = self . name
if len ( filename ) == 0 :
filename = " UntitledPeer "
filename = " " . join ( filename . split ( ' ' ) )
filename = f " { filename } _ { self . configuration . Name } "
illegal_filename = [ " . " , " , " , " / " , " ? " , " < " , " > " , " \\ " , " : " , " * " , ' | ' ' \" ' , " com1 " , " com2 " , " com3 " ,
" com4 " , " com5 " , " com6 " , " com7 " , " com8 " , " com9 " , " lpt1 " , " lpt2 " , " lpt3 " , " lpt4 " ,
" lpt5 " , " lpt6 " , " lpt7 " , " lpt8 " , " lpt9 " , " con " , " nul " , " prn " ]
for i in illegal_filename :
filename = filename . replace ( i , " " )
2025-02-12 20:07:37 +08:00
finalFilename = " "
for i in filename :
if re . match ( " ^[a-zA-Z0-9_=+.-]$ " , i ) :
finalFilename + = i
2024-12-03 02:34:45 +08:00
peerConfiguration = f ''' [Interface]
PrivateKey = { self . private_key }
Address = { self . allowed_ip }
MTU = { str ( self . mtu ) }
Jc = { self . configuration . Jc }
Jmin = { self . configuration . Jmin }
Jmax = { self . configuration . Jmax }
S1 = { self . configuration . S1 }
S2 = { self . configuration . S2 }
H1 = { self . configuration . H1 }
H2 = { self . configuration . H2 }
H3 = { self . configuration . H3 }
H4 = { self . configuration . H4 }
'''
if len ( self . DNS ) > 0 :
peerConfiguration + = f " DNS = { self . DNS } \n "
peerConfiguration + = f '''
[ Peer ]
PublicKey = { self . configuration . PublicKey }
AllowedIPs = { self . endpoint_allowed_ip }
Endpoint = { DashboardConfig . GetConfig ( " Peers " , " remote_endpoint " ) [ 1 ] } : { self . configuration . ListenPort }
PersistentKeepalive = { str ( self . keepalive ) }
'''
if len ( self . preshared_key ) > 0 :
peerConfiguration + = f " PresharedKey = { self . preshared_key } \n "
return {
2025-02-12 20:07:37 +08:00
" fileName " : finalFilename ,
2024-12-03 02:34:45 +08:00
" file " : peerConfiguration
}
def updatePeer ( self , name : str , private_key : str ,
preshared_key : str ,
dns_addresses : str , allowed_ip : str , endpoint_allowed_ip : str , mtu : int ,
keepalive : int , advanced_security : str ) - > ResponseObject :
if not self . configuration . getStatus ( ) :
self . configuration . toggleConfiguration ( )
existingAllowedIps = [ item for row in list (
map ( lambda x : [ q . strip ( ) for q in x . split ( ' , ' ) ] ,
map ( lambda y : y . allowed_ip ,
list ( filter ( lambda k : k . id != self . id , self . configuration . getPeersList ( ) ) ) ) ) ) for item in row ]
if allowed_ip in existingAllowedIps :
return ResponseObject ( False , " Allowed IP already taken by another peer " )
if not ValidateIPAddressesWithRange ( endpoint_allowed_ip ) :
return ResponseObject ( False , f " Endpoint Allowed IPs format is incorrect " )
if len ( dns_addresses ) > 0 and not ValidateDNSAddress ( dns_addresses ) :
return ResponseObject ( False , f " DNS format is incorrect " )
if mtu < 0 or mtu > 1460 :
return ResponseObject ( False , " MTU format is not correct " )
if keepalive < 0 :
return ResponseObject ( False , " Persistent Keepalive format is not correct " )
if advanced_security != " on " and advanced_security != " off " :
return ResponseObject ( False , " Advanced Security can only be on or off " )
if len ( private_key ) > 0 :
pubKey = GenerateWireguardPublicKey ( private_key )
if not pubKey [ 0 ] or pubKey [ 1 ] != self . id :
return ResponseObject ( False , " Private key does not match with the public key " )
try :
rd = random . Random ( )
uid = str ( uuid . UUID ( int = rd . getrandbits ( 128 ) , version = 4 ) )
pskExist = len ( preshared_key ) > 0
if pskExist :
with open ( uid , " w+ " ) as f :
f . write ( preshared_key )
newAllowedIPs = allowed_ip . replace ( " " , " " )
updateAllowedIp = subprocess . check_output (
2025-05-01 22:40:53 +08:00
f " { self . configuration . Protocol } set { self . configuration . Name } peer { self . id } allowed-ips { newAllowedIPs } { f ' preshared-key { uid } ' if pskExist else ' preshared-key /dev/null ' } " ,
2024-12-03 02:34:45 +08:00
shell = True , stderr = subprocess . STDOUT )
if pskExist : os . remove ( uid )
2024-11-23 17:30:22 +08:00
2024-12-03 02:34:45 +08:00
if len ( updateAllowedIp . decode ( ) . strip ( " \n " ) ) != 0 :
return ResponseObject ( False ,
" Update peer failed when updating Allowed IPs " )
saveConfig = subprocess . check_output ( f " { self . configuration . Protocol } -quick save { self . configuration . Name } " ,
shell = True , stderr = subprocess . STDOUT )
if f " wg showconf { self . configuration . Name } " not in saveConfig . decode ( ) . strip ( ' \n ' ) :
return ResponseObject ( False ,
" Update peer failed when saving the configuration " )
sqlUpdate (
''' UPDATE ' %s ' SET name = ?, private_key = ?, DNS = ?, endpoint_allowed_ip = ?, mtu = ?,
keepalive = ? , preshared_key = ? , advanced_security = ? WHERE id = ? ''' % s elf.configuration.Name,
( name , private_key , dns_addresses , endpoint_allowed_ip , mtu ,
keepalive , preshared_key , advanced_security , self . id , )
)
return ResponseObject ( )
except subprocess . CalledProcessError as exc :
return ResponseObject ( False , exc . output . decode ( " UTF-8 " ) . strip ( ) )
2024-06-18 03:16:42 +08:00
2024-11-25 22:11:51 +08:00
"""
Dashboard Configuration
"""
2024-06-18 03:16:42 +08:00
class DashboardConfig :
def __init__ ( self ) :
if not os . path . exists ( DASHBOARD_CONF ) :
open ( DASHBOARD_CONF , " x " )
self . __config = configparser . ConfigParser ( strict = False )
self . __config . read_file ( open ( DASHBOARD_CONF , " r+ " ) )
2024-11-24 00:22:33 +08:00
self . hiddenAttribute = [ " totp_key " , " auth_req " ]
2024-06-18 03:16:42 +08:00
self . __default = {
" Account " : {
" username " : " admin " ,
" password " : " admin " ,
" enable_totp " : " false " ,
2024-08-05 15:39:11 -04:00
" totp_verified " : " false " ,
2024-06-18 03:16:42 +08:00
" totp_key " : pyotp . random_base32 ( )
} ,
" Server " : {
" wg_conf_path " : " /etc/wireguard " ,
2024-12-02 15:09:54 +08:00
" awg_conf_path " : " /etc/amnezia/amneziawg " ,
2024-08-14 01:17:47 -04:00
" app_prefix " : " " ,
2024-06-18 03:16:42 +08:00
" app_ip " : " 0.0.0.0 " ,
" app_port " : " 10086 " ,
" auth_req " : " true " ,
" version " : DASHBOARD_VERSION ,
" dashboard_refresh_interval " : " 60000 " ,
2025-02-17 13:47:05 +08:00
" dashboard_peer_list_display " : " grid " ,
2024-06-18 03:16:42 +08:00
" dashboard_sort " : " status " ,
2024-07-30 18:45:05 -04:00
" dashboard_theme " : " dark " ,
2024-09-06 22:38:56 +08:00
" dashboard_api_key " : " false " ,
" dashboard_language " : " en "
2024-06-18 03:16:42 +08:00
} ,
" Peers " : {
" peer_global_DNS " : " 1.1.1.1 " ,
" peer_endpoint_allowed_ip " : " 0.0.0.0/0 " ,
" peer_display_mode " : " grid " ,
2024-11-25 22:11:51 +08:00
" remote_endpoint " : GetRemoteEndpoint ( ) ,
2024-06-18 03:16:42 +08:00
" peer_MTU " : " 1420 " ,
" peer_keep_alive " : " 21 "
} ,
" Other " : {
" welcome_session " : " true "
2024-08-15 16:55:34 -04:00
} ,
" Database " : {
2025-05-05 00:40:29 +08:00
" type " : " sqlite " ,
" host " : " " ,
2025-05-05 20:34:08 +08:00
" port " : " " ,
2025-05-05 00:40:29 +08:00
" username " : " " ,
" password " : " "
2024-10-29 14:57:29 +08:00
} ,
2024-12-07 16:54:00 +08:00
" Email " : {
" server " : " " ,
" port " : " " ,
" encryption " : " " ,
" username " : " " ,
2025-01-08 18:09:05 +08:00
" email_password " : " " ,
2025-01-16 00:44:22 +08:00
" send_from " : " " ,
" email_template " : " "
2024-12-07 16:54:00 +08:00
} ,
2024-10-29 14:57:29 +08:00
" WireGuardConfiguration " : {
" autostart " : " "
2024-06-18 03:16:42 +08:00
}
}
2022-01-02 14:44:27 -05:00
2024-06-18 03:16:42 +08:00
for section , keys in self . __default . items ( ) :
for key , value in keys . items ( ) :
exist , currentData = self . GetConfig ( section , key )
if not exist :
self . SetConfig ( section , key , value , True )
2025-05-07 18:40:24 +08:00
self . engine = db . create_engine ( self . getConnectionString ( ' wgdashboard ' ) )
self . dbMetadata = db . MetaData ( )
2024-07-30 18:45:05 -04:00
self . __createAPIKeyTable ( )
self . DashboardAPIKeys = self . __getAPIKeys ( )
2024-08-11 01:48:13 -04:00
self . APIAccessed = False
2024-08-19 16:50:00 -04:00
self . SetConfig ( " Server " , " version " , DASHBOARD_VERSION )
2025-05-05 00:40:29 +08:00
def getConnectionString ( self , database ) - > str or None :
2025-05-07 18:40:24 +08:00
cn = None
2025-05-05 00:40:29 +08:00
if self . GetConfig ( " Database " , " type " ) [ 1 ] == " sqlite " :
2025-05-07 18:40:24 +08:00
cn = f ' sqlite:/// { os . path . join ( CONFIGURATION_PATH , " db " , f " { database } .db " ) } '
2025-05-05 00:40:29 +08:00
elif self . GetConfig ( " Database " , " type " ) [ 1 ] == " postgresql " :
2025-05-07 18:40:24 +08:00
cn = f ' postgresql+psycopg2:// { self . GetConfig ( " Database " , " username " ) [ 1 ] } : { self . GetConfig ( " Database " , " password " ) [ 1 ] } @ { self . GetConfig ( " Database " , " host " ) [ 1 ] } / { database } '
if not database_exists ( cn ) :
create_database ( cn )
return cn
2025-05-05 00:40:29 +08:00
2024-07-30 18:45:05 -04:00
def __createAPIKeyTable ( self ) :
2025-05-07 18:40:24 +08:00
self . apiKeyTable = db . Table ( ' DashboardAPIKeys ' , self . dbMetadata ,
db . Column ( " Key " , db . String , nullable = False , primary_key = True ) ,
db . Column ( " CreatedAt " ,
( db . DATETIME if self . GetConfig ( ' Database ' , ' type ' ) [ 1 ] == ' sqlite ' else db . TIMESTAMP ) ,
server_default = db . func . now ( )
) ,
db . Column ( " ExpiredAt " ,
( db . DATETIME if self . GetConfig ( ' Database ' , ' type ' ) [ 1 ] == ' sqlite ' else db . TIMESTAMP )
)
)
self . dbMetadata . create_all ( self . engine )
# existingTable = sqlSelect("SELECT name FROM sqlite_master WHERE type='table' AND name = 'DashboardAPIKeys'").fetchall()
# if len(existingTable) == 0:
# sqlUpdate("CREATE TABLE DashboardAPIKeys (Key VARCHAR NOT NULL PRIMARY KEY, CreatedAt DATETIME NOT NULL DEFAULT (datetime('now', 'localtime')), ExpiredAt VARCHAR)")
2024-07-30 18:45:05 -04:00
def __getAPIKeys ( self ) - > list [ DashboardAPIKey ] :
2025-05-07 18:40:24 +08:00
# keys = sqlSelect("SELECT * FROM DashboardAPIKeys WHERE ExpiredAt IS NULL OR ExpiredAt > datetime('now', 'localtime') ORDER BY CreatedAt DESC").fetchall()
2025-05-08 17:27:49 +08:00
try :
with self . engine . connect ( ) as conn :
keys = conn . execute ( self . apiKeyTable . select ( ) . where (
db . or_ ( self . apiKeyTable . columns . ExpiredAt == None , self . apiKeyTable . columns . ExpiredAt > datetime . now ( ) )
) ) . fetchall ( )
fKeys = [ ]
for k in keys :
fKeys . append ( DashboardAPIKey ( k [ 0 ] , k [ 1 ] . strftime ( " % Y- % m- %d % H: % M: % S " ) , ( k [ 2 ] . strftime ( " % Y- % m- %d % H: % M: % S " ) if k [ 2 ] else None ) ) )
return fKeys
except Exception as e :
print ( " " )
return [ ]
2024-07-30 18:45:05 -04:00
def createAPIKeys ( self , ExpiredAt = None ) :
newKey = secrets . token_urlsafe ( 32 )
2025-05-07 18:40:24 +08:00
# sqlUpdate('INSERT INTO DashboardAPIKeys (Key, ExpiredAt) VALUES (?, ?)', (newKey, ExpiredAt,))
with self . engine . begin ( ) as conn :
conn . execute (
self . apiKeyTable . insert ( ) . values ( {
" Key " : newKey ,
" ExpiredAt " : ExpiredAt
} )
)
2024-09-05 14:51:00 +08:00
2024-07-30 18:45:05 -04:00
self . DashboardAPIKeys = self . __getAPIKeys ( )
def deleteAPIKey ( self , key ) :
2025-05-07 18:40:24 +08:00
# sqlUpdate("UPDATE DashboardAPIKeys SET ExpiredAt = datetime('now', 'localtime') WHERE Key = ?", (key, ))
with self . engine . begin ( ) as conn :
conn . execute (
self . apiKeyTable . update ( ) . values ( {
" ExpiredAt " : datetime . now ( ) ,
} ) . where ( self . apiKeyTable . columns . Key == key )
)
2024-07-30 18:45:05 -04:00
self . DashboardAPIKeys = self . __getAPIKeys ( )
2025-01-08 18:09:05 +08:00
def __configValidation ( self , section : str , key : str , value : Any ) - > [ bool , str ] :
2025-02-16 23:07:16 +08:00
if ( type ( value ) is str and len ( value ) == 0
and section not in [ ' Email ' , ' WireGuardConfiguration ' ] and
( section == ' Peer ' and key == ' peer_global_dns ' ) ) :
2024-06-18 03:16:42 +08:00
return False , " Field cannot be empty! "
2025-02-16 23:07:16 +08:00
if section == " Peers " and key == " peer_global_dns " and len ( value ) > 0 :
2024-11-25 22:11:51 +08:00
return ValidateDNSAddress ( value )
2025-01-08 18:09:05 +08:00
if section == " Peers " and key == " peer_endpoint_allowed_ip " :
2024-06-18 03:16:42 +08:00
value = value . split ( " , " )
for i in value :
2025-02-14 23:59:21 +08:00
i = i . strip ( )
2024-06-18 03:16:42 +08:00
try :
ipaddress . ip_network ( i , strict = False )
except Exception as e :
return False , str ( e )
2025-01-08 18:09:05 +08:00
if section == " Server " and key == " wg_conf_path " :
2024-06-18 03:16:42 +08:00
if not os . path . exists ( value ) :
return False , f " { value } is not a valid path "
2025-01-08 18:09:05 +08:00
if section == " Account " and key == " password " :
2024-06-18 03:16:42 +08:00
if self . GetConfig ( " Account " , " password " ) [ 0 ] :
if not self . __checkPassword (
value [ " currentPassword " ] , self . GetConfig ( " Account " , " password " ) [ 1 ] . encode ( " utf-8 " ) ) :
return False , " Current password does not match. "
if value [ " newPassword " ] != value [ " repeatNewPassword " ] :
return False , " New passwords does not match "
return True , " "
def generatePassword ( self , plainTextPassword : str ) :
2024-08-02 17:27:28 -04:00
return bcrypt . hashpw ( plainTextPassword . encode ( " utf-8 " ) , bcrypt . gensalt ( ) )
2024-06-18 03:16:42 +08:00
def __checkPassword ( self , plainTextPassword : str , hashedPassword : bytes ) :
return bcrypt . checkpw ( plainTextPassword . encode ( " utf-8 " ) , hashedPassword )
def SetConfig ( self , section : str , key : str , value : any , init : bool = False ) - > [ bool , str ] :
if key in self . hiddenAttribute and not init :
return False , None
if not init :
2025-01-08 18:09:05 +08:00
valid , msg = self . __configValidation ( section , key , value )
2024-06-18 03:16:42 +08:00
if not valid :
return False , msg
if section == " Account " and key == " password " :
if not init :
value = self . generatePassword ( value [ " newPassword " ] ) . decode ( " utf-8 " )
2021-08-05 23:15:50 -04:00
else :
2024-06-18 03:16:42 +08:00
value = self . generatePassword ( value ) . decode ( " utf-8 " )
2025-04-19 02:54:47 +08:00
if section == " Email " and key == " email_template " :
value = value . encode ( ' unicode_escape ' ) . decode ( ' utf-8 ' )
2022-03-22 16:13:57 -04:00
2024-09-06 16:31:54 +08:00
if section == " Server " and key == " wg_conf_path " :
if not os . path . exists ( value ) :
return False , " Path does not exist "
2024-06-18 03:16:42 +08:00
if section not in self . __config :
2025-04-19 02:54:47 +08:00
if init :
self . __config [ section ] = { }
else :
return False , " Section does not exist "
2025-04-23 18:05:19 +08:00
2025-04-19 02:54:47 +08:00
if ( ( key not in self . __config [ section ] . keys ( ) and init ) or
2025-04-23 18:05:19 +08:00
( key in self . __config [ section ] . keys ( ) ) ) :
2024-06-18 03:16:42 +08:00
if type ( value ) is bool :
if value :
self . __config [ section ] [ key ] = " true "
else :
self . __config [ section ] [ key ] = " false "
2024-10-29 14:57:29 +08:00
elif type ( value ) in [ int , float ] :
2024-10-03 15:24:50 +08:00
self . __config [ section ] [ key ] = str ( value )
2024-10-29 14:57:29 +08:00
elif type ( value ) is list :
2024-11-02 14:26:47 +06:00
self . __config [ section ] [ key ] = " || " . join ( value ) . strip ( " || " )
2024-06-18 03:16:42 +08:00
else :
self . __config [ section ] [ key ] = value
return self . SaveConfig ( ) , " "
2025-04-19 02:54:47 +08:00
else :
return False , f " { key } does not exist under { section } "
2024-06-18 03:16:42 +08:00
return True , " "
2021-12-26 02:26:39 +03:00
2024-06-18 03:16:42 +08:00
def SaveConfig ( self ) - > bool :
try :
with open ( DASHBOARD_CONF , " w+ " , encoding = ' utf-8 ' ) as configFile :
self . __config . write ( configFile )
return True
except Exception as e :
return False
2024-09-06 16:31:54 +08:00
def GetConfig ( self , section , key ) - > [ bool , any ] :
2024-06-18 03:16:42 +08:00
if section not in self . __config :
return False , None
if key not in self . __config [ section ] :
return False , None
2025-04-19 02:54:47 +08:00
if section == " Email " and key == " email_template " :
return True , self . __config [ section ] [ key ] . encode ( ' utf-8 ' ) . decode ( ' unicode_escape ' )
if section == " WireGuardConfiguration " and key == " autostart " :
return True , list ( filter ( lambda x : len ( x ) > 0 , self . __config [ section ] [ key ] . split ( " || " ) ) )
2024-06-18 03:16:42 +08:00
if self . __config [ section ] [ key ] in [ " 1 " , " yes " , " true " , " on " ] :
return True , True
if self . __config [ section ] [ key ] in [ " 0 " , " no " , " false " , " off " ] :
return True , False
2024-10-29 14:57:29 +08:00
2024-06-18 03:16:42 +08:00
return True , self . __config [ section ] [ key ]
def toJson ( self ) - > dict [ str , dict [ Any , Any ] ] :
the_dict = { }
for section in self . __config . sections ( ) :
the_dict [ section ] = { }
for key , val in self . __config . items ( section ) :
if key not in self . hiddenAttribute :
2024-10-29 14:57:29 +08:00
the_dict [ section ] [ key ] = self . GetConfig ( section , key ) [ 1 ]
2024-06-18 03:16:42 +08:00
return the_dict
2025-03-16 00:42:40 +08:00
2021-12-26 02:26:39 +03:00
2024-11-25 22:11:51 +08:00
"""
Database Connection Functions
"""
2022-01-02 16:35:39 +03:00
2024-12-29 15:57:57 +08:00
sqldb = sqlite3 . connect ( os . path . join ( CONFIGURATION_PATH , ' db ' , ' wgdashboard.db ' ) , check_same_thread = False )
sqldb . row_factory = sqlite3 . Row
2024-08-14 01:17:47 -04:00
2024-08-20 00:02:00 -07:00
def sqlSelect ( statement : str , paramters : tuple = ( ) ) - > sqlite3 . Cursor :
2024-12-29 15:57:57 +08:00
result = [ ]
2024-12-31 10:31:24 +08:00
try :
cursor = sqldb . cursor ( )
result = cursor . execute ( statement , paramters )
except Exception as error :
print ( " [WGDashboard] SQLite Error: " + str ( error ) + " | Statement: " + statement )
2024-12-29 15:57:57 +08:00
return result
2024-11-08 00:29:01 +08:00
2024-08-20 00:02:00 -07:00
def sqlUpdate ( statement : str , paramters : tuple = ( ) ) - > sqlite3 . Cursor :
2024-12-29 13:12:40 +08:00
sqldb = sqlite3 . connect ( os . path . join ( CONFIGURATION_PATH , ' db ' , ' wgdashboard.db ' ) )
sqldb . row_factory = sqlite3 . Row
cursor = sqldb . cursor ( )
2024-08-20 00:02:00 -07:00
with sqldb :
cursor = sqldb . cursor ( )
2024-09-05 14:51:00 +08:00
try :
2024-10-25 00:19:27 +08:00
statement = statement . rstrip ( ' ; ' )
s = f ' BEGIN TRANSACTION; { statement } ;END TRANSACTION; '
2024-09-05 16:17:56 +08:00
cursor . execute ( statement , paramters )
2024-12-29 13:12:40 +08:00
# sqldb.commit()
except Exception as error :
2024-10-17 00:04:26 +08:00
print ( " [WGDashboard] SQLite Error: " + str ( error ) + " | Statement: " + statement )
2024-12-29 15:57:57 +08:00
sqldb . close ( )
2024-08-20 00:02:00 -07:00
2024-08-14 01:17:47 -04:00
DashboardConfig = DashboardConfig ( )
2025-01-08 18:09:05 +08:00
EmailSender = EmailSender ( DashboardConfig )
2024-08-14 01:17:47 -04:00
_ , APP_PREFIX = DashboardConfig . GetConfig ( " Server " , " app_prefix " )
cors = CORS ( app , resources = { rf " { APP_PREFIX } /api/* " : {
" origins " : " * " ,
" methods " : " DELETE, POST, GET, OPTIONS " ,
" allow_headers " : [ " Content-Type " , " wg-dashboard-apikey " ]
} } )
2024-06-18 03:16:42 +08:00
'''
API Routes
'''
2021-05-04 01:32:34 -04:00
2024-06-18 03:16:42 +08:00
@app.before_request
def auth_req ( ) :
2024-08-10 19:03:21 -04:00
if request . method . lower ( ) == ' options ' :
2024-11-24 00:22:33 +08:00
return ResponseObject ( True )
2024-08-11 01:48:13 -04:00
DashboardConfig . APIAccessed = False
2024-08-03 17:03:39 -04:00
if " api " in request . path :
if str ( request . method ) == " GET " :
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 ( ) ) } " )
2024-06-18 03:16:42 +08:00
authenticationRequired = DashboardConfig . GetConfig ( " Server " , " auth_req " ) [ 1 ]
2024-08-02 17:27:28 -04:00
d = request . headers
2024-06-18 03:16:42 +08:00
if authenticationRequired :
2024-08-02 17:27:28 -04:00
apiKey = d . get ( ' wg-dashboard-apikey ' )
2024-07-31 02:27:44 -04:00
apiKeyEnabled = DashboardConfig . GetConfig ( " Server " , " dashboard_api_key " ) [ 1 ]
if apiKey is not None and len ( apiKey ) > 0 and apiKeyEnabled :
apiKeyExist = len ( list ( filter ( lambda x : x . Key == apiKey , DashboardConfig . DashboardAPIKeys ) ) ) == 1
2024-08-03 17:03:39 -04:00
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = f " API Key Access: { ( ' true ' if apiKeyExist else ' false ' ) } - Key: { apiKey } " )
2024-07-31 02:27:44 -04:00
if not apiKeyExist :
2024-08-11 01:48:13 -04:00
DashboardConfig . APIAccessed = False
2024-07-31 02:27:44 -04:00
response = Flask . make_response ( app , {
" status " : False ,
" message " : " API Key does not exist " ,
" data " : None
} )
response . content_type = " application/json "
response . status_code = 401
return response
2024-08-11 01:48:13 -04:00
DashboardConfig . APIAccessed = True
2024-07-31 02:27:44 -04:00
else :
2024-08-11 01:48:13 -04:00
DashboardConfig . APIAccessed = False
2024-12-26 00:06:37 +08:00
whiteList = [
' /static/ ' , ' validateAuthentication ' , ' authenticate ' , ' getDashboardConfiguration ' ,
' getDashboardTheme ' , ' getDashboardVersion ' , ' sharePeer/get ' , ' isTotpEnabled ' , ' locale ' ,
' /fileDownload '
]
if ( " username " not in session
2024-08-14 01:17:47 -04:00
and ( f " { ( APP_PREFIX if len ( APP_PREFIX ) > 0 else ' ' ) } / " != request . path
2024-12-26 00:06:37 +08:00
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 )
2024-07-31 02:27:44 -04:00
) :
response = Flask . make_response ( app , {
" status " : False ,
" message " : " Unauthorized access. " ,
" data " : None
} )
response . content_type = " application/json "
response . status_code = 401
return response
2021-05-04 21:26:40 -04:00
2024-08-14 01:17:47 -04:00
@app.route ( f ' { APP_PREFIX } /api/handshake ' , methods = [ " GET " , " OPTIONS " ] )
2024-11-24 00:22:33 +08:00
def API_Handshake ( ) :
2024-08-10 19:03:21 -04:00
return ResponseObject ( True )
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/validateAuthentication ' )
2024-06-18 03:16:42 +08:00
def API_ValidateAuthentication ( ) :
2024-11-24 00:22:33 +08:00
token = request . cookies . get ( " authToken " )
if DashboardConfig . GetConfig ( " Server " , " auth_req " ) [ 1 ] :
if token is None or token == " " or " username " not in session or session [ " username " ] != token :
return ResponseObject ( False , " Invalid authentication. " )
2024-06-18 03:16:42 +08:00
return ResponseObject ( True )
2021-08-14 17:13:16 -04:00
2024-11-24 00:22:33 +08:00
@app.get ( f ' { APP_PREFIX } /api/requireAuthentication ' )
def API_RequireAuthentication ( ) :
return ResponseObject ( data = DashboardConfig . GetConfig ( " Server " , " auth_req " ) [ 1 ] )
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/authenticate ' )
2024-06-18 03:16:42 +08:00
def API_AuthenticateLogin ( ) :
data = request . get_json ( )
2024-11-25 02:48:55 +08:00
if not DashboardConfig . GetConfig ( " Server " , " auth_req " ) [ 1 ] :
return ResponseObject ( True , DashboardConfig . GetConfig ( " Other " , " welcome_session " ) [ 1 ] )
2024-08-11 01:48:13 -04:00
if DashboardConfig . APIAccessed :
authToken = hashlib . sha256 ( f " { request . headers . get ( ' wg-dashboard-apikey ' ) } { datetime . now ( ) } " . encode ( ) ) . hexdigest ( )
session [ ' username ' ] = authToken
resp = ResponseObject ( True , DashboardConfig . GetConfig ( " Other " , " welcome_session " ) [ 1 ] )
2024-08-15 16:55:34 -04:00
resp . set_cookie ( " authToken " , authToken )
2024-08-11 01:48:13 -04:00
session . permanent = True
return resp
2024-06-18 03:16:42 +08:00
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 ' ]
if ( valid
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 [ ' username ' ] = authToken
resp = ResponseObject ( True , DashboardConfig . GetConfig ( " Other " , " welcome_session " ) [ 1 ] )
resp . set_cookie ( " authToken " , authToken )
session . permanent = True
2024-08-03 17:03:39 -04:00
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = f " Login success: { data [ ' username ' ] } " )
2024-06-18 03:16:42 +08:00
return resp
2024-08-03 17:03:39 -04:00
DashboardLogger . log ( str ( request . url ) , str ( request . remote_addr ) , Message = f " Login failed: { data [ ' username ' ] } " )
2024-06-18 03:16:42 +08:00
if totpEnabled :
return ResponseObject ( False , " Sorry, your username, password or OTP is incorrect. " )
2021-12-29 12:17:44 -05:00
else :
2024-06-18 03:16:42 +08:00
return ResponseObject ( False , " Sorry, your username or password is incorrect. " )
2021-05-13 18:00:40 -04:00
2024-08-15 16:55:34 -04:00
@app.get ( f ' { APP_PREFIX } /api/signout ' )
2024-06-18 03:16:42 +08:00
def API_SignOut ( ) :
resp = ResponseObject ( True , " " )
resp . delete_cookie ( " authToken " )
2024-11-25 01:46:27 +08:00
session . clear ( )
2024-06-18 03:16:42 +08:00
return resp
2022-01-02 16:35:39 +03:00
2024-08-14 01:17:47 -04:00
@app.route ( f ' { APP_PREFIX } /api/getWireguardConfigurations ' , methods = [ " GET " ] )
2024-06-18 03:16:42 +08:00
def API_getWireguardConfigurations ( ) :
2024-11-25 22:11:51 +08:00
InitWireguardConfigurationsList ( )
2024-06-18 03:16:42 +08:00
return ResponseObject ( data = [ wc for wc in WireguardConfigurations . values ( ) ] )
2022-01-02 14:44:27 -05:00
2024-08-14 01:17:47 -04:00
@app.route ( f ' { APP_PREFIX } /api/addWireguardConfiguration ' , methods = [ " POST " ] )
2024-06-18 03:16:42 +08:00
def API_addWireguardConfiguration ( ) :
2022-01-06 15:17:43 -05:00
data = request . get_json ( )
2024-06-18 03:16:42 +08:00
requiredKeys = [
2024-12-04 17:50:16 +08:00
" ConfigurationName " , " Address " , " ListenPort " , " PrivateKey " , " Protocol "
2024-06-18 03:16:42 +08:00
]
2024-10-25 23:34:07 +08:00
for i in requiredKeys :
if i not in data . keys ( ) :
2024-06-18 03:16:42 +08:00
return ResponseObject ( False , " Please provide all required parameters. " )
2024-12-04 17:50:16 +08:00
if data . get ( " Protocol " ) not in ProtocolsEnabled ( ) :
return ResponseObject ( False , " Please provide a valid protocol: wg / awg. " )
2024-06-18 03:16:42 +08:00
# Check duplicate names, ports, address
for i in WireguardConfigurations . values ( ) :
if i . Name == data [ ' ConfigurationName ' ] :
return ResponseObject ( False ,
f " Already have a configuration with the name \" { data [ ' ConfigurationName ' ] } \" " ,
" ConfigurationName " )
if str ( i . ListenPort ) == str ( data [ " ListenPort " ] ) :
return ResponseObject ( False ,
f " Already have a configuration with the port \" { data [ ' ListenPort ' ] } \" " ,
" ListenPort " )
if i . Address == data [ " Address " ] :
return ResponseObject ( False ,
f " Already have a configuration with the address \" { data [ ' Address ' ] } \" " ,
" Address " )
2024-10-25 23:34:07 +08:00
if " Backup " in data . keys ( ) :
2024-12-04 17:50:16 +08:00
path = {
" wg " : DashboardConfig . GetConfig ( " Server " , " wg_conf_path " ) [ 1 ] ,
" awg " : DashboardConfig . GetConfig ( " Server " , " awg_conf_path " ) [ 1 ]
}
if ( os . path . exists ( os . path . join ( path [ ' wg ' ] , ' WGDashboard_Backup ' , data [ " Backup " ] ) ) and
os . path . exists ( os . path . join ( path [ ' wg ' ] , ' WGDashboard_Backup ' , data [ " Backup " ] . replace ( ' .conf ' , ' .sql ' ) ) ) ) :
protocol = " wg "
elif ( os . path . exists ( os . path . join ( path [ ' awg ' ] , ' WGDashboard_Backup ' , data [ " Backup " ] ) ) and
os . path . exists ( os . path . join ( path [ ' awg ' ] , ' WGDashboard_Backup ' , data [ " Backup " ] . replace ( ' .conf ' , ' .sql ' ) ) ) ) :
protocol = " awg "
else :
return ResponseObject ( False , " Backup does not exist " )
2024-10-25 23:34:07 +08:00
shutil . copy (
2024-12-04 17:50:16 +08:00
os . path . join ( path [ protocol ] , ' WGDashboard_Backup ' , data [ " Backup " ] ) ,
os . path . join ( path [ protocol ] , f ' { data [ " ConfigurationName " ] } .conf ' )
2024-10-25 23:34:07 +08:00
)
2024-12-04 17:50:16 +08:00
WireguardConfigurations [ data [ ' ConfigurationName ' ] ] = WireguardConfiguration ( data = data , name = data [ ' ConfigurationName ' ] ) if protocol == ' wg ' else AmneziaWireguardConfiguration ( data = data , name = data [ ' ConfigurationName ' ] )
2024-10-25 23:34:07 +08:00
else :
2024-12-04 17:50:16 +08:00
WireguardConfigurations [ data [ ' ConfigurationName ' ] ] = WireguardConfiguration ( data = data ) if data . get ( ' Protocol ' ) == ' wg ' else AmneziaWireguardConfiguration ( data = data )
2024-06-18 03:16:42 +08:00
return ResponseObject ( )
2025-04-23 19:24:50 +08:00
@app.get ( f ' { APP_PREFIX } /api/toggleWireguardConfiguration ' )
2024-06-18 03:16:42 +08:00
def API_toggleWireguardConfiguration ( ) :
configurationName = request . args . get ( ' configurationName ' )
if configurationName is None or len (
configurationName ) == 0 or configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Please provide a valid configuration name " , status_code = 404 )
2024-06-18 03:16:42 +08:00
toggleStatus , msg = WireguardConfigurations [ configurationName ] . toggleConfiguration ( )
return ResponseObject ( toggleStatus , msg , WireguardConfigurations [ configurationName ] . Status )
2024-10-04 16:58:47 +08:00
@app.post ( f ' { APP_PREFIX } /api/updateWireguardConfiguration ' )
def API_updateWireguardConfiguration ( ) :
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 " )
if name not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2024-10-04 16:58:47 +08:00
status , msg = WireguardConfigurations [ name ] . updateConfigurationSettings ( data )
return ResponseObject ( status , message = msg , data = WireguardConfigurations [ name ] )
2024-06-18 03:16:42 +08:00
2024-12-06 20:27:04 +08:00
@app.get ( f ' { APP_PREFIX } /api/getWireguardConfigurationRawFile ' )
def API_GetWireguardConfigurationRawFile ( ) :
configurationName = request . args . get ( ' configurationName ' )
if configurationName is None or len (
configurationName ) == 0 or configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Please provide a valid configuration name " , status_code = 404 )
2024-12-06 20:27:04 +08:00
return ResponseObject ( data = {
" path " : WireguardConfigurations [ configurationName ] . configPath ,
" content " : WireguardConfigurations [ configurationName ] . getRawConfigurationFile ( )
} )
@app.post ( f ' { APP_PREFIX } /api/updateWireguardConfigurationRawFile ' )
def API_UpdateWireguardConfigurationRawFile ( ) :
data = request . get_json ( )
configurationName = data . get ( ' configurationName ' )
rawConfiguration = data . get ( ' rawConfiguration ' )
if configurationName is None or len (
configurationName ) == 0 or configurationName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Please provide a valid configuration name " )
if rawConfiguration is None or len ( rawConfiguration ) == 0 :
return ResponseObject ( False , " Please provide content " )
status , err = WireguardConfigurations [ configurationName ] . updateRawConfigurationFile ( rawConfiguration )
return ResponseObject ( status = status , message = err )
2024-10-25 00:19:27 +08:00
@app.post ( f ' { APP_PREFIX } /api/deleteWireguardConfiguration ' )
def API_deleteWireguardConfiguration ( ) :
data = request . get_json ( )
2025-03-28 00:13:38 +08:00
if " ConfigurationName " not in data . keys ( ) or data . get ( " ConfigurationName " ) is None or data . get ( " ConfigurationName " ) not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Please provide the configuration name you want to delete " , status_code = 404 )
2025-03-28 00:13:38 +08:00
status = WireguardConfigurations [ data . get ( " ConfigurationName " ) ] . deleteConfiguration ( )
2024-10-25 00:19:27 +08:00
if status :
2025-03-28 00:13:38 +08:00
WireguardConfigurations . pop ( data . get ( " ConfigurationName " ) )
2024-10-25 00:19:27 +08:00
return ResponseObject ( status )
2024-11-06 18:36:55 +08:00
@app.post ( f ' { APP_PREFIX } /api/renameWireguardConfiguration ' )
def API_renameWireguardConfiguration ( ) :
data = request . get_json ( )
2025-03-28 00:13:38 +08:00
keys = [ " ConfigurationName " , " NewConfigurationName " ]
2024-11-06 18:36:55 +08:00
for k in keys :
if ( k not in data . keys ( ) or data . get ( k ) is None or len ( data . get ( k ) ) == 0 or
2025-03-28 00:13:38 +08:00
( k == " ConfigurationName " and data . get ( k ) not in WireguardConfigurations . keys ( ) ) ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Please provide the configuration name you want to rename " , status_code = 404 )
2024-11-06 18:36:55 +08:00
2025-03-28 00:13:38 +08:00
status , message = WireguardConfigurations [ data . get ( " ConfigurationName " ) ] . renameConfiguration ( data . get ( " NewConfigurationName " ) )
2024-11-06 18:36:55 +08:00
if status :
2025-03-28 00:13:38 +08:00
WireguardConfigurations . pop ( data . get ( " ConfigurationName " ) )
2024-11-06 18:36:55 +08:00
WireguardConfigurations [ data . get ( " NewConfigurationName " ) ] = WireguardConfiguration ( data . get ( " NewConfigurationName " ) )
return ResponseObject ( status , message )
2024-12-30 20:30:09 +08:00
@app.get ( f ' { APP_PREFIX } /api/getWireguardConfigurationRealtimeTraffic ' )
def API_getWireguardConfigurationRealtimeTraffic ( ) :
configurationName = request . args . get ( ' configurationName ' )
if configurationName is None or configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2024-12-30 20:30:09 +08:00
return ResponseObject ( data = WireguardConfigurations [ configurationName ] . getRealtimeTrafficUsage ( ) )
2024-10-15 00:30:20 +08:00
@app.get ( f ' { APP_PREFIX } /api/getWireguardConfigurationBackup ' )
def API_getWireguardConfigurationBackup ( ) :
configurationName = request . args . get ( ' configurationName ' )
if configurationName is None or configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2024-10-15 00:30:20 +08:00
return ResponseObject ( data = WireguardConfigurations [ configurationName ] . getBackups ( ) )
2024-10-25 00:19:27 +08:00
@app.get ( f ' { APP_PREFIX } /api/getAllWireguardConfigurationBackup ' )
def API_getAllWireguardConfigurationBackup ( ) :
data = {
" ExistingConfigurations " : { } ,
" NonExistingConfigurations " : { }
}
existingConfiguration = WireguardConfigurations . keys ( )
for i in existingConfiguration :
b = WireguardConfigurations [ i ] . getBackups ( True )
if len ( b ) > 0 :
data [ ' ExistingConfigurations ' ] [ i ] = WireguardConfigurations [ i ] . getBackups ( True )
2024-12-04 17:50:16 +08:00
for protocol in ProtocolsEnabled ( ) :
directory = os . path . join ( DashboardConfig . GetConfig ( " Server " , f " { protocol } _conf_path " ) [ 1 ] , ' 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 )
for f , ct in files :
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 ] = [ ]
date = s . group ( 2 )
d = {
" protocol " : protocol ,
" filename " : f ,
" backupDate " : date ,
" 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 ) ) :
d [ ' database ' ] = True
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 )
2024-10-25 00:19:27 +08:00
return ResponseObject ( data = data )
2024-10-16 17:44:49 +08:00
@app.get ( f ' { APP_PREFIX } /api/createWireguardConfigurationBackup ' )
def API_createWireguardConfigurationBackup ( ) :
configurationName = request . args . get ( ' configurationName ' )
if configurationName is None or configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2024-12-06 20:27:04 +08:00
return ResponseObject ( status = WireguardConfigurations [ configurationName ] . backupConfigurationFile ( ) [ 0 ] ,
2024-10-16 17:44:49 +08:00
data = WireguardConfigurations [ configurationName ] . getBackups ( ) )
@app.post ( f ' { APP_PREFIX } /api/deleteWireguardConfigurationBackup ' )
def API_deleteWireguardConfigurationBackup ( ) :
data = request . get_json ( )
2025-04-07 18:45:12 +08:00
if ( " ConfigurationName " not in data . keys ( ) or
" BackupFileName " not in data . keys ( ) or
len ( data [ ' ConfigurationName ' ] ) == 0 or
len ( data [ ' BackupFileName ' ] ) == 0 ) :
2024-10-16 17:44:49 +08:00
return ResponseObject ( False ,
2025-04-19 02:54:47 +08:00
" Please provide configurationName and backupFileName in body " , status_code = 400 )
2025-04-07 18:45:12 +08:00
configurationName = data [ ' ConfigurationName ' ]
backupFileName = data [ ' BackupFileName ' ]
2024-10-16 17:44:49 +08:00
if configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2024-10-16 17:44:49 +08:00
2025-04-19 02:54:47 +08:00
status = WireguardConfigurations [ configurationName ] . deleteBackup ( backupFileName )
return ResponseObject ( status = status , message = ( None if status else ' Backup file does not exist ' ) ,
status_code = ( 200 if status else 404 ) )
2024-10-16 17:44:49 +08:00
2024-12-26 00:06:37 +08:00
@app.get ( f ' { APP_PREFIX } /api/downloadWireguardConfigurationBackup ' )
def API_downloadWireguardConfigurationBackup ( ) :
configurationName = request . args . get ( ' configurationName ' )
backupFileName = request . args . get ( ' backupFileName ' )
if configurationName is None or configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2024-12-26 00:06:37 +08:00
status , zip = WireguardConfigurations [ configurationName ] . downloadBackup ( backupFileName )
2025-04-19 02:54:47 +08:00
return ResponseObject ( status , data = zip , status_code = ( 200 if status else 404 ) )
2024-12-26 00:06:37 +08:00
2024-10-16 17:44:49 +08:00
@app.post ( f ' { APP_PREFIX } /api/restoreWireguardConfigurationBackup ' )
def API_restoreWireguardConfigurationBackup ( ) :
data = request . get_json ( )
2025-04-07 18:45:12 +08:00
if ( " ConfigurationName " not in data . keys ( ) or
" BackupFileName " not in data . keys ( ) or
len ( data [ ' ConfigurationName ' ] ) == 0 or
len ( data [ ' BackupFileName ' ] ) == 0 ) :
2024-10-16 17:44:49 +08:00
return ResponseObject ( False ,
2025-04-19 02:54:47 +08:00
" Please provide ConfigurationName and BackupFileName in body " , status_code = 400 )
2025-04-07 18:45:12 +08:00
configurationName = data [ ' ConfigurationName ' ]
backupFileName = data [ ' BackupFileName ' ]
2024-10-16 17:44:49 +08:00
if configurationName not in WireguardConfigurations . keys ( ) :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
status = WireguardConfigurations [ configurationName ] . restoreBackup ( backupFileName )
return ResponseObject ( status = status , message = ( None if status else ' Restore backup failed ' ) )
2024-10-16 17:44:49 +08:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/getDashboardConfiguration ' )
2024-06-18 03:16:42 +08:00
def API_getDashboardConfiguration ( ) :
return ResponseObject ( data = DashboardConfig . toJson ( ) )
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/updateDashboardConfigurationItem ' )
2024-06-18 03:16:42 +08:00
def API_updateDashboardConfigurationItem ( ) :
2020-12-26 23:42:41 -05:00
data = request . get_json ( )
2024-06-18 03:16:42 +08:00
if " section " not in data . keys ( ) or " key " not in data . keys ( ) or " value " not in data . keys ( ) :
return ResponseObject ( False , " Invalid request. " )
valid , msg = DashboardConfig . SetConfig (
data [ " section " ] , data [ " key " ] , data [ ' value ' ] )
if not valid :
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , msg , status_code = 404 )
2024-09-06 16:31:54 +08:00
if data [ ' section ' ] == " Server " :
if data [ ' key ' ] == ' wg_conf_path ' :
2024-12-02 15:09:54 +08:00
WireguardConfigurations . clear ( )
2024-09-06 16:31:54 +08:00
WireguardConfigurations . clear ( )
2025-01-24 00:01:29 +08:00
InitWireguardConfigurationsList ( )
2024-11-02 14:26:47 +06:00
return ResponseObject ( True , data = DashboardConfig . GetConfig ( data [ " section " ] , data [ " key " ] ) [ 1 ] )
2022-01-02 16:35:39 +03:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/getDashboardAPIKeys ' )
2024-07-30 18:45:05 -04:00
def API_getDashboardAPIKeys ( ) :
if DashboardConfig . GetConfig ( ' Server ' , ' dashboard_api_key ' ) :
return ResponseObject ( data = DashboardConfig . DashboardAPIKeys )
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " WGDashboard API Keys function is disabled " )
2024-07-30 18:45:05 -04:00
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/newDashboardAPIKey ' )
2024-07-30 18:45:05 -04:00
def API_newDashboardAPIKey ( ) :
data = request . get_json ( )
if DashboardConfig . GetConfig ( ' Server ' , ' dashboard_api_key ' ) :
try :
2025-04-19 02:54:47 +08:00
if data [ ' NeverExpire ' ] :
2024-07-30 18:45:05 -04:00
expiredAt = None
else :
2024-08-06 19:15:00 -04:00
expiredAt = datetime . strptime ( data [ ' ExpiredAt ' ] , ' % Y- % m- %d % H: % M: % S ' )
2024-07-30 18:45:05 -04:00
DashboardConfig . createAPIKeys ( expiredAt )
return ResponseObject ( True , data = DashboardConfig . DashboardAPIKeys )
except Exception as e :
return ResponseObject ( False , str ( e ) )
return ResponseObject ( False , " Dashboard API Keys function is disbaled " )
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/deleteDashboardAPIKey ' )
2024-07-30 18:45:05 -04:00
def API_deleteDashboardAPIKey ( ) :
data = request . get_json ( )
if DashboardConfig . GetConfig ( ' Server ' , ' dashboard_api_key ' ) :
if len ( data [ ' Key ' ] ) > 0 and len ( list ( filter ( lambda x : x . Key == data [ ' Key ' ] , DashboardConfig . DashboardAPIKeys ) ) ) > 0 :
DashboardConfig . deleteAPIKey ( data [ ' Key ' ] )
return ResponseObject ( True , data = DashboardConfig . DashboardAPIKeys )
2025-04-19 02:54:47 +08:00
else :
return ResponseObject ( False , " API Key does not exist " , status_code = 404 )
2024-07-30 18:45:05 -04:00
return ResponseObject ( False , " Dashboard API Keys function is disbaled " )
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/updatePeerSettings/<configName> ' )
2024-06-18 03:16:42 +08:00
def API_updatePeerSettings ( configName ) :
2021-04-02 20:48:00 -04:00
data = request . get_json ( )
id = data [ ' id ' ]
2024-06-18 03:16:42 +08:00
if len ( id ) > 0 and configName in WireguardConfigurations . keys ( ) :
name = data [ ' name ' ]
private_key = data [ ' private_key ' ]
dns_addresses = data [ ' DNS ' ]
allowed_ip = data [ ' allowed_ip ' ]
endpoint_allowed_ip = data [ ' endpoint_allowed_ip ' ]
preshared_key = data [ ' preshared_key ' ]
mtu = data [ ' mtu ' ]
keepalive = data [ ' keepalive ' ]
wireguardConfig = WireguardConfigurations [ configName ]
foundPeer , peer = wireguardConfig . searchPeer ( id )
if foundPeer :
2024-12-03 02:34:45 +08:00
if wireguardConfig . Protocol == ' wg ' :
return peer . updatePeer ( name , private_key , preshared_key , dns_addresses ,
allowed_ip , endpoint_allowed_ip , mtu , keepalive )
2024-06-18 03:16:42 +08:00
return peer . updatePeer ( name , private_key , preshared_key , dns_addresses ,
2025-05-01 22:40:53 +08:00
allowed_ip , endpoint_allowed_ip , mtu , keepalive , " off " )
2024-12-03 02:34:45 +08:00
2024-06-18 03:16:42 +08:00
return ResponseObject ( False , " Peer does not exist " )
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/resetPeerData/<configName> ' )
2024-08-08 23:27:13 -04:00
def API_resetPeerData ( configName ) :
data = request . get_json ( )
id = data [ ' id ' ]
type = data [ ' type ' ]
if len ( id ) == 0 or configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration/Peer does not exist " )
wgc = WireguardConfigurations . get ( configName )
foundPeer , peer = wgc . searchPeer ( id )
if not foundPeer :
return ResponseObject ( False , " Configuration/Peer does not exist " )
2025-03-07 00:52:51 +08:00
resetStatus = peer . resetDataUsage ( type )
if resetStatus :
wgc . restrictPeers ( [ id ] )
wgc . allowAccessPeers ( [ id ] )
return ResponseObject ( status = resetStatus )
2024-06-18 03:16:42 +08:00
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/deletePeers/<configName> ' )
2024-06-18 03:16:42 +08:00
def API_deletePeers ( configName : str ) - > ResponseObject :
data = request . get_json ( )
peers = data [ ' peers ' ]
if configName in WireguardConfigurations . keys ( ) :
if len ( peers ) == 0 :
2025-05-02 11:52:39 +02:00
return ResponseObject ( False , " Please specify one or more peers " , status_code = 400 )
2024-06-18 03:16:42 +08:00
configuration = WireguardConfigurations . get ( configName )
return configuration . deletePeers ( peers )
2021-04-02 20:48:00 -04:00
2025-05-02 11:52:39 +02:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2021-12-26 02:26:39 +03:00
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/restrictPeers/<configName> ' )
2024-06-18 03:16:42 +08:00
def API_restrictPeers ( configName : str ) - > ResponseObject :
2021-04-02 20:48:00 -04:00
data = request . get_json ( )
2024-06-18 03:16:42 +08:00
peers = data [ ' peers ' ]
if configName in WireguardConfigurations . keys ( ) :
if len ( peers ) == 0 :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Please specify one or more peers " )
2024-06-18 03:16:42 +08:00
configuration = WireguardConfigurations . get ( configName )
return configuration . restrictPeers ( peers )
2025-04-19 02:54:47 +08:00
return ResponseObject ( False , " Configuration does not exist " , status_code = 404 )
2022-01-02 16:35:39 +03:00
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/sharePeer/create ' )
2024-08-06 10:17:14 -04:00
def API_sharePeer_create ( ) :
data : dict [ str , str ] = request . get_json ( )
Configuration = data . get ( ' Configuration ' )
Peer = data . get ( ' Peer ' )
ExpireDate = data . get ( ' ExpireDate ' )
if Configuration is None or Peer is None :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Please specify configuration and peers " )
2024-08-06 10:17:14 -04:00
activeLink = AllPeerShareLinks . getLink ( Configuration , Peer )
if len ( activeLink ) > 0 :
2024-11-29 14:42:13 +08:00
return ResponseObject ( True ,
" This peer is already sharing. Please view data for shared link. " ,
data = activeLink [ 0 ]
)
2025-05-08 17:27:49 +08:00
status , message = AllPeerShareLinks . addLink ( Configuration , Peer , datetime . strptime ( ExpireDate , " % Y- % m- %d % H: % M: % S " ) )
2024-08-06 10:17:14 -04:00
if not status :
return ResponseObject ( status , message )
return ResponseObject ( data = AllPeerShareLinks . getLinkByID ( message ) )
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/sharePeer/update ' )
2024-08-06 10:17:14 -04:00
def API_sharePeer_update ( ) :
data : dict [ str , str ] = request . get_json ( )
ShareID : str = data . get ( " ShareID " )
ExpireDate : str = data . get ( " ExpireDate " )
if ShareID is None :
return ResponseObject ( False , " Please specify ShareID " )
if len ( AllPeerShareLinks . getLinkByID ( ShareID ) ) == 0 :
return ResponseObject ( False , " ShareID does not exist " )
2025-05-08 17:27:49 +08:00
status , message = AllPeerShareLinks . updateLinkExpireDate ( ShareID , datetime . strptime ( ExpireDate , " % Y- % m- %d % H: % M: % S " ) )
2024-08-06 10:17:14 -04:00
if not status :
return ResponseObject ( status , message )
return ResponseObject ( data = AllPeerShareLinks . getLinkByID ( ShareID ) )
2024-06-18 03:16:42 +08:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/sharePeer/get ' )
2024-08-07 00:37:05 -04:00
def API_sharePeer_get ( ) :
data = request . args
ShareID = data . get ( " ShareID " )
if ShareID is None or len ( ShareID ) == 0 :
return ResponseObject ( False , " Please provide ShareID " )
link = AllPeerShareLinks . getLinkByID ( ShareID )
if len ( link ) == 0 :
return ResponseObject ( False , " This link is either expired to invalid " )
l = link [ 0 ]
if l . Configuration not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " The peer you ' re looking for does not exist " )
c = WireguardConfigurations . get ( l . Configuration )
fp , p = c . searchPeer ( l . Peer )
if not fp :
return ResponseObject ( False , " The peer you ' re looking for does not exist " )
return ResponseObject ( data = p . downloadPeer ( ) )
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/allowAccessPeers/<configName> ' )
2024-06-18 03:16:42 +08:00
def API_allowAccessPeers ( configName : str ) - > ResponseObject :
2021-08-05 23:15:50 -04:00
data = request . get_json ( )
2024-06-18 03:16:42 +08:00
peers = data [ ' peers ' ]
if configName in WireguardConfigurations . keys ( ) :
if len ( peers ) == 0 :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Please specify one or more peers " )
2024-06-18 03:16:42 +08:00
configuration = WireguardConfigurations . get ( configName )
return configuration . allowAccessPeers ( peers )
return ResponseObject ( False , " Configuration does not exist " )
2021-08-14 17:13:16 -04:00
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/addPeers/<configName> ' )
2024-06-18 03:16:42 +08:00
def API_addPeers ( configName ) :
if configName in WireguardConfigurations . keys ( ) :
2024-10-02 17:09:35 +08:00
try :
data : dict = request . get_json ( )
2024-08-19 16:50:00 -04:00
2024-10-02 17:09:35 +08:00
bulkAdd : bool = data . get ( " bulkAdd " , False )
bulkAddAmount : int = data . get ( ' bulkAddAmount ' , 0 )
preshared_key_bulkAdd : bool = data . get ( ' preshared_key_bulkAdd ' , False )
2025-02-08 16:40:20 +08:00
2024-10-02 17:09:35 +08:00
public_key : str = data . get ( ' public_key ' , " " )
2025-02-10 16:17:00 +08:00
allowed_ips : list [ str ] = data . get ( ' allowed_ips ' , [ ] )
2025-02-16 17:42:32 +08:00
allowed_ips_validation : bool = data . get ( ' allowed_ips_validation ' , True )
2024-08-08 23:27:13 -04:00
2024-10-02 17:09:35 +08:00
endpoint_allowed_ip : str = data . get ( ' endpoint_allowed_ip ' , DashboardConfig . GetConfig ( " Peers " , " peer_endpoint_allowed_ip " ) [ 1 ] )
dns_addresses : str = data . get ( ' DNS ' , DashboardConfig . GetConfig ( " Peers " , " peer_global_DNS " ) [ 1 ] )
mtu : int = data . get ( ' mtu ' , int ( DashboardConfig . GetConfig ( " Peers " , " peer_MTU " ) [ 1 ] ) )
keep_alive : int = data . get ( ' keepalive ' , int ( DashboardConfig . GetConfig ( " Peers " , " peer_keep_alive " ) [ 1 ] ) )
2025-02-08 16:40:20 +08:00
preshared_key : str = data . get ( ' preshared_key ' , " " )
2024-10-02 17:09:35 +08:00
if type ( mtu ) is not int or mtu < 0 or mtu > 1460 :
mtu = int ( DashboardConfig . GetConfig ( " Peers " , " peer_MTU " ) [ 1 ] )
if type ( keep_alive ) is not int or keep_alive < 0 :
keep_alive = int ( DashboardConfig . GetConfig ( " Peers " , " peer_keep_alive " ) [ 1 ] )
config = WireguardConfigurations . get ( configName )
if not config . getStatus ( ) :
config . toggleConfiguration ( )
2025-02-16 23:07:16 +08:00
ipStatus , availableIps = config . getAvailableIP ( - 1 )
ipCountStatus , numberOfAvailableIPs = config . getNumberOfAvailableIP ( )
2025-02-16 17:42:32 +08:00
defaultIPSubnet = list ( availableIps . keys ( ) ) [ 0 ]
2024-10-02 17:09:35 +08:00
if bulkAdd :
if type ( preshared_key_bulkAdd ) is not bool :
preshared_key_bulkAdd = False
if type ( bulkAddAmount ) is not int or bulkAddAmount < 1 :
return ResponseObject ( False , " Please specify amount of peers you want to add " )
2025-02-16 17:42:32 +08:00
if not ipStatus :
2024-10-02 17:09:35 +08:00
return ResponseObject ( False , " No more available IP can assign " )
2025-02-16 17:42:32 +08:00
if len ( availableIps . keys ( ) ) == 0 :
return ResponseObject ( False , " This configuration does not have any IP address available " )
2025-02-16 23:07:16 +08:00
if bulkAddAmount > sum ( list ( numberOfAvailableIPs . values ( ) ) ) :
2024-10-02 17:09:35 +08:00
return ResponseObject ( False ,
2025-02-16 23:07:16 +08:00
f " The maximum number of peers can add is { sum ( list ( numberOfAvailableIPs . values ( ) ) ) } " )
2024-10-02 17:09:35 +08:00
keyPairs = [ ]
2025-02-16 23:07:16 +08:00
addedCount = 0
for subnet in availableIps . keys ( ) :
for ip in availableIps [ subnet ] :
newPrivateKey = GenerateWireguardPrivateKey ( ) [ 1 ]
addedCount + = 1
keyPairs . append ( {
" private_key " : newPrivateKey ,
" id " : GenerateWireguardPublicKey ( newPrivateKey ) [ 1 ] ,
" preshared_key " : ( GenerateWireguardPrivateKey ( ) [ 1 ] if preshared_key_bulkAdd else " " ) ,
" allowed_ip " : ip ,
" name " : f " BulkPeer_ { ( addedCount + 1 ) } _ { datetime . now ( ) . strftime ( ' % Y % m %d _ % H % M % S ' ) } " ,
" DNS " : dns_addresses ,
" endpoint_allowed_ip " : endpoint_allowed_ip ,
" mtu " : mtu ,
" keepalive " : keep_alive ,
2025-05-01 22:40:53 +08:00
" advanced_security " : " off "
2025-02-16 23:07:16 +08:00
} )
if addedCount == bulkAddAmount :
break
if addedCount == bulkAddAmount :
break
if len ( keyPairs ) == 0 or ( bulkAdd and len ( keyPairs ) != bulkAddAmount ) :
2024-10-02 17:09:35 +08:00
return ResponseObject ( False , " Generating key pairs by bulk failed " )
2025-02-10 16:17:00 +08:00
status , result = config . addPeers ( keyPairs )
return ResponseObject ( status = status , message = result [ ' message ' ] , data = result [ ' peers ' ] )
2024-10-02 17:09:35 +08:00
else :
if config . searchPeer ( public_key ) [ 0 ] is True :
return ResponseObject ( False , f " This peer already exist " )
name = data . get ( " name " , " " )
private_key = data . get ( " private_key " , " " )
2025-04-19 02:54:47 +08:00
if len ( public_key ) == 0 :
if len ( private_key ) == 0 :
private_key = GenerateWireguardPrivateKey ( ) [ 1 ]
public_key = GenerateWireguardPublicKey ( private_key ) [ 1 ]
else :
public_key = GenerateWireguardPublicKey ( private_key ) [ 1 ]
else :
if len ( private_key ) > 0 :
genPub = GenerateWireguardPublicKey ( private_key ) [ 1 ]
# Check if provided pubkey match provided private key
if public_key != genPub :
return ResponseObject ( False , " Provided Public Key does not match provided Private Key " )
# if len(public_key) == 0 and len(private_key) == 0:
# private_key = GenerateWireguardPrivateKey()[1]
# public_key = GenerateWireguardPublicKey(private_key)[1]
# elif len(public_key) == 0 and len(private_key) > 0:
# public_key = GenerateWireguardPublicKey(private_key)[1]
2025-02-10 16:17:00 +08:00
if len ( allowed_ips ) == 0 :
2025-02-16 17:42:32 +08:00
if ipStatus :
2025-02-16 23:07:16 +08:00
for subnet in availableIps . keys ( ) :
for ip in availableIps [ subnet ] :
allowed_ips = [ ip ]
break
break
2025-02-10 16:17:00 +08:00
else :
return ResponseObject ( False , " No more available IP can assign " )
2025-02-08 16:40:20 +08:00
2025-02-16 17:42:32 +08:00
if allowed_ips_validation :
2025-02-08 16:40:20 +08:00
for i in allowed_ips :
2025-02-16 17:42:32 +08:00
found = False
2025-02-16 23:07:16 +08:00
for subnet in availableIps . keys ( ) :
network = ipaddress . ip_network ( subnet , False )
ap = ipaddress . ip_network ( i )
if network . version == ap . version and ap . subnet_of ( network ) :
2025-02-16 17:42:32 +08:00
found = True
2025-02-16 23:07:16 +08:00
2025-02-16 17:42:32 +08:00
if not found :
2025-02-08 16:40:20 +08:00
return ResponseObject ( False , f " This IP is not available: { i } " )
2025-02-10 16:17:00 +08:00
status , result = config . addPeers ( [
2024-11-06 20:58:53 +08:00
{
" name " : name ,
" id " : public_key ,
" private_key " : private_key ,
" allowed_ip " : ' , ' . join ( allowed_ips ) ,
" preshared_key " : preshared_key ,
" endpoint_allowed_ip " : endpoint_allowed_ip ,
" DNS " : dns_addresses ,
" mtu " : mtu ,
2024-12-03 02:34:45 +08:00
" keepalive " : keep_alive ,
2025-05-01 22:40:53 +08:00
" advanced_security " : " off "
2024-11-06 20:58:53 +08:00
} ]
)
2025-02-10 16:17:00 +08:00
return ResponseObject ( status = status , message = result [ ' message ' ] , data = result [ ' peers ' ] )
2024-10-02 17:09:35 +08:00
except Exception as e :
2025-02-16 17:42:32 +08:00
print ( e , str ( e . __traceback__ ) )
2024-10-02 17:09:35 +08:00
return ResponseObject ( False , " Add peers failed. Please see data for specific issue " )
2024-06-18 03:16:42 +08:00
return ResponseObject ( False , " Configuration does not exist " )
2024-09-24 22:54:18 +08:00
@app.get ( f " { APP_PREFIX } /api/downloadPeer/<configName> " )
2024-06-18 03:16:42 +08:00
def API_downloadPeer ( configName ) :
data = request . args
if configName not in WireguardConfigurations . keys ( ) :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Configuration does not exist " )
2024-06-18 03:16:42 +08:00
configuration = WireguardConfigurations [ configName ]
peerFound , peer = configuration . searchPeer ( data [ ' id ' ] )
if len ( data [ ' id ' ] ) == 0 or not peerFound :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Peer does not exist " )
2024-06-18 03:16:42 +08:00
return ResponseObject ( data = peer . downloadPeer ( ) )
2024-09-24 22:54:18 +08:00
@app.get ( f " { APP_PREFIX } /api/downloadAllPeers/<configName> " )
2024-06-18 03:16:42 +08:00
def API_downloadAllPeers ( configName ) :
if configName not in WireguardConfigurations . keys ( ) :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Configuration does not exist " )
2024-06-18 03:16:42 +08:00
configuration = WireguardConfigurations [ configName ]
peerData = [ ]
untitledPeer = 0
for i in configuration . Peers :
file = i . downloadPeer ( )
2025-01-16 18:36:06 +08:00
if file [ " fileName " ] == " UntitledPeer " :
2024-06-18 03:16:42 +08:00
file [ " fileName " ] = str ( untitledPeer ) + " _ " + file [ " fileName " ]
untitledPeer + = 1
peerData . append ( file )
return ResponseObject ( data = peerData )
2024-09-24 22:54:18 +08:00
@app.get ( f " { APP_PREFIX } /api/getAvailableIPs/<configName> " )
2024-06-18 03:16:42 +08:00
def API_getAvailableIPs ( configName ) :
2024-11-25 22:11:51 +08:00
if configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration does not exist " )
status , ips = WireguardConfigurations . get ( configName ) . getAvailableIP ( )
2024-06-18 03:16:42 +08:00
return ResponseObject ( status = status , data = ips )
2025-02-16 17:42:32 +08:00
@app.get ( f " { APP_PREFIX } /api/getNumberOfAvailableIPs/<configName> " )
def API_getNumberOfAvailableIPs ( configName ) :
if configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration does not exist " )
status , ips = WireguardConfigurations . get ( configName ) . getNumberOfAvailableIP ( )
return ResponseObject ( status = status , data = ips )
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/getWireguardConfigurationInfo ' )
2024-06-18 03:16:42 +08:00
def API_getConfigurationInfo ( ) :
configurationName = request . args . get ( " configurationName " )
if not configurationName or configurationName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Please provide configuration name " )
return ResponseObject ( data = {
" configurationInfo " : WireguardConfigurations [ configurationName ] ,
" configurationPeers " : WireguardConfigurations [ configurationName ] . getPeersList ( ) ,
" configurationRestrictedPeers " : WireguardConfigurations [ configurationName ] . getRestrictedPeersList ( )
} )
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/getDashboardTheme ' )
2024-06-18 03:16:42 +08:00
def API_getDashboardTheme ( ) :
return ResponseObject ( data = DashboardConfig . GetConfig ( " Server " , " dashboard_theme " ) [ 1 ] )
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/getDashboardVersion ' )
2024-08-19 16:50:00 -04:00
def API_getDashboardVersion ( ) :
return ResponseObject ( data = DashboardConfig . GetConfig ( " Server " , " version " ) [ 1 ] )
2025-04-19 02:54:47 +08:00
@app.post ( f ' { APP_PREFIX } /api/savePeerScheduleJob ' )
2024-06-25 23:02:13 +08:00
def API_savePeerScheduleJob ( ) :
data = request . json
2025-04-19 02:54:47 +08:00
if " Job " not in data . keys ( ) :
2024-06-25 23:02:13 +08:00
return ResponseObject ( False , " Please specify job " )
job : dict = data [ ' Job ' ]
if " Peer " not in job . keys ( ) or " Configuration " not in job . keys ( ) :
return ResponseObject ( False , " Please specify peer and configuration " )
configuration = WireguardConfigurations . get ( job [ ' Configuration ' ] )
2025-04-19 02:54:47 +08:00
if configuration is None :
return ResponseObject ( False , " Configuration does not exist " )
2024-06-25 23:02:13 +08:00
f , fp = configuration . searchPeer ( job [ ' Peer ' ] )
if not f :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Peer does not exist " )
2025-04-19 02:54:47 +08:00
2024-06-25 23:02:13 +08:00
s , p = AllPeerJobs . saveJob ( PeerJob (
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
if s :
return ResponseObject ( s , data = p )
return ResponseObject ( s , message = p )
2025-04-19 02:54:47 +08:00
@app.post ( f ' { APP_PREFIX } /api/deletePeerScheduleJob ' )
2024-07-01 00:58:02 +08:00
def API_deletePeerScheduleJob ( ) :
data = request . json
2025-04-19 02:54:47 +08:00
if " Job " not in data . keys ( ) :
2024-07-01 00:58:02 +08:00
return ResponseObject ( False , " Please specify job " )
job : dict = data [ ' Job ' ]
if " Peer " not in job . keys ( ) or " Configuration " not in job . keys ( ) :
return ResponseObject ( False , " Please specify peer and configuration " )
configuration = WireguardConfigurations . get ( job [ ' Configuration ' ] )
2025-04-19 02:54:47 +08:00
if configuration is None :
return ResponseObject ( False , " Configuration does not exist " )
2024-07-01 00:58:02 +08:00
f , fp = configuration . searchPeer ( job [ ' Peer ' ] )
if not f :
2024-09-22 21:50:30 +08:00
return ResponseObject ( False , " Peer does not exist " )
2024-07-01 00:58:02 +08:00
s , p = AllPeerJobs . deleteJob ( PeerJob (
job [ ' JobID ' ] , job [ ' Configuration ' ] , job [ ' Peer ' ] , job [ ' Field ' ] , job [ ' Operator ' ] , job [ ' Value ' ] ,
job [ ' CreationDate ' ] , job [ ' ExpireDate ' ] , job [ ' Action ' ] ) )
if s :
2025-05-10 18:16:29 +08:00
return ResponseObject ( s )
2024-07-01 00:58:02 +08:00
return ResponseObject ( s , message = p )
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/getPeerScheduleJobLogs/<configName> ' )
2024-07-29 18:40:07 -04:00
def API_getPeerScheduleJobLogs ( configName ) :
if configName not in WireguardConfigurations . keys ( ) :
return ResponseObject ( False , " Configuration does not exist " )
data = request . args . get ( " requestAll " )
requestAll = False
if data is not None and data == " true " :
requestAll = True
return ResponseObject ( data = JobLogger . getLogs ( requestAll , configName ) )
2024-12-26 00:06:37 +08:00
'''
File Download
'''
@app.get ( f ' { APP_PREFIX } /fileDownload ' )
def API_download ( ) :
file = request . args . get ( ' file ' )
if file is None or len ( file ) == 0 :
return ResponseObject ( False , " Please specify a file " )
if os . path . exists ( os . path . join ( ' download ' , file ) ) :
return send_file ( os . path . join ( ' download ' , file ) , as_attachment = True )
else :
return ResponseObject ( False , " File does not exist " )
2024-06-25 23:02:13 +08:00
'''
Tools
'''
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/ping/getAllPeersIpAddress ' )
2024-06-18 03:16:42 +08:00
def API_ping_getAllPeersIpAddress ( ) :
ips = { }
for c in WireguardConfigurations . values ( ) :
cips = { }
for p in c . Peers :
allowed_ip = p . allowed_ip . replace ( " " , " " ) . split ( " , " )
parsed = [ ]
for x in allowed_ip :
2024-09-19 22:32:15 +08:00
try :
ip = ipaddress . ip_network ( x , strict = False )
except ValueError as e :
print ( f " { p . id } - { c . Name } " )
2024-06-18 03:16:42 +08:00
if len ( list ( ip . hosts ( ) ) ) == 1 :
parsed . append ( str ( ip . hosts ( ) [ 0 ] ) )
endpoint = p . endpoint . replace ( " " , " " ) . replace ( " (none) " , " " )
if len ( p . name ) > 0 :
cips [ f " { p . name } - { p . id } " ] = {
" allowed_ips " : parsed ,
" endpoint " : endpoint
}
2021-08-14 17:13:16 -04:00
else :
2024-06-18 03:16:42 +08:00
cips [ f " { p . id } " ] = {
" allowed_ips " : parsed ,
" endpoint " : endpoint
}
ips [ c . Name ] = cips
return ResponseObject ( data = ips )
2022-01-02 16:35:39 +03:00
2024-10-02 17:09:35 +08:00
import requests
2022-01-02 16:35:39 +03:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/ping/execute ' )
2024-06-18 03:16:42 +08:00
def API_ping_execute ( ) :
if " ipAddress " in request . args . keys ( ) and " count " in request . args . keys ( ) :
ip = request . args [ ' ipAddress ' ]
count = request . args [ ' count ' ]
try :
if ip is not None and len ( ip ) > 0 and count is not None and count . isnumeric ( ) :
result = ping ( ip , count = int ( count ) , source = None )
2024-10-02 17:09:35 +08:00
data = {
2024-06-18 03:16:42 +08:00
" 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 ,
2024-10-02 17:09:35 +08:00
" 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 ( )
except Exception as e :
pass
return ResponseObject ( data = data )
2024-06-18 03:16:42 +08:00
return ResponseObject ( False , " Please specify an IP Address (v4/v6) " )
except Exception as exp :
return ResponseObject ( False , exp )
return ResponseObject ( False , " Please provide ipAddress and count " )
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/traceroute/execute ' )
2024-06-18 03:16:42 +08:00
def API_traceroute_execute ( ) :
if " ipAddress " in request . args . keys ( ) and len ( request . args . get ( " ipAddress " ) ) > 0 :
ipAddress = request . args . get ( ' ipAddress ' )
try :
2024-10-02 17:09:35 +08:00
tracerouteResult = traceroute ( ipAddress , timeout = 1 , max_hops = 64 )
2024-06-18 03:16:42 +08:00
result = [ ]
for hop in tracerouteResult :
if len ( result ) > 1 :
skipped = False
for i in range ( result [ - 1 ] [ " hop " ] + 1 , hop . distance ) :
result . append (
{
" hop " : i ,
" ip " : " * " ,
" avg_rtt " : " * " ,
" min_rtt " : " * " ,
" max_rtt " : " * "
}
)
skip = True
if skipped : continue
result . append (
{
" hop " : hop . distance ,
" ip " : hop . address ,
" avg_rtt " : hop . avg_rtt ,
" min_rtt " : hop . min_rtt ,
" max_rtt " : hop . max_rtt
} )
2024-10-02 17:09:35 +08:00
try :
r = requests . post ( f " http://ip-api.com/batch?fields=city,country,lat,lon,query " ,
data = json . dumps ( [ x [ ' ip ' ] for x in result ] ) )
d = r . json ( )
for i in range ( len ( result ) ) :
2025-02-12 20:07:37 +08:00
result [ i ] [ ' geo ' ] = d [ i ]
2024-10-02 17:09:35 +08:00
except Exception as e :
2025-02-12 20:07:37 +08:00
return ResponseObject ( data = result , message = " Failed to request IP address geolocation " )
2024-06-18 03:16:42 +08:00
return ResponseObject ( data = result )
except Exception as exp :
return ResponseObject ( False , exp )
else :
return ResponseObject ( False , " Please provide ipAddress " )
2021-09-03 17:32:51 -04:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/getDashboardUpdate ' )
2024-08-17 00:31:46 -04:00
def API_getDashboardUpdate ( ) :
2024-08-19 21:30:47 -04:00
import urllib . request as req
2024-08-17 00:31:46 -04:00
try :
r = req . urlopen ( " https://api.github.com/repos/donaldzou/WGDashboard/releases/latest " , timeout = 5 ) . read ( )
data = dict ( json . loads ( r ) )
tagName = data . get ( ' tag_name ' )
htmlUrl = data . get ( ' html_url ' )
if tagName is not None and htmlUrl is not None :
2025-02-13 23:04:27 -09:00
if version . parse ( tagName ) > version . parse ( DASHBOARD_VERSION ) :
2024-09-19 22:32:15 +08:00
return ResponseObject ( message = f " { tagName } is now available for update! " , data = htmlUrl )
2024-08-17 00:31:46 -04:00
else :
return ResponseObject ( message = " You ' re on the latest version " )
return ResponseObject ( False )
2025-02-12 20:07:37 +08:00
except Exception as e :
2024-08-19 21:30:47 -04:00
return ResponseObject ( False , f " Request to GitHub API failed. " )
2021-09-03 17:32:51 -04:00
2024-06-18 03:16:42 +08:00
'''
Sign Up
'''
2022-03-24 02:10:52 -04:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/isTotpEnabled ' )
2024-06-18 03:16:42 +08:00
def API_isTotpEnabled ( ) :
2024-08-05 15:39:11 -04:00
return (
ResponseObject ( data = DashboardConfig . GetConfig ( " Account " , " enable_totp " ) [ 1 ] and DashboardConfig . GetConfig ( " Account " , " totp_verified " ) [ 1 ] ) )
2022-03-21 22:33:19 -04:00
2022-03-24 02:10:52 -04:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } /api/Welcome_GetTotpLink ' )
2024-06-18 03:16:42 +08:00
def API_Welcome_GetTotpLink ( ) :
2024-08-05 15:39:11 -04:00
if not DashboardConfig . GetConfig ( " Account " , " totp_verified " ) [ 1 ] :
2025-05-03 16:13:30 +08:00
DashboardConfig . SetConfig ( " Account " , " totp_key " , pyotp . random_base32 ( ) , True )
2024-06-18 03:16:42 +08:00
return ResponseObject (
data = pyotp . totp . TOTP ( DashboardConfig . GetConfig ( " Account " , " totp_key " ) [ 1 ] ) . provisioning_uri (
issuer_name = " WGDashboard " ) )
return ResponseObject ( False )
2022-03-24 02:10:52 -04:00
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/Welcome_VerifyTotpLink ' )
2024-06-18 03:16:42 +08:00
def API_Welcome_VerifyTotpLink ( ) :
2023-11-28 16:37:16 -05:00
data = request . get_json ( )
2024-08-05 15:39:11 -04:00
totp = pyotp . TOTP ( DashboardConfig . GetConfig ( " Account " , " totp_key " ) [ 1 ] ) . now ( )
if totp == data [ ' totp ' ] :
DashboardConfig . SetConfig ( " Account " , " totp_verified " , " true " )
DashboardConfig . SetConfig ( " Account " , " enable_totp " , " true " )
return ResponseObject ( totp == data [ ' totp ' ] )
2023-11-28 16:37:16 -05:00
2024-09-24 22:54:18 +08:00
@app.post ( f ' { APP_PREFIX } /api/Welcome_Finish ' )
2024-06-18 03:16:42 +08:00
def API_Welcome_Finish ( ) :
2022-04-23 00:34:11 -04:00
data = request . get_json ( )
2024-06-18 03:16:42 +08:00
if DashboardConfig . GetConfig ( " Other " , " welcome_session " ) [ 1 ] :
if data [ " username " ] == " " :
return ResponseObject ( False , " Username cannot be blank. " )
2022-04-23 00:34:11 -04:00
2024-06-18 03:16:42 +08:00
if data [ " newPassword " ] == " " or len ( data [ " newPassword " ] ) < 8 :
return ResponseObject ( False , " Password must be at least 8 characters " )
2022-04-21 15:11:01 -04:00
2024-06-18 03:16:42 +08:00
updateUsername , updateUsernameErr = DashboardConfig . SetConfig ( " Account " , " username " , data [ " username " ] )
updatePassword , updatePasswordErr = DashboardConfig . SetConfig ( " Account " , " password " ,
{
" newPassword " : data [ " newPassword " ] ,
2024-08-02 17:27:28 -04:00
" repeatNewPassword " : data [ " repeatNewPassword " ] ,
2024-06-18 03:16:42 +08:00
" currentPassword " : " admin "
} )
2024-08-05 15:39:11 -04:00
if not updateUsername or not updatePassword :
return ResponseObject ( False , f " { updateUsernameErr } , { updatePasswordErr } " . strip ( " , " ) )
2021-12-26 02:26:39 +03:00
2024-06-18 03:16:42 +08:00
DashboardConfig . SetConfig ( " Other " , " welcome_session " , False )
return ResponseObject ( )
2022-01-02 16:35:39 +03:00
2024-09-23 00:17:30 +08:00
class Locale :
def __init__ ( self ) :
self . localePath = ' ./static/locale/ '
self . activeLanguages = { }
with open ( os . path . join ( f " { self . localePath } active_languages.json " ) , " r " ) as f :
2025-04-20 02:40:36 +08:00
self . activeLanguages = sorted ( json . loads ( ' ' . join ( f . readlines ( ) ) ) , key = lambda x : x [ ' lang_name ' ] )
2024-09-23 00:17:30 +08:00
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 " )
else :
DashboardConfig . SetConfig ( " Server " , " dashboard_language " , lang_id )
Locale = Locale ( )
@app.get ( f ' { APP_PREFIX } /api/locale ' )
def API_Locale_CurrentLang ( ) :
return ResponseObject ( data = Locale . getLanguage ( ) )
@app.get ( f ' { APP_PREFIX } /api/locale/available ' )
def API_Locale_Available ( ) :
return ResponseObject ( data = Locale . activeLanguages )
@app.post ( f ' { APP_PREFIX } /api/locale/update ' )
def API_Locale_Update ( ) :
data = request . get_json ( )
if ' lang_id ' not in data . keys ( ) :
return ResponseObject ( False , " Please specify a lang_id " )
Locale . updateLanguage ( data [ ' lang_id ' ] )
return ResponseObject ( data = Locale . getLanguage ( ) )
2024-09-09 23:43:55 +08:00
2025-01-08 18:09:05 +08:00
@app.get ( f ' { APP_PREFIX } /api/email/ready ' )
def API_Email_Ready ( ) :
return ResponseObject ( EmailSender . ready ( ) )
@app.post ( f ' { APP_PREFIX } /api/email/send ' )
def API_Email_Send ( ) :
data = request . get_json ( )
2025-01-13 16:47:15 +08:00
if " Receiver " not in data . keys ( ) :
2025-01-08 18:09:05 +08:00
return ResponseObject ( False , " Please at least specify receiver " )
2025-01-13 16:47:15 +08:00
body = data . get ( ' Body ' , ' ' )
2025-01-16 00:44:22 +08:00
download = None
2025-04-19 02:54:47 +08:00
if ( " ConfigurationName " in data . keys ( )
and " Peer " in data . keys ( ) ) :
2025-01-13 16:47:15 +08:00
if data . get ( ' ConfigurationName ' ) in WireguardConfigurations . keys ( ) :
configuration = WireguardConfigurations . get ( data . get ( ' ConfigurationName ' ) )
attachmentName = " "
if configuration is not None :
fp , p = configuration . searchPeer ( data . get ( ' Peer ' ) )
if fp :
template = Template ( body )
download = p . downloadPeer ( )
body = template . render ( peer = p . toJson ( ) , configurationFile = download )
if data . get ( ' IncludeAttachment ' , False ) :
u = str ( uuid4 ( ) )
attachmentName = f ' { u } .conf '
2025-04-19 02:54:47 +08:00
with open ( os . path . join ( ' ./attachments ' , attachmentName , ) , ' w+ ' ) as f :
f . write ( download [ ' file ' ] )
2025-01-13 16:47:15 +08:00
s , m = EmailSender . send ( data . get ( ' Receiver ' ) , data . get ( ' Subject ' , ' ' ) , body ,
2025-04-19 02:54:47 +08:00
data . get ( ' IncludeAttachment ' , False ) , ( attachmentName if download else ' ' ) )
2025-01-08 18:09:05 +08:00
return ResponseObject ( s , m )
2025-01-16 00:44:22 +08:00
@app.post ( f ' { APP_PREFIX } /api/email/previewBody ' )
def API_Email_PreviewBody ( ) :
data = request . get_json ( )
body = data . get ( ' Body ' , ' ' )
if len ( body ) == 0 :
return ResponseObject ( False , " Nothing to preview " )
if ( " ConfigurationName " not in data . keys ( )
or " Peer " not in data . keys ( ) or data . get ( ' ConfigurationName ' ) not in WireguardConfigurations . keys ( ) ) :
return ResponseObject ( False , " Please specify configuration and peer " )
configuration = WireguardConfigurations . get ( data . get ( ' ConfigurationName ' ) )
fp , p = configuration . searchPeer ( data . get ( ' Peer ' ) )
if not fp :
return ResponseObject ( False , " Peer does not exist " )
try :
template = Template ( body )
download = p . downloadPeer ( )
body = template . render ( peer = p . toJson ( ) , configurationFile = download )
return ResponseObject ( data = body )
except Exception as e :
return ResponseObject ( False , message = str ( e ) )
2024-11-27 18:26:13 +08:00
@app.get ( f ' { APP_PREFIX } /api/systemStatus ' )
def API_SystemStatus ( ) :
2025-01-24 19:19:17 +08:00
return ResponseObject ( data = SystemStatus )
2024-11-27 18:26:13 +08:00
2024-12-04 17:50:16 +08:00
@app.get ( f ' { APP_PREFIX } /api/protocolsEnabled ' )
def API_ProtocolsEnabled ( ) :
return ResponseObject ( data = ProtocolsEnabled ( ) )
2024-11-27 18:26:13 +08:00
2024-09-24 22:54:18 +08:00
@app.get ( f ' { APP_PREFIX } / ' )
2024-06-18 03:16:42 +08:00
def index ( ) :
2024-11-07 18:33:39 +08:00
return render_template ( ' index.html ' )
2024-01-09 00:25:47 -05:00
2025-02-12 20:07:37 +08:00
def peerInformationBackgroundThread ( ) :
2024-09-06 16:31:54 +08:00
global WireguardConfigurations
print ( f " [WGDashboard] Background Thread #1 Started " , flush = True )
time . sleep ( 10 )
while True :
with app . app_context ( ) :
2024-06-18 03:16:42 +08:00
for c in WireguardConfigurations . values ( ) :
if c . getStatus ( ) :
try :
c . getPeersTransfer ( )
c . getPeersLatestHandshake ( )
c . getPeersEndpoint ( )
2024-08-09 17:29:57 -04:00
c . getPeersList ( )
2024-08-19 16:50:00 -04:00
c . getRestrictedPeersList ( )
2024-06-18 03:16:42 +08:00
except Exception as e :
2024-08-04 20:17:29 -04:00
print ( f " [WGDashboard] Background Thread #1 Error: { str ( e ) } " , flush = True )
2024-09-06 16:31:54 +08:00
time . sleep ( 10 )
2022-01-02 16:35:39 +03:00
2024-07-01 00:58:02 +08:00
def peerJobScheduleBackgroundThread ( ) :
with app . app_context ( ) :
2024-08-04 20:17:29 -04:00
print ( f " [WGDashboard] Background Thread #2 Started " , flush = True )
2024-07-27 18:51:43 -04:00
time . sleep ( 10 )
2024-07-01 00:58:02 +08:00
while True :
AllPeerJobs . runJob ( )
2024-08-04 01:31:31 -04:00
time . sleep ( 180 )
2024-07-01 00:58:02 +08:00
2024-06-18 03:40:25 +08:00
def gunicornConfig ( ) :
_ , app_ip = DashboardConfig . GetConfig ( " Server " , " app_ip " )
_ , app_port = DashboardConfig . GetConfig ( " Server " , " app_port " )
return app_ip , app_port
2024-12-04 17:50:16 +08:00
def ProtocolsEnabled ( ) - > list [ str ] :
2024-12-02 16:34:26 +08:00
from shutil import which
2024-12-04 17:50:16 +08:00
protocols = [ ]
if which ( ' awg ' ) is not None and which ( ' awg-quick ' ) is not None :
protocols . append ( " awg " )
if which ( ' wg ' ) is not None and which ( ' wg-quick ' ) is not None :
protocols . append ( " wg " )
return protocols
2024-12-02 16:34:26 +08:00
2024-12-26 00:06:37 +08:00
def InitWireguardConfigurationsList ( startup : bool = False ) :
if os . path . exists ( DashboardConfig . GetConfig ( " Server " , " wg_conf_path " ) [ 1 ] ) :
confs = os . listdir ( DashboardConfig . GetConfig ( " Server " , " wg_conf_path " ) [ 1 ] )
confs . sort ( )
for i in confs :
if RegexMatch ( " ^(. { 1,}).(conf)$ " , i ) :
i = i . replace ( ' .conf ' , ' ' )
try :
if i in WireguardConfigurations . keys ( ) :
if WireguardConfigurations [ i ] . configurationFileChanged ( ) :
WireguardConfigurations [ i ] = WireguardConfiguration ( i )
else :
WireguardConfigurations [ i ] = WireguardConfiguration ( i , startup = startup )
except WireguardConfiguration . InvalidConfigurationFileException as e :
print ( f " { i } have an invalid configuration file. " )
2024-11-25 22:11:51 +08:00
2024-12-04 17:50:16 +08:00
if " awg " in ProtocolsEnabled ( ) :
2024-12-02 16:34:26 +08:00
confs = os . listdir ( DashboardConfig . GetConfig ( " Server " , " awg_conf_path " ) [ 1 ] )
confs . sort ( )
for i in confs :
if RegexMatch ( " ^(. { 1,}).(conf)$ " , i ) :
i = i . replace ( ' .conf ' , ' ' )
try :
if i in WireguardConfigurations . keys ( ) :
if WireguardConfigurations [ i ] . configurationFileChanged ( ) :
WireguardConfigurations [ i ] = AmneziaWireguardConfiguration ( i )
else :
WireguardConfigurations [ i ] = AmneziaWireguardConfiguration ( i , startup = startup )
except WireguardConfigurations . InvalidConfigurationFileException as e :
print ( f " { i } have an invalid configuration file. " )
2024-12-02 15:09:54 +08:00
2025-05-08 17:27:49 +08:00
AllPeerShareLinks : PeerShareLinks = PeerShareLinks ( DashboardConfig )
2024-07-30 18:45:05 -04:00
AllPeerJobs : PeerJobs = PeerJobs ( )
2025-05-05 00:40:29 +08:00
JobLogger : PeerJobLogger = PeerJobLogger ( CONFIGURATION_PATH , AllPeerJobs , DashboardConfig )
DashboardLogger : DashboardLogger = DashboardLogger ( CONFIGURATION_PATH , DashboardConfig )
2024-06-19 17:09:58 +08:00
_ , app_ip = DashboardConfig . GetConfig ( " Server " , " app_ip " )
_ , app_port = DashboardConfig . GetConfig ( " Server " , " app_port " )
_ , WG_CONF_PATH = DashboardConfig . GetConfig ( " Server " , " wg_conf_path " )
2024-08-03 13:25:57 -04:00
WireguardConfigurations : dict [ str , WireguardConfiguration ] = { }
2024-12-02 15:09:54 +08:00
AmneziaWireguardConfigurations : dict [ str , AmneziaWireguardConfiguration ] = { }
2024-11-25 22:11:51 +08:00
InitWireguardConfigurationsList ( startup = True )
2024-06-18 03:40:25 +08:00
2024-08-05 15:39:11 -04:00
def startThreads ( ) :
2025-02-12 20:07:37 +08:00
bgThread = threading . Thread ( target = peerInformationBackgroundThread , daemon = True )
2024-08-05 15:39:11 -04:00
bgThread . start ( )
2025-02-12 20:07:37 +08:00
scheduleJobThread = threading . Thread ( target = peerJobScheduleBackgroundThread , daemon = True )
2024-08-05 15:39:11 -04:00
scheduleJobThread . start ( )
2024-07-01 00:58:02 +08:00
2021-10-18 02:24:09 +03:00
if __name__ == " __main__ " :
2024-08-05 15:39:11 -04:00
startThreads ( )
2025-04-24 18:51:26 +03:30
app . run ( host = app_ip , debug = False , port = app_port )