a4a224b284
- Migration 003; windowed host+type+rule correlation on ingest
- GET /problems filters; GET /problems/{id} with event timeline; ack/resolve 409 guard
- Tests and SAC_PROBLEM_CORRELATION_WINDOW_MINUTES config
Co-authored-by: Cursor <cursoragent@cursor.com>
63 lines
2.0 KiB
Python
63 lines
2.0 KiB
Python
import os
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SCHEMA_PATH = ROOT / "schemas" / "event-schema-v1.json"
|
|
DEFAULT_CONFIG_FILE = Path("/opt/security-alert-center/config/sac-api.env")
|
|
|
|
|
|
def _resolve_config_files() -> tuple[str, ...]:
|
|
"""Файлы конфигурации для pydantic (корректный разбор кавычек и URL)."""
|
|
candidates: list[Path] = []
|
|
explicit = os.environ.get("SAC_CONFIG_FILE", "").strip()
|
|
if explicit:
|
|
candidates.append(Path(explicit))
|
|
else:
|
|
candidates.append(DEFAULT_CONFIG_FILE)
|
|
candidates.append(Path(".env"))
|
|
return tuple(str(p) for p in candidates if p.is_file())
|
|
|
|
|
|
def _settings_config() -> SettingsConfigDict:
|
|
files = _resolve_config_files()
|
|
return SettingsConfigDict(
|
|
env_file=files if files else None,
|
|
env_file_encoding="utf-8",
|
|
extra="ignore",
|
|
)
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
model_config = _settings_config()
|
|
|
|
database_url: str = "postgresql+psycopg2://sac:sac@localhost:5432/sac"
|
|
sac_public_url: str = "http://localhost:8000"
|
|
jwt_secret: str = "change-me-in-production"
|
|
jwt_algorithm: str = "HS256"
|
|
jwt_expire_minutes: int = 60 * 24
|
|
|
|
sac_bootstrap_api_key: str = ""
|
|
sac_admin_username: str = "admin"
|
|
sac_admin_password: str = ""
|
|
|
|
event_schema_path: str = str(SCHEMA_PATH)
|
|
cors_origins: str = "*"
|
|
telegram_enabled: bool = False
|
|
telegram_bot_token: str = ""
|
|
telegram_chat_id: str = ""
|
|
telegram_min_severity: str = "high"
|
|
|
|
# Порог «живости» агента: agent.heartbeat раз в ~12 ч (ssh-monitor)
|
|
sac_heartbeat_stale_minutes: int = 780
|
|
|
|
# Окно корреляции Problems: host + type + rule в одном open Problem
|
|
sac_problem_correlation_window_minutes: int = 60
|
|
|
|
|
|
@lru_cache
|
|
def get_settings() -> Settings:
|
|
return Settings()
|