test: establish monitoring configuration contract

This commit is contained in:
2026-08-04 11:01:09 +08:00
parent 63bf68e73f
commit 44bf6e003e
8 changed files with 127 additions and 14 deletions
+33 -4
View File
@@ -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")