"""Application configuration management endpoints.""" import json from pathlib import Path from fastapi import APIRouter, HTTPException from models import ConfigUpdateRequest import config as config_module router = APIRouter(prefix="/api", tags=["config"]) def reload_config_from_file(): """Reload config values from JSON - triggers dynamic reload""" # With the new dynamic config system, we just need to call reload # which will cause all future property accesses to read from the updated file config_module.reload_config() print("[CONFIG] Configuration reloaded after settings update") @router.get("/config") async def get_config(): #Get all config values (with sensitive values masked) path = Path("data/config.json") # Default config values from config.py default_values = { "TORN_API_KEY": config_module.TORN_API_KEY, "FFSCOUTER_KEY": config_module.FFSCOUTER_KEY, "DISCORD_TOKEN": config_module.DISCORD_TOKEN, "ALLOWED_CHANNEL_ID": config_module.ALLOWED_CHANNEL_ID, "HIT_CHECK_INTERVAL": config_module.HIT_CHECK_INTERVAL, "REASSIGN_DELAY": config_module.REASSIGN_DELAY, "ASSIGNMENT_TIMEOUT": config_module.ASSIGNMENT_TIMEOUT, "ASSIGNMENT_REMINDER": config_module.ASSIGNMENT_REMINDER, "CHAIN_TIMER_THRESHOLD": config_module.CHAIN_TIMER_THRESHOLD, "AUTH_PASSWORD": config_module.AUTH_PASSWORD, "JWT_SECRET": config_module.JWT_SECRET } if path.exists(): with open(path, "r", encoding="utf-8") as f: data = json.load(f) file_values = data.get("config", {}) # Merge defaults with file values (file values take precedence) config_values = {**default_values, **file_values} else: config_values = default_values # Mask sensitive values masked_config = config_values.copy() sensitive = ["TORN_API_KEY", "FFSCOUTER_KEY", "DISCORD_TOKEN", "AUTH_PASSWORD", "JWT_SECRET"] for key in sensitive: if key in masked_config and masked_config[key]: val = str(masked_config[key]) masked_config[key] = "****" + val[-4:] if len(val) > 4 else "****" return {"config": masked_config, "sensitive_fields": sensitive} @router.post("/config") async def update_config(req: ConfigUpdateRequest): #Update a single config value path = Path("data/config.json") # Valid config keys (from config.py) valid_keys = { "TORN_API_KEY", "FFSCOUTER_KEY", "DISCORD_TOKEN", "ALLOWED_CHANNEL_ID", "HIT_CHECK_INTERVAL", "REASSIGN_DELAY", "ASSIGNMENT_TIMEOUT", "ASSIGNMENT_REMINDER", "CHAIN_TIMER_THRESHOLD", "AUTH_PASSWORD", "JWT_SECRET" } # Validate key is valid if req.key not in valid_keys: raise HTTPException(status_code=400, detail="Invalid config key") # Load existing or create from current config if path.exists(): with open(path, "r", encoding="utf-8") as f: data = json.load(f) else: data = { "comment": "Application configuration settings", "config": { "TORN_API_KEY": config_module.TORN_API_KEY, "FFSCOUTER_KEY": config_module.FFSCOUTER_KEY, "DISCORD_TOKEN": config_module.DISCORD_TOKEN, "ALLOWED_CHANNEL_ID": config_module.ALLOWED_CHANNEL_ID, "HIT_CHECK_INTERVAL": config_module.HIT_CHECK_INTERVAL, "REASSIGN_DELAY": config_module.REASSIGN_DELAY, "ASSIGNMENT_TIMEOUT": config_module.ASSIGNMENT_TIMEOUT, "ASSIGNMENT_REMINDER": config_module.ASSIGNMENT_REMINDER, "CHAIN_TIMER_THRESHOLD": config_module.CHAIN_TIMER_THRESHOLD, "AUTH_PASSWORD": config_module.AUTH_PASSWORD, "JWT_SECRET": config_module.JWT_SECRET } } # Add key if it doesn't exist in config (for backwards compatibility) if req.key not in data["config"]: print(f"Adding new config key: {req.key}") data["config"][req.key] = getattr(config_module, req.key) # Update value data["config"][req.key] = req.value # Save to file with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) # Reload config in memory reload_config_from_file() return {"status": "ok", "key": req.key}