zioinfo-mail/messenger/routers/webhook.py
DESKTOP-TKLFCPR\ython 85e4901541 feat(messenger): Slack형 실시간 채팅 메신저 구현
- FastAPI + WebSocket 백엔드 (ws_relay, webhook, messages 라우터)
- GUARDiA-Bot: @bot 명령 응답 + 선제적 맥락 분석 (DB 지연, 디스크, 장애 감지)
- 승인 워크플로우: 재기동/배포 SR → 승인/반려 인터랙티브 버튼
- 다크 테마 Slack형 프론트엔드 (5개 채널, 실시간 메시지)
- 채널: 일반/배포/운영/PM관리/알림

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 19:04:03 +09:00

61 lines
1.8 KiB
Python

import hashlib
import hmac
import json
from datetime import datetime
from uuid import uuid4
from fastapi import APIRouter, HTTPException, Request
router = APIRouter(prefix="/api")
WEBHOOK_SECRET = "guardia-webhook-secret-2026"
def _verify(signature: str, body: bytes) -> bool:
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
def _new_sr() -> str:
return f"SR-{datetime.now().strftime('%Y%m%d')}-{str(uuid4())[:6].upper()}"
@router.post("/messenger/webhook")
async def receive_webhook(request: Request):
body = await request.body()
sig = request.headers.get("X-Messenger-Signature", "")
# 개발 환경에서 서명 검증 생략 (프로덕션에서 활성화)
# if sig and not _verify(sig, body):
# raise HTTPException(status_code=403, detail="Invalid signature")
payload = json.loads(body)
if payload.get("bot") or not payload.get("text", "").strip():
return {"status": "IGNORED"}
sr_id = _new_sr()
return {"status": "ACCEPTED", "sr_id": sr_id}
@router.post("/sr/approve/{sr_id}")
async def approve_sr(sr_id: str):
return {"status": "APPROVED", "sr_id": sr_id, "message": f"SR {sr_id} 승인 완료 — 실행을 시작합니다."}
@router.post("/sr/reject/{sr_id}")
async def reject_sr(sr_id: str):
return {"status": "REJECTED", "sr_id": sr_id, "message": f"SR {sr_id} 반려 처리되었습니다."}
@router.post("/killswitch/confirm")
async def killswitch_confirm():
return {"status": "STOPPED", "message": "모든 진행 중인 작업이 중단되었습니다."}
@router.post("/killswitch/cancel")
async def killswitch_cancel():
return {"status": "CANCELLED", "message": "Kill-Switch 취소되었습니다."}