Files
security-alert-center/backend/app/api/v1/settings.py
T
PapaTramp 79a78dc7c9 feat: Telegram settings in DB with UI edit and test send
Store notification_channels (migration 004), effective config DB over env,
PUT/test API endpoints, Settings form, and pass db session into ingest notify.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 16:05:32 +10:00

107 lines
3.5 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from app.auth.jwt_auth import get_current_user
from app.database import get_db
from app.services.notification_settings import (
VALID_SEVERITIES,
TelegramConfig,
get_effective_telegram_config,
upsert_telegram_channel,
)
from app.services.telegram_notify import TelegramNotConfiguredError, TelegramSendError, send_telegram_test_message
router = APIRouter(prefix="/settings", tags=["settings"])
def _mask_secret(value: str, *, visible_tail: int = 4) -> str | None:
text = value.strip()
if not text:
return None
if len(text) <= visible_tail:
return "*" * len(text)
return f"{'*' * 8}{text[-visible_tail:]}"
class TelegramSettingsResponse(BaseModel):
enabled: bool
configured: bool
chat_id_hint: str | None = None
bot_token_hint: str | None = None
min_severity: str
source: str = Field(description="env или db")
class NotificationSettingsResponse(BaseModel):
telegram: TelegramSettingsResponse
class TelegramSettingsUpdate(BaseModel):
enabled: bool
min_severity: str
bot_token: str | None = None
chat_id: str | None = None
class TelegramTestResponse(BaseModel):
ok: bool = True
message: str = "Тестовое сообщение отправлено в Telegram"
def _telegram_to_response(cfg: TelegramConfig) -> TelegramSettingsResponse:
return TelegramSettingsResponse(
enabled=cfg.enabled,
configured=cfg.configured,
chat_id_hint=_mask_secret(cfg.chat_id),
bot_token_hint=_mask_secret(cfg.bot_token),
min_severity=cfg.min_severity,
source=cfg.source,
)
@router.get("/notifications", response_model=NotificationSettingsResponse)
def get_notification_settings(
db: Session = Depends(get_db),
_user: str = Depends(get_current_user),
) -> NotificationSettingsResponse:
cfg = get_effective_telegram_config(db)
return NotificationSettingsResponse(telegram=_telegram_to_response(cfg))
@router.put("/notifications/telegram", response_model=TelegramSettingsResponse)
def update_telegram_settings(
body: TelegramSettingsUpdate,
db: Session = Depends(get_db),
_user: str = Depends(get_current_user),
) -> TelegramSettingsResponse:
if body.min_severity not in VALID_SEVERITIES:
raise HTTPException(status_code=422, detail=f"min_severity must be one of: {sorted(VALID_SEVERITIES)}")
try:
cfg = upsert_telegram_channel(
db,
enabled=body.enabled,
min_severity=body.min_severity,
bot_token=body.bot_token,
chat_id=body.chat_id,
)
except ValueError as exc:
raise HTTPException(status_code=422, detail=str(exc)) from exc
return _telegram_to_response(cfg)
@router.post("/notifications/telegram/test", response_model=TelegramTestResponse)
def test_telegram_settings(
db: Session = Depends(get_db),
_user: str = Depends(get_current_user),
) -> TelegramTestResponse:
cfg = get_effective_telegram_config(db)
try:
send_telegram_test_message(config=cfg)
except TelegramNotConfiguredError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
except TelegramSendError as exc:
code = exc.status_code if exc.status_code and 400 <= exc.status_code < 500 else 502
raise HTTPException(status_code=code, detail=str(exc)) from exc
return TelegramTestResponse()