Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9afe335384 | |||
| c14a28b093 | |||
| 568bccb37e | |||
| e5a24df13d |
@@ -1,3 +1,3 @@
|
||||
825448
|
||||
825449
|
||||
825463
|
||||
830554
|
||||
830555
|
||||
830569
|
||||
|
||||
@@ -72,6 +72,74 @@ class ConfigManager:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_secrets_path():
|
||||
"""Returns the absolute path to secrets.yaml."""
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
project_root = os.path.dirname(base_dir)
|
||||
return os.path.join(project_root, "config", "secrets.yaml")
|
||||
|
||||
@staticmethod
|
||||
def update_secrets(updates: dict) -> dict:
|
||||
"""Update secrets.yaml with new values (flattened structure)."""
|
||||
path = ConfigManager.get_secrets_path()
|
||||
|
||||
current_secrets = {}
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
current_secrets = yaml.safe_load(f) or {}
|
||||
except Exception as e:
|
||||
log.error(f"❌ Failed to read {path}: {e}")
|
||||
|
||||
# Update flattened secrets
|
||||
current_secrets.update(updates)
|
||||
|
||||
try:
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
# Header for clarity
|
||||
f.write("# TFM aInventory - Managed Secrets (Updated via Admin UI)\n")
|
||||
yaml.dump(current_secrets, f, default_flow_style=False, sort_keys=True)
|
||||
log.info(f"✅ Updated {path} with new secrets.")
|
||||
|
||||
# Reload the global config
|
||||
load_config()
|
||||
return get_config()
|
||||
except Exception as e:
|
||||
log.error(f"❌ Failed to write {path}: {e}")
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def update_keys(updates: dict) -> dict:
|
||||
"""
|
||||
Intelligent key update: routes sensitive AI keys to secrets.yaml
|
||||
and others to backend.yaml.
|
||||
"""
|
||||
secrets_updates = {}
|
||||
backend_updates = {}
|
||||
|
||||
sensitive_keys = ["GEMINI_API_KEY", "CLAUDE_API_KEY", "JWT_SECRET_KEY", "LDAP_PASSWORD"]
|
||||
|
||||
for key, val in updates.items():
|
||||
if key in sensitive_keys:
|
||||
secrets_updates[key] = val
|
||||
else:
|
||||
# Non-sensitive or complex nested settings
|
||||
backend_updates[key] = val
|
||||
|
||||
if secrets_updates:
|
||||
ConfigManager.update_secrets(secrets_updates)
|
||||
|
||||
if backend_updates:
|
||||
ConfigManager.update_config(backend_updates)
|
||||
|
||||
return get_config()
|
||||
|
||||
@staticmethod
|
||||
def get_config() -> dict:
|
||||
"""Return the current global configuration."""
|
||||
return get_config()
|
||||
|
||||
@staticmethod
|
||||
def get_masked_key(key_name: str):
|
||||
"""Returns a masked version of a configuration value or env var."""
|
||||
@@ -113,6 +181,10 @@ def update_config(updates: dict) -> dict:
|
||||
"""Update backend.yaml with new values and return updated config."""
|
||||
return ConfigManager.update_config(updates)
|
||||
|
||||
def get_config() -> dict:
|
||||
"""Return the current global configuration."""
|
||||
return ConfigManager.get_config()
|
||||
|
||||
def validate_config_file() -> bool:
|
||||
"""Validate backend.yaml syntax and required fields."""
|
||||
return ConfigManager.validate_config_file()
|
||||
|
||||
@@ -67,8 +67,11 @@ def get_ai_config(
|
||||
current_admin: auth.TokenData = Depends(auth.get_current_admin)
|
||||
):
|
||||
"""Check AI provider status and active provider."""
|
||||
gemini_key = os.environ.get("GEMINI_API_KEY")
|
||||
claude_key = os.environ.get("CLAUDE_API_KEY")
|
||||
config = ConfigManager.get_config() # Alias for config_loader.get_config
|
||||
ai_config = config.get("ai", {})
|
||||
|
||||
gemini_key = ai_config.get("gemini_api_key")
|
||||
claude_key = ai_config.get("claude_api_key")
|
||||
|
||||
provider_setting = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_provider").first()
|
||||
active_provider = provider_setting.value if provider_setting else "gemini"
|
||||
@@ -112,10 +115,14 @@ def update_ai_keys(
|
||||
if updates:
|
||||
ConfigManager.update_keys(updates)
|
||||
|
||||
# Re-load config to return accurate status
|
||||
config = ConfigManager.get_config()
|
||||
ai_cfg = config.get("ai", {})
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"gemini_configured": bool(os.environ.get("GEMINI_API_KEY")),
|
||||
"claude_configured": bool(os.environ.get("CLAUDE_API_KEY"))
|
||||
"gemini_configured": bool(ai_cfg.get("gemini_api_key")),
|
||||
"claude_configured": bool(ai_cfg.get("claude_api_key"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
BIN
backups/ainventory_2026-04-23_13-58-34.tar.gz
Normal file
BIN
backups/ainventory_2026-04-23_13-58-34.tar.gz
Normal file
Binary file not shown.
BIN
backups/ainventory_2026-04-23_14-04-08.tar.gz
Normal file
BIN
backups/ainventory_2026-04-23_14-04-08.tar.gz
Normal file
Binary file not shown.
@@ -111,10 +111,6 @@ application:
|
||||
# Environment: BACKEND_APPLICATION_LOGS_DIR or LOGS_DIR
|
||||
logs_dir: "./logs"
|
||||
|
||||
# Comma-separated list of extra allowed CORS origins (IPs/FQDNs)
|
||||
# Environment: BACKEND_APPLICATION_CORS_ORIGINS or EXTRA_ALLOWED_ORIGINS
|
||||
cors_origins: "http://localhost:8917"
|
||||
|
||||
# --- Feature Flags ---
|
||||
features:
|
||||
# Enable AI image extraction and OCR?
|
||||
|
||||
@@ -6,11 +6,9 @@
|
||||
|
||||
# --- API Connection ---
|
||||
api:
|
||||
# Base URL of the backend API
|
||||
# Default: http://localhost:8916
|
||||
# Environment: FRONTEND_API_BACKEND_URL or BACKEND_URL
|
||||
backend_url: "http://localhost:8916"
|
||||
|
||||
# Note: backend_url is dynamically injected by the launcher script
|
||||
# based on network.yaml settings to ensure SSOT integrity.
|
||||
|
||||
# API timeout in milliseconds
|
||||
# Default: 30000 (30 seconds)
|
||||
# Environment: FRONTEND_API_TIMEOUT_MS
|
||||
|
||||
@@ -38,36 +38,18 @@ ssl:
|
||||
certificate_path: ""
|
||||
|
||||
# Path to SSL private key
|
||||
# Environment: NETWORK_SSL_KEY_PATH
|
||||
# Path to SSL private key
|
||||
key_path: ""
|
||||
|
||||
# --- Proxy (Caddy) Configuration ---
|
||||
proxy:
|
||||
# Caddy log level (debug|info|warn|error)
|
||||
# Default: info
|
||||
# Environment: NETWORK_PROXY_CADDY_LOG_LEVEL
|
||||
caddy_log_level: "info"
|
||||
# --- Master Infrastructure Settings ---
|
||||
# Establish this file as the SSOT for network topology.
|
||||
application:
|
||||
# The IP address or hostname of the server (Used for CORS and Frontend API)
|
||||
# Default: localhost
|
||||
# Environment: SERVER_IP
|
||||
server_ip: "localhost"
|
||||
|
||||
# Maximum timeout for reading requests in seconds
|
||||
# Default: 60
|
||||
# Environment: NETWORK_PROXY_READ_TIMEOUT_S
|
||||
proxy_read_timeout_s: 60
|
||||
# Comma-separated list of extra allowed CORS origins (IPs/FQDNs/Subnets)
|
||||
# Environment: EXTRA_ALLOWED_ORIGINS
|
||||
cors_origins: ""
|
||||
|
||||
# Maximum request body size in MB
|
||||
# Default: 10
|
||||
# Environment: NETWORK_PROXY_MAX_REQUEST_SIZE_MB
|
||||
max_request_size_mb: 10
|
||||
|
||||
# --- CORS Policies ---
|
||||
cors:
|
||||
# Comma-separated list of allowed origins
|
||||
# Environment: NETWORK_CORS_ALLOWED_ORIGINS or EXTRA_ALLOWED_ORIGINS
|
||||
allowed_origins: "*"
|
||||
|
||||
# Allowed HTTP methods
|
||||
# Environment: NETWORK_CORS_ALLOWED_METHODS
|
||||
allowed_methods: "GET,POST,PUT,DELETE,OPTIONS"
|
||||
|
||||
# Allowed HTTP headers
|
||||
# Environment: NETWORK_CORS_ALLOWED_HEADERS
|
||||
allowed_headers: "Authorization,Content-Type,Accept"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.14.10",
|
||||
"last_build": "2026-04-23-1358",
|
||||
"version": "1.14.12",
|
||||
"last_build": "2026-04-23-1445",
|
||||
"codename": "ConfigCore",
|
||||
"commit": "a5a460e7"
|
||||
"commit": "c14a28b0"
|
||||
}
|
||||
Reference in New Issue
Block a user