121 lines
4.2 KiB
Python
121 lines
4.2 KiB
Python
"""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 into module globals"""
|
|
path = Path("data/config.json")
|
|
if not path.exists():
|
|
return
|
|
|
|
try:
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
# Update config module globals
|
|
for key, value in data.get("config", {}).items():
|
|
if hasattr(config_module, key):
|
|
setattr(config_module, key, value)
|
|
except Exception as e:
|
|
print(f"Error reloading config from file: {e}")
|
|
|
|
|
|
@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
|
|
}
|
|
|
|
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"]
|
|
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"
|
|
}
|
|
|
|
# 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
|
|
}
|
|
}
|
|
|
|
# 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}
|