fix(telegram): execute approved callbacks
This commit is contained in:
@@ -19,6 +19,7 @@ Endpoints:
|
||||
- 每個 Nonce 只能使用一次
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
@@ -27,6 +28,8 @@ from pydantic import BaseModel
|
||||
from src.core.config import settings
|
||||
from src.core.logging import get_logger
|
||||
from src.services.approval_db import get_approval_service
|
||||
from src.services.approval_execution import get_execution_service
|
||||
from src.services.incident_approval_service import get_incident_approval_service
|
||||
from src.services.security_interceptor import (
|
||||
NonceReplayError,
|
||||
UserNotWhitelistedError,
|
||||
@@ -64,6 +67,80 @@ class TestPushRequest(BaseModel):
|
||||
incident_id: str = ""
|
||||
|
||||
|
||||
async def _run_telegram_approved_execution(approval) -> None:
|
||||
"""Run the approved action that originated from a Telegram callback."""
|
||||
approval_id = str(getattr(approval, "id", ""))
|
||||
incident_id = getattr(approval, "incident_id", None)
|
||||
try:
|
||||
result = await get_execution_service().execute_approved_action(approval)
|
||||
logger.info(
|
||||
"telegram_approval_execution_completed",
|
||||
approval_id=approval_id,
|
||||
incident_id=incident_id,
|
||||
success=bool(result),
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"telegram_approval_execution_failed",
|
||||
approval_id=approval_id,
|
||||
incident_id=incident_id,
|
||||
error=str(exc),
|
||||
)
|
||||
|
||||
|
||||
def _schedule_telegram_approved_execution(approval) -> bool:
|
||||
"""Schedule execution after Telegram approval reaches required signatures."""
|
||||
try:
|
||||
asyncio.create_task(_run_telegram_approved_execution(approval))
|
||||
logger.info(
|
||||
"telegram_approval_execution_scheduled",
|
||||
approval_id=str(getattr(approval, "id", "")),
|
||||
incident_id=getattr(approval, "incident_id", None),
|
||||
)
|
||||
return True
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"telegram_approval_execution_schedule_failed",
|
||||
approval_id=str(getattr(approval, "id", "")),
|
||||
incident_id=getattr(approval, "incident_id", None),
|
||||
error=str(exc),
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
async def _finalize_telegram_approval(approval, execution_triggered: bool) -> bool:
|
||||
"""Complete the execution handoff for Telegram approvals.
|
||||
|
||||
ApprovalDBService only records the signature/status transition. The actual
|
||||
executor scheduling lives in API callers, so Telegram must mirror the REST
|
||||
approval endpoint instead of stopping at a visual approval stamp.
|
||||
"""
|
||||
if not execution_triggered:
|
||||
return False
|
||||
return _schedule_telegram_approved_execution(approval)
|
||||
|
||||
|
||||
async def _sync_telegram_rejection(approval_id: str) -> bool:
|
||||
"""Keep Incident state aligned when an approval is rejected from Telegram."""
|
||||
try:
|
||||
await get_incident_approval_service().on_approval_status_change(
|
||||
approval_id=approval_id,
|
||||
new_status="rejected",
|
||||
)
|
||||
logger.info(
|
||||
"telegram_rejection_incident_synced",
|
||||
approval_id=approval_id,
|
||||
)
|
||||
return True
|
||||
except Exception as exc:
|
||||
logger.error(
|
||||
"telegram_rejection_incident_sync_failed",
|
||||
approval_id=approval_id,
|
||||
error=str(exc),
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Endpoints
|
||||
# =============================================================================
|
||||
@@ -198,12 +275,17 @@ async def telegram_webhook(
|
||||
)
|
||||
|
||||
if approval:
|
||||
execution_scheduled = await _finalize_telegram_approval(
|
||||
approval=approval,
|
||||
execution_triggered=execution_triggered,
|
||||
)
|
||||
logger.info(
|
||||
"telegram_approval_signed",
|
||||
approval_id=approval_id,
|
||||
user_id=user_id,
|
||||
status=approval.status.value,
|
||||
execution_triggered=execution_triggered,
|
||||
execution_scheduled=execution_scheduled,
|
||||
)
|
||||
await _log_user_action("approve", True, getattr(approval, "incident_id", None))
|
||||
|
||||
@@ -213,6 +295,7 @@ async def telegram_webhook(
|
||||
"approval_id": approval_id,
|
||||
"status": approval.status.value,
|
||||
"execution_triggered": execution_triggered,
|
||||
"execution_scheduled": execution_scheduled,
|
||||
}
|
||||
|
||||
elif action == "reject":
|
||||
@@ -224,10 +307,12 @@ async def telegram_webhook(
|
||||
)
|
||||
|
||||
if approval:
|
||||
incident_synced = await _sync_telegram_rejection(approval_id)
|
||||
logger.info(
|
||||
"telegram_approval_rejected",
|
||||
approval_id=approval_id,
|
||||
user_id=user_id,
|
||||
incident_synced=incident_synced,
|
||||
)
|
||||
await _log_user_action("reject", False, getattr(approval, "incident_id", None))
|
||||
|
||||
@@ -236,6 +321,7 @@ async def telegram_webhook(
|
||||
"message": "Rejected",
|
||||
"approval_id": approval_id,
|
||||
"status": approval.status.value,
|
||||
"incident_synced": incident_synced,
|
||||
}
|
||||
|
||||
return {"ok": False, "message": "Unknown action"}
|
||||
|
||||
Reference in New Issue
Block a user