test: establish monitoring configuration contract
This commit is contained in:
+33
-4
@@ -1,10 +1,16 @@
|
||||
"""应用配置,通过环境变量注入,不支持 .env 文件"""
|
||||
"""Application configuration loaded from environment variables or a local .env file."""
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings
|
||||
from typing import Optional
|
||||
|
||||
|
||||
LEGACY_DEFAULT_SECRET = "change-me-to-a-long-random-string"
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# ---------- 运行环境 ----------
|
||||
environment: str = "development"
|
||||
|
||||
# ---------- 数据库 ----------
|
||||
DATABASE_URL: str = "sqlite+aiosqlite:///./pingwatch.db"
|
||||
# PostgreSQL: "postgresql+asyncpg://user:pass@localhost/pingwatch"
|
||||
@@ -15,7 +21,7 @@ class Settings(BaseSettings):
|
||||
WECOM_APP_SECRET: str = ""
|
||||
|
||||
# ---------- Casdoor ----------
|
||||
CASDOOR_ENDPOINT: str = "https://casdoor.dhdx.fun"
|
||||
casdoor_endpoint: str = "https://casdoor.dhdx.fun"
|
||||
CASDOOR_CLIENT_ID: str = ""
|
||||
CASDOOR_CLIENT_SECRET: str = ""
|
||||
CASDOOR_CERTIFICATE: str = "" # 可选,用于验证 id_token 签名
|
||||
@@ -23,6 +29,19 @@ class Settings(BaseSettings):
|
||||
CASDOOR_APPLICATION: str = "PingWatch"
|
||||
CASDOOR_REDIRECT_URI: str = "http://10.10.10.7:5173/login" # 前端回调地址
|
||||
|
||||
# ---------- 连通性监测 ----------
|
||||
probe_packets_per_round: int = Field(default=3, ge=1, le=10)
|
||||
offline_consecutive_rounds: int = Field(default=2, ge=1, le=10)
|
||||
degraded_window_rounds: int = Field(default=5, ge=2, le=60)
|
||||
degraded_loss_percent: float = Field(default=20.0, ge=1, le=100)
|
||||
recovery_consecutive_clean_rounds: int = Field(default=3, ge=1, le=20)
|
||||
|
||||
# ---------- 企业微信投递 ----------
|
||||
wecom_notification_enabled: bool = False
|
||||
wecom_notification_max_attempts: int = Field(default=5, ge=1, le=10)
|
||||
wecom_retry_base_seconds: int = Field(default=30, ge=1, le=3600)
|
||||
WECOM_TO_PARTY: str = ""
|
||||
|
||||
# ---------- Ping 引擎 ----------
|
||||
PING_INTERVAL_SECONDS: int = 30
|
||||
PING_TIMEOUT_SECONDS: float = 5.0
|
||||
@@ -40,7 +59,7 @@ class Settings(BaseSettings):
|
||||
ALERT_RETENTION_DAYS: int = 365
|
||||
|
||||
# ---------- JWT ----------
|
||||
SECRET_KEY: str = "change-me-to-a-long-random-string"
|
||||
secret_key: str = LEGACY_DEFAULT_SECRET
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 480
|
||||
|
||||
# ---------- LogHive ----------
|
||||
@@ -53,5 +72,15 @@ class Settings(BaseSettings):
|
||||
|
||||
model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
|
||||
|
||||
@property
|
||||
def CASDOOR_ENDPOINT(self) -> str:
|
||||
"""Compatibility accessor for existing uppercase configuration consumers."""
|
||||
return self.casdoor_endpoint
|
||||
|
||||
@property
|
||||
def SECRET_KEY(self) -> str:
|
||||
"""Compatibility accessor for existing uppercase configuration consumers."""
|
||||
return self.secret_key
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Runtime validation for security-sensitive application settings."""
|
||||
|
||||
from app.config import LEGACY_DEFAULT_SECRET, Settings
|
||||
|
||||
|
||||
def validate_runtime_settings(settings: Settings) -> None:
|
||||
"""Reject unsafe settings before scheduling monitoring work."""
|
||||
if settings.environment == "production":
|
||||
if settings.secret_key == LEGACY_DEFAULT_SECRET:
|
||||
raise ValueError("SECRET_KEY must be provided by the runtime environment")
|
||||
if not settings.casdoor_endpoint.startswith("https://"):
|
||||
raise ValueError("CASDOOR endpoint must use TLS in production")
|
||||
|
||||
if settings.wecom_notification_enabled:
|
||||
credentials = (
|
||||
settings.WECOM_CORP_ID,
|
||||
settings.WECOM_AGENT_ID,
|
||||
settings.WECOM_APP_SECRET,
|
||||
)
|
||||
if not all(credentials):
|
||||
raise ValueError("WECOM delivery requires corp ID, agent ID, and app secret")
|
||||
Reference in New Issue
Block a user