diff --git a/src/config.ini b/src/config.ini index 74661e9..834e119 100644 --- a/src/config.ini +++ b/src/config.ini @@ -13,14 +13,15 @@ debug_enabled = True wg_conf_path = /etc/wireguard awg_conf_path = /etc/amnezia/amneziawg app_prefix = -auth_req = False +auth_req = True version = v5.0.0 dashboard_refresh_interval = 60000 dashboard_peer_list_display = grid dashboard_sort = status dashboard_theme = dark wgdashboard_apikey = true -dashboard_language = en-US +#wgdashboard_language = nl-NL +wgdashboard_language = en-US log_level = DEBUG [Account] diff --git a/src/main.py b/src/main.py index 91172e9..1e2b6f9 100644 --- a/src/main.py +++ b/src/main.py @@ -8,7 +8,7 @@ import json import os import secrets -from modules.config.reader import reader +from modules.config.config import config from modules.database.database import database from modules.utilities.utilities import utilities as util @@ -18,17 +18,17 @@ from modules.routes.routes import routes if __name__ == '__main__': # Read the config file (ini) - ok, config_contents = reader.read_config() + ok, config_contents = config.read() if not ok: exit(1) - found, config_server = util.filter_config(config_contents, 'SERVER') + found, config_server = config.filter(config_contents, 'SERVER') # Configure the loglevel of WGDashboard wanted_loglevel = config_server.get('log_level', 'DEBUG').upper() setup_logger(wanted_loglevel) # Get the database configuration from thee config - found, config_database = util.filter_config(config_contents, 'DATABASE') + found, config_database = config.filter(config_contents, 'DATABASE') if not found: exit(1) @@ -46,6 +46,7 @@ if __name__ == '__main__': app.register_blueprint(routes, url_prefix=prefix) app.wgd_config = config_contents + app.locale_path = './static/locales/' app.secret_key = secrets.token_urlsafe(64) app.config['SESSION_TYPE'] = 'filesystem' diff --git a/src/modules/config/config.py b/src/modules/config/config.py new file mode 100644 index 0000000..a32f31a --- /dev/null +++ b/src/modules/config/config.py @@ -0,0 +1,38 @@ +#!/bin/env python3 + +import logging as log + +from .utilities import config_utilities + +class config(): + @staticmethod + def filter(config_contents: dict, filter_keyword: str) -> tuple[bool, dict]: + ''' + Helper function to grab a specific part of the config + ''' + for section_name, section_values in config_contents.items(): + if str(section_name).lower() == filter_keyword.lower(): + if isinstance(section_values, dict): + return True, dict(section_values) + return False, {} + + @staticmethod + def read() -> tuple[bool, dict]: + ''' + check some basic things and then return the dict containing the config data + ''' + + ok, candidate_path = config_utilities.search_known_paths() + if not ok: + return False, {} + ok, config_contents = config_utilities.verify_contents(candidate_path) + if not ok: + return False, {} + + return True, config_contents + + @staticmethod + def update(section: str, key: str, value: str) -> bool: + print(config_utilities.search_known_paths()) + print(section, key, value) + return True diff --git a/src/modules/config/reader.py b/src/modules/config/reader.py deleted file mode 100644 index 96178c8..0000000 --- a/src/modules/config/reader.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/env python3 - -import logging as log - -from .utilities import checks - -class reader(): - @staticmethod - def read_config() -> tuple[bool, dict]: - ''' - check some basic things and then return the dict containing the config data - ''' - - ok, candidate_path = checks.search_known_paths() - if not ok: - return False, {} - ok, config_contents = checks.verify_contents(candidate_path) - if not ok: - return False, {} - - return True, config_contents - - @staticmethod - def refresh_config(config_contents: dict) -> dict: - log.debug(f'refreshing config values') - return reader.read_config() \ No newline at end of file diff --git a/src/modules/config/utilities.py b/src/modules/config/utilities.py index a8614f0..e85d3b3 100644 --- a/src/modules/config/utilities.py +++ b/src/modules/config/utilities.py @@ -5,7 +5,7 @@ import logging as log import configparser as cp import os -class checks(): +class config_utilities(): @staticmethod def search_known_paths() -> tuple[bool, str]: ''' diff --git a/src/modules/routes/locale.py b/src/modules/routes/locale.py new file mode 100644 index 0000000..d961bc5 --- /dev/null +++ b/src/modules/routes/locale.py @@ -0,0 +1,49 @@ +#!/bin/env python3 + +import os +import json +import flask + +from .response import make_resp_obj + +from ..config.config import config + +class localeman: + def __init__(self): + _, self.wgd_config = config.read() + self.locale_path = flask.current_app.locale_path + + try: + path = os.path.join(self.locale_path, "supported_locales.json") + + with open(path, "r", encoding="utf-8") as locale_path: + self.active_languages = sorted( + json.load(locale_path), + key=lambda x: x.get("lang_name", "") + ) + except Exception: + self.active_languages = [] + + def get_language(self): + ok, server_config = config.filter(self.wgd_config, "SERVER") + if not ok: + return None + + lang = server_config.get("wgdashboard_language", "en") + if lang.lower() == "en": + return None + + path = os.path.join(self.locale_path, f"{lang}.json") + if os.path.isfile(path): + with open(path, "r", encoding="utf-8") as lang_file: + return json.load(lang_file) + return None + +# def update_language(self, lang_id): +# path = os.path.join(self.locale_path, f"{lang_id}.json") +# +# if not os.path.isfile(path): +# lang_id = "en" +# +# util.set_config(self.wgd_config, "Server", "dashboard_language", lang_id) +# return self.get_language() \ No newline at end of file diff --git a/src/modules/routes/routes.py b/src/modules/routes/routes.py index 0e02af7..aa759cc 100644 --- a/src/modules/routes/routes.py +++ b/src/modules/routes/routes.py @@ -5,27 +5,31 @@ import logging as log import flask import json import os +import werkzeug from .response import make_resp_obj from .utilities import helpers +from .locale import localeman from ..database.functions import functions -from ..utilities.utilities import utilities as util +from ..config.config import config routes = flask.Blueprint("routes", __name__) white_list = [ + "/", # we need to whitelist / "/client", "/static/", "/fileDownload", - "authenticate", + "/api/authenticate", + "/api/locale", "getDashboardConfiguration", "getDashboardTheme", "getDashboardVersion", "sharePeer/get", "isTotpEnabled", - "locale", "validateAuthentication", + "favicon.ico", ] @routes.before_request @@ -33,7 +37,7 @@ def auth_required(): if flask.request.method.lower() == "options": return make_resp_obj("", {"status": True}, 200) - ok, config_server = util.filter_config(flask.current_app.wgd_config, 'SERVER') + ok, config_server = config.filter(flask.current_app.wgd_config, 'SERVER') if not ok: return make_resp_obj("Internal error", {}, 500) @@ -62,14 +66,14 @@ def auth_required(): @routes.route('/api/authenticate', methods=["POST"]) def api_authenticate(): - ok, config_server = util.filter_config(flask.current_app.wgd_config, 'SERVER') + ok, config_server = config.filter(flask.current_app.wgd_config, 'SERVER') if not ok: return make_resp_obj("Internal error", {}, 500) auth_required_flag = config_server.get('auth_req', True) if not auth_required_flag: - ok, config_other = util.filter_config(flask.current_app.wgd_config, 'OTHER') + ok, config_other = config.filter(flask.current_app.wgd_config, 'OTHER') if not ok: return make_resp_obj("Internal error", {}, 500) @@ -89,23 +93,23 @@ def api_authenticate(): @routes.route('/') def index_handler(path): static_folder = flask.current_app.static_folder - template_folder = flask.current_app.template_folder + try: + safe_path = werkzeug.utils.safe_join(static_folder, path) + if not safe_path or not os.path.exists(safe_path): + raise Exception() + rel_path = os.path.relpath(safe_path, static_folder) + return flask.send_from_directory(flask.current_app.static_folder, rel_path) - safe_path = os.path.normpath(path) + except Exception: + print(f"ROUTE NOT INPLEMENTED: {path}") + return flask.send_from_directory(flask.current_app.static_folder, "index.html") - if safe_path.startswith('..'): - return make_resp_obj("Invalid request", {}, 400) +@routes.route('/api/locale') +def api_locale_handler(): + locale_manager = localeman() + locale_data = locale_manager.get_language() - file_path = os.path.join(static_folder, safe_path) - if os.path.isfile(file_path): - return flask.send_from_directory(static_folder, safe_path) - - return flask.send_from_directory(flask.current_app.static_folder, "index.html") - -@routes.route("/client") -@routes.route("/clients") -def client_handler(): - return flask.send_from_directory(CLIENT_DIST, "client.html") + return make_resp_obj("", locale_data, 200) @routes.route('/health', methods=["GET"]) @routes.route('/healthz', methods=["GET"]) diff --git a/src/modules/utilities/utilities.py b/src/modules/utilities/utilities.py index e6a9b78..988221c 100644 --- a/src/modules/utilities/utilities.py +++ b/src/modules/utilities/utilities.py @@ -5,17 +5,6 @@ import logging as log import os class utilities(): - @staticmethod - def filter_config(config_contents: dict, filter_keyword: str) -> tuple[bool, dict]: - ''' - Helper function to grab a specific part of the config - ''' - for section_name, section_values in config_contents.items(): - if str(section_name).lower() == filter_keyword.lower(): - if isinstance(section_values, dict): - return True, dict(section_values) - return False, {} - @staticmethod def ensure_directory(path: str) -> bool: ''' diff --git a/src/requirements.txt b/src/requirements.txt index 0d07826..9991e98 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,3 +1,4 @@ configparser==7.2.0 +sqlalchemy==2.0.48 Flask==3.1.3 -sqlalchemy==2.0.48 \ No newline at end of file +Werkzeug==3.1.6 \ No newline at end of file