Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96f4a5d228 | |||
| 827dfcdf2f | |||
| 9afe335384 | |||
| c14a28b093 |
@@ -1,3 +1,3 @@
|
|||||||
826698
|
831167
|
||||||
826699
|
831168
|
||||||
826713
|
831182
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
import logging
|
import logging
|
||||||
from .config_loader import load_config, get_config
|
from .config_loader import load_config as loader_load_config, get_config as loader_get_config
|
||||||
|
|
||||||
log = logging.getLogger("ainventory")
|
log = logging.getLogger("ainventory")
|
||||||
|
|
||||||
@@ -52,8 +52,8 @@ class ConfigManager:
|
|||||||
log.info(f"✅ Updated {path} with new values.")
|
log.info(f"✅ Updated {path} with new values.")
|
||||||
|
|
||||||
# Reload the global config
|
# Reload the global config
|
||||||
load_config()
|
loader_load_config()
|
||||||
return get_config()
|
return loader_get_config()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"❌ Failed to write {path}: {e}")
|
log.error(f"❌ Failed to write {path}: {e}")
|
||||||
raise
|
raise
|
||||||
@@ -72,10 +72,78 @@ class ConfigManager:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return False
|
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
|
||||||
|
loader_load_config()
|
||||||
|
return loader_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 loader_get_config()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_config() -> dict:
|
||||||
|
"""Return the current global configuration."""
|
||||||
|
return loader_get_config()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_masked_key(key_name: str):
|
def get_masked_key(key_name: str):
|
||||||
"""Returns a masked version of a configuration value or env var."""
|
"""Returns a masked version of a configuration value or env var."""
|
||||||
config = get_config()
|
config = loader_get_config()
|
||||||
|
|
||||||
# Try to find in config first
|
# Try to find in config first
|
||||||
val = None
|
val = None
|
||||||
@@ -113,6 +181,10 @@ def update_config(updates: dict) -> dict:
|
|||||||
"""Update backend.yaml with new values and return updated config."""
|
"""Update backend.yaml with new values and return updated config."""
|
||||||
return ConfigManager.update_config(updates)
|
return ConfigManager.update_config(updates)
|
||||||
|
|
||||||
|
def get_config() -> dict:
|
||||||
|
"""Return the current global configuration."""
|
||||||
|
return loader_get_config()
|
||||||
|
|
||||||
def validate_config_file() -> bool:
|
def validate_config_file() -> bool:
|
||||||
"""Validate backend.yaml syntax and required fields."""
|
"""Validate backend.yaml syntax and required fields."""
|
||||||
return ConfigManager.validate_config_file()
|
return ConfigManager.validate_config_file()
|
||||||
|
|||||||
@@ -67,8 +67,11 @@ def get_ai_config(
|
|||||||
current_admin: auth.TokenData = Depends(auth.get_current_admin)
|
current_admin: auth.TokenData = Depends(auth.get_current_admin)
|
||||||
):
|
):
|
||||||
"""Check AI provider status and active provider."""
|
"""Check AI provider status and active provider."""
|
||||||
gemini_key = os.environ.get("GEMINI_API_KEY")
|
config = ConfigManager.get_config() # Alias for config_loader.get_config
|
||||||
claude_key = os.environ.get("CLAUDE_API_KEY")
|
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()
|
provider_setting = db.query(models.SystemSetting).filter(models.SystemSetting.key == "ai_provider").first()
|
||||||
active_provider = provider_setting.value if provider_setting else "gemini"
|
active_provider = provider_setting.value if provider_setting else "gemini"
|
||||||
@@ -112,10 +115,14 @@ def update_ai_keys(
|
|||||||
if updates:
|
if updates:
|
||||||
ConfigManager.update_keys(updates)
|
ConfigManager.update_keys(updates)
|
||||||
|
|
||||||
|
# Re-load config to return accurate status
|
||||||
|
config = ConfigManager.get_config()
|
||||||
|
ai_cfg = config.get("ai", {})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"gemini_configured": bool(os.environ.get("GEMINI_API_KEY")),
|
"gemini_configured": bool(ai_cfg.get("gemini_api_key")),
|
||||||
"claude_configured": bool(os.environ.get("CLAUDE_API_KEY"))
|
"claude_configured": bool(ai_cfg.get("claude_api_key"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
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.
BIN
backups/ainventory_2026-04-23_14-45-07.tar.gz
Normal file
BIN
backups/ainventory_2026-04-23_14-45-07.tar.gz
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "1.14.11",
|
"version": "1.14.13",
|
||||||
"last_build": "2026-04-23-1404",
|
"last_build": "2026-04-23-1451",
|
||||||
"codename": "ConfigCore",
|
"codename": "ConfigCore",
|
||||||
"commit": "e5a24df1"
|
"commit": "827dfcdf"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user