From aba0f5bd092c9b7becc6997a75629b84533dafb5 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Thu, 23 Apr 2026 21:44:06 -0400 Subject: [PATCH] Honor seeded Buildroot setup config --- buildroot-manage.sh | 1 + config.yaml.example | 3 +++ repeater/web/api_endpoints.py | 29 +++++++++++++++++++++++------ repeater/web/auth_endpoints.py | 16 ++++++++++++++-- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/buildroot-manage.sh b/buildroot-manage.sh index 102edfc..2eb1066 100644 --- a/buildroot-manage.sh +++ b/buildroot-manage.sh @@ -776,6 +776,7 @@ security = repeater.setdefault("security", {}) radio = data.setdefault("radio", {}) repeater["node_name"] = node_name +repeater["setup_complete"] = True security["admin_password"] = admin_password security["jwt_secret"] = jwt_secret diff --git a/config.yaml.example b/config.yaml.example index 1060e7b..9336dc5 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -6,6 +6,9 @@ repeater: # Node name for logging and identification node_name: "mesh-repeater-01" + # Set true once initial provisioning is complete so the web UI can skip /setup + setup_complete: false + # TX mode: forward | monitor | no_tx (default: forward) # forward = repeat on; monitor = no repeat but companions/tenants can send; no_tx = all TX off # mode: forward diff --git a/repeater/web/api_endpoints.py b/repeater/web/api_endpoints.py index 4776038..a98d3e2 100644 --- a/repeater/web/api_endpoints.py +++ b/repeater/web/api_endpoints.py @@ -7,6 +7,7 @@ from pathlib import Path from typing import Callable, Optional import cherrypy +import yaml from pymc_core.protocol import CryptoUtils from repeater import __version__ @@ -196,6 +197,14 @@ class APIEndpoints: def _is_cors_enabled(self): return self.config.get("web", {}).get("cors_enabled", False) + def _load_live_config(self): + """Prefer the on-disk config when checking first-boot/setup state.""" + try: + with open(self._config_path, "r", encoding="utf-8") as f: + return yaml.safe_load(f) or {} + except Exception: + return self.config or {} + def _set_cors_headers(self): if self._is_cors_enabled(): cherrypy.response.headers["Access-Control-Allow-Origin"] = "*" @@ -302,24 +311,27 @@ class APIEndpoints: def needs_setup(self): """Check if the repeater needs initial setup configuration""" try: - config = self.config + config = self._load_live_config() # Check for default values that indicate first-time setup - node_name = config.get("repeater", {}).get("node_name", "") + repeater_config = config.get("repeater", {}) + node_name = repeater_config.get("node_name", "") has_default_name = node_name in ["mesh-repeater-01", ""] - admin_password = ( - config.get("repeater", {}).get("security", {}).get("admin_password", "") - ) + admin_password = repeater_config.get("security", {}).get("admin_password", "") has_default_password = admin_password in ["admin123", ""] + setup_complete = bool(repeater_config.get("setup_complete", False)) - needs_setup = has_default_name or has_default_password + needs_setup = (has_default_name or has_default_password) and not ( + setup_complete and not has_default_name and not has_default_password + ) return { "needs_setup": needs_setup, "reasons": { "default_name": has_default_name, "default_password": has_default_password, + "setup_complete": setup_complete, }, } except Exception as e: @@ -478,6 +490,7 @@ class APIEndpoints: if "repeater" not in config_yaml: config_yaml["repeater"] = {} config_yaml["repeater"]["node_name"] = node_name + config_yaml["repeater"]["setup_complete"] = True if "security" not in config_yaml["repeater"]: config_yaml["repeater"]["security"] = {} @@ -561,6 +574,10 @@ class APIEndpoints: with open(self._config_path, "w") as f: yaml.dump(config_yaml, f, default_flow_style=False, sort_keys=False) + # Keep in-memory config aligned until the restart lands. + self.config.clear() + self.config.update(config_yaml) + logger.info( f"Setup wizard completed: node_name={node_name}, hardware={hardware_key}, freq={freq_mhz}MHz" ) diff --git a/repeater/web/auth_endpoints.py b/repeater/web/auth_endpoints.py index 2ceb572..8361035 100644 --- a/repeater/web/auth_endpoints.py +++ b/repeater/web/auth_endpoints.py @@ -3,6 +3,7 @@ Authentication endpoints for login and token management """ import cherrypy import logging +import yaml from .auth.middleware import require_auth logger = logging.getLogger(__name__) @@ -152,6 +153,16 @@ class AuthEndpoints: self.jwt_handler = jwt_handler self.token_manager = token_manager self.config_manager = config_manager + + def _load_live_config(self): + config_path = getattr(self.config_manager, "config_path", None) + if not config_path: + return self.config + try: + with open(config_path, "r", encoding="utf-8") as f: + return yaml.safe_load(f) or {} + except Exception: + return self.config @cherrypy.expose def login(self, **kwargs): @@ -185,7 +196,8 @@ class AuthEndpoints: # Validate credentials against config # Check if username is 'admin' and password matches config - repeater_config = self.config.get('repeater', {}) + live_config = self._load_live_config() + repeater_config = live_config.get('repeater', {}) security_config = repeater_config.get('security', {}) config_password = security_config.get('admin_password', '') @@ -460,4 +472,4 @@ class AuthEndpoints: return json.dumps({ 'success': False, 'error': 'Failed to change password' - }).encode('utf-8') \ No newline at end of file + }).encode('utf-8')