diff --git a/docker/Dockerfile b/docker/Dockerfile index f17b951c..82970eba 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -104,6 +104,7 @@ ARG wg_port="51820" ENV TZ="Europe/Amsterdam" \ global_dns="9.9.9.9" \ wgd_port="10086" \ + log_level="INFO" \ public_ip="" \ WGDASH=/opt/wgdashboard diff --git a/docker/README.md b/docker/README.md index 638fbce3..e68c749c 100644 --- a/docker/README.md +++ b/docker/README.md @@ -102,6 +102,7 @@ Updating the WGDashboard container should be through 'The Docker Way' - by pulli | `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. | | `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. | | `wgd_port` | Any port that is allowed for the process | `10086` | `443` | This port is used to set the WGDashboard web port. | +| `log_level` | `DEBUG`, `INFO`, `WARNING`, `ERROR` and `CRITICAL` | `INFO` | `WARNING` | Sets the severity of the logs being displayed. | | `username` | Any non‐empty string | `-` | `admin` | Username for the WGDashboard web interface account. | | `password` | Any non‐empty string | `-` | `s3cr3tP@ss` | Password for the WGDashboard web interface account (stored hashed). | | `enable_totp` | `true`, `false` | `true` | `false` | Enable TOTP‐based two‐factor authentication for the account. | diff --git a/docker/compose.yaml b/docker/compose.yaml index d8f2eac9..70f8741b 100644 --- a/docker/compose.yaml +++ b/docker/compose.yaml @@ -12,7 +12,8 @@ services: # Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard. # By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character) # Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means. - #environment: + environment: + - log_level=WARNING #- tz= # <--- Set container timezone, default: Europe/Amsterdam. #- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me. #- wgd_port= # <--- Set the port WGDashboard will use for its web-server. diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 1765006a..70b231ee 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -137,7 +137,8 @@ set_envvars() { set_ini Peers remote_endpoint "${public_ip}" set_ini Server app_port "${wgd_port}" - + set_ini Server log_level "${log_level}" + # Account settings - process all parameters [[ -n "$username" ]] && echo "Configuring user account:" # Basic account variables diff --git a/src/dashboard.py b/src/dashboard.py index 223e9d5f..95409f7e 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -72,7 +72,6 @@ def ResponseObject(status=True, message=None, data=None, status_code = 200) -> F ''' Flask App ''' -app = Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin")) def peerInformationBackgroundThread(): global WireguardConfigurations @@ -120,7 +119,8 @@ def peerJobScheduleBackgroundThread(): def gunicornConfig(): _, app_ip = DashboardConfig.GetConfig("Server", "app_ip") _, app_port = DashboardConfig.GetConfig("Server", "app_port") - return app_ip, app_port + + return app_ip, app_port, log_level def ProtocolsEnabled() -> list[str]: from shutil import which @@ -172,16 +172,7 @@ def startThreads(): scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread, daemon=True) scheduleJobThread.start() -dictConfig({ - 'version': 1, - 'formatters': {'default': { - 'format': '[%(asctime)s] [%(levelname)s] in [%(module)s] %(message)s', - }}, - 'root': { - 'level': 'INFO' - } -}) - +app = Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin")) WireguardConfigurations: dict[str, WireguardConfiguration] = {} CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.') @@ -189,6 +180,7 @@ CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.') app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928 app.secret_key = secrets.token_urlsafe(32) app.json = CustomJsonEncoder(app) + with app.app_context(): SystemStatus = SystemStatus() DashboardConfig = DashboardConfig() @@ -199,19 +191,44 @@ with app.app_context(): DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations) DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig) NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates() + InitWireguardConfigurationsList(startup=True) + DashboardClients: DashboardClients = DashboardClients(WireguardConfigurations) - app.register_blueprint(createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients)) + app.register_blueprint( + createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients) + ) _, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix") +_, app_ip = DashboardConfig.GetConfig("Server", "app_ip") +_, app_port = DashboardConfig.GetConfig("Server", "app_port") +_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path") +_, log_level = DashboardConfig.GetConfig("Server", "log_level") + cors = CORS(app, resources={rf"{APP_PREFIX}/api/*": { "origins": "*", "methods": "DELETE, POST, GET, OPTIONS", "allow_headers": ["Content-Type", "wg-dashboard-apikey"] }}) -_, app_ip = DashboardConfig.GetConfig("Server", "app_ip") -_, app_port = DashboardConfig.GetConfig("Server", "app_port") -_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path") + +app.logger.setLevel(getattr(logging, log_level.upper(), logging.INFO)) + +dictConfig({ + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'default': { + 'format': '[%(asctime)s] [%(levelname)s] in [%(module)s] %(message)s', + }, + }, + 'handlers': { + 'wsgi': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://flask.logging.wsgi_errors_stream', + 'formatter': 'default', + }, + } +}) ''' API Routes diff --git a/src/gunicorn.conf.py b/src/gunicorn.conf.py index 1385a2ee..aea10b8c 100644 --- a/src/gunicorn.conf.py +++ b/src/gunicorn.conf.py @@ -1,7 +1,7 @@ import dashboard from datetime import datetime global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger, Dash -app_host, app_port = dashboard.gunicornConfig() +app_host, app_port, log_level = dashboard.gunicornConfig() date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S') def post_worker_init(worker): @@ -16,7 +16,7 @@ daemon = True pidfile = './gunicorn.pid' wsgi_app = "dashboard:app" accesslog = f"./log/access_{date}.log" -loglevel = "info" +loglevel = f"{log_level}" capture_output = True errorlog = f"./log/error_{date}.log" pythonpath = "., ./modules" diff --git a/src/modules/AmneziaWireguardConfiguration.py b/src/modules/AmneziaWireguardConfiguration.py index 43b8f159..2bad4c42 100644 --- a/src/modules/AmneziaWireguardConfiguration.py +++ b/src/modules/AmneziaWireguardConfiguration.py @@ -174,7 +174,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): def getPeers(self): self.Peers.clear() - current_app.logger.info(f"Refreshing {self.Name} peer list") + current_app.logger.debug(f"Refreshing {self.Name} peer list") if self.configurationFileChanged(): with open(self.configPath, 'r') as configFile: @@ -183,7 +183,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration): content = configFile.read().split('\n') try: if "[Peer]" not in content: - current_app.logger.info(f"{self.Name} config has no [Peer] section") + current_app.logger.debug(f"{self.Name} config has no [Peer] section") return peerStarts = content.index("[Peer]") diff --git a/src/modules/ConnectionString.py b/src/modules/ConnectionString.py index 43276e6f..f29d5a24 100644 --- a/src/modules/ConnectionString.py +++ b/src/modules/ConnectionString.py @@ -35,10 +35,13 @@ def ConnectionString(database_name: str) -> str: host = parser.get("Database", "host") cn = f"mysql+pymysql://{username}:{password}@{host}/{database_name}" else: - cn = f"sqlite:///{os.path.join(SQLITE_PATH, f'{database_name}.db')}" + cn = f'sqlite:///{os.path.join(sqlitePath, f"{database}.db")}' - # Ensure database exists - if not database_exists(cn): - create_database(cn) + try: + if not database_exists(cn): + create_database(cn) + except Exception as e: + current_app.logger.critical("Database error. Terminating...", e) + exit(1) return cn \ No newline at end of file diff --git a/src/modules/DashboardConfig.py b/src/modules/DashboardConfig.py index da163bcc..b3baa070 100644 --- a/src/modules/DashboardConfig.py +++ b/src/modules/DashboardConfig.py @@ -47,7 +47,8 @@ class DashboardConfig: "dashboard_sort": "status", "dashboard_theme": "dark", "dashboard_api_key": "false", - "dashboard_language": "en-US" + "dashboard_language": "en-US", + "log_level": "INFO" }, "Peers": { "peer_global_DNS": "1.1.1.1", diff --git a/src/modules/PeerJobs.py b/src/modules/PeerJobs.py index a4fa8ede..7d3f13b2 100644 --- a/src/modules/PeerJobs.py +++ b/src/modules/PeerJobs.py @@ -141,7 +141,7 @@ class PeerJobs: def runJob(self): - current_app.logger.info("Running scheduled jobs") + current_app.logger.debug("Running scheduled jobs") needToDelete = [] self.__getJobs() for job in self.Jobs: diff --git a/src/modules/WireguardConfiguration.py b/src/modules/WireguardConfiguration.py index 05c5de6e..c7cda2f4 100644 --- a/src/modules/WireguardConfiguration.py +++ b/src/modules/WireguardConfiguration.py @@ -396,7 +396,7 @@ class WireguardConfiguration: def getPeers(self): tmpList = [] - current_app.logger.info(f"Refreshing {self.Name} peer list") + current_app.logger.debug(f"Refreshing {self.Name} peer list") if self.configurationFileChanged(): with open(self.configPath, 'r') as configFile: @@ -405,7 +405,7 @@ class WireguardConfiguration: content = configFile.read().split('\n') try: if "[Peer]" not in content: - current_app.logger.info(f"{self.Name} config has no [Peer] section") + current_app.logger.debug(f"{self.Name} config has no [Peer] section") return peerStarts = content.index("[Peer]")