mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-08-07 09:23:06 +02:00
feat: add global flood policy configuration and API endpoint for updates
This commit is contained in:
@@ -34,6 +34,13 @@ repeater:
|
||||
# Recommended: 10 hours for typical deployments
|
||||
send_advert_interval_hours: 10
|
||||
|
||||
# Mesh Network Configuration
|
||||
mesh:
|
||||
# Global flood policy - controls whether the repeater allows or denies flooding by default
|
||||
# true = allow flooding globally, false = deny flooding globally
|
||||
# Individual transport keys can override this setting
|
||||
global_flood_allow: false
|
||||
|
||||
radio:
|
||||
# Frequency in Hz (869.618 MHz for EU)
|
||||
frequency: 869618000
|
||||
|
||||
@@ -45,6 +45,70 @@ def load_config(config_path: Optional[str] = None) -> Dict[str, Any]:
|
||||
return config
|
||||
|
||||
|
||||
def save_config(config_data: Dict[str, Any], config_path: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Save configuration to YAML file.
|
||||
|
||||
Args:
|
||||
config_data: Configuration dictionary to save
|
||||
config_path: Path to config file (uses default if None)
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
if config_path is None:
|
||||
config_path = os.getenv("PYMC_REPEATER_CONFIG", "/etc/pymc_repeater/config.yaml")
|
||||
|
||||
try:
|
||||
# Create backup of existing config
|
||||
config_file = Path(config_path)
|
||||
if config_file.exists():
|
||||
backup_path = config_file.with_suffix('.yaml.backup')
|
||||
config_file.rename(backup_path)
|
||||
logger.info(f"Created backup at {backup_path}")
|
||||
|
||||
# Save new config
|
||||
with open(config_path, 'w') as f:
|
||||
yaml.safe_dump(config_data, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
logger.info(f"Saved configuration to {config_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to save configuration: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def update_global_flood_policy(allow: bool, config_path: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Update the global flood policy in the configuration.
|
||||
|
||||
Args:
|
||||
allow: True to allow flooding globally, False to deny
|
||||
config_path: Path to config file (uses default if None)
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Load current config
|
||||
config = load_config(config_path)
|
||||
|
||||
# Ensure mesh section exists
|
||||
if "mesh" not in config:
|
||||
config["mesh"] = {}
|
||||
|
||||
# Set global flood policy
|
||||
config["mesh"]["global_flood_allow"] = allow
|
||||
|
||||
# Save updated config
|
||||
return save_config(config, config_path)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update global flood policy: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _load_or_create_identity_key(path: Optional[str] = None) -> bytes:
|
||||
|
||||
if path is None:
|
||||
|
||||
@@ -5,6 +5,7 @@ from datetime import datetime
|
||||
from typing import Callable, Optional
|
||||
import cherrypy
|
||||
from repeater import __version__
|
||||
from repeater.config import update_global_flood_policy
|
||||
from .cad_calibration_engine import CADCalibrationEngine
|
||||
|
||||
logger = logging.getLogger("HTTPServer")
|
||||
@@ -804,4 +805,42 @@ class APIEndpoints:
|
||||
return self._error("Invalid key_id format")
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting transport key: {e}")
|
||||
return self._error(e)
|
||||
return self._error(e)
|
||||
|
||||
@cherrypy.expose
|
||||
@cors_enabled
|
||||
@cherrypy.tools.json_in()
|
||||
def global_flood_policy(self):
|
||||
"""
|
||||
Update global flood policy configuration
|
||||
|
||||
POST /global_flood_policy
|
||||
Body: {"global_flood_allow": true/false}
|
||||
"""
|
||||
if cherrypy.request.method == "POST":
|
||||
try:
|
||||
data = cherrypy.request.json or {}
|
||||
global_flood_allow = data.get("global_flood_allow")
|
||||
|
||||
if global_flood_allow is None:
|
||||
return self._error("Missing required field: global_flood_allow")
|
||||
|
||||
if not isinstance(global_flood_allow, bool):
|
||||
return self._error("global_flood_allow must be a boolean value")
|
||||
|
||||
# Update the configuration
|
||||
success = update_global_flood_policy(global_flood_allow)
|
||||
|
||||
if success:
|
||||
return self._success(
|
||||
{"global_flood_allow": global_flood_allow},
|
||||
message=f"Global flood policy updated to {'allow' if global_flood_allow else 'deny'}"
|
||||
)
|
||||
else:
|
||||
return self._error("Failed to update global flood policy configuration")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating global flood policy: {e}")
|
||||
return self._error(e)
|
||||
else:
|
||||
return self._error("Method not supported")
|
||||
Reference in New Issue
Block a user