# =================================================================== # apps/webhook/app.py # 웹훅 수신 서버 애플리케이션 # =================================================================== # Notion 등 외부 서비스의 웹훅을 수신하고 처리합니다. # Mattermost 등으로 알림을 발송합니다. # =================================================================== """ 웹훅 수신 서버 애플리케이션 Notion 등 외부 서비스의 웹훅을 수신하고 Mattermost 등으로 알림을 발송합니다. 사용 예시: from apps.webhook.app import create_app app = create_app() app.run() """ from flask import Flask, Blueprint, jsonify, request from werkzeug.exceptions import HTTPException from core.config import get_config from core.logging_utils import get_logger, setup_logging from services.notification import NotionWebhookHandler, MattermostNotifier logger = get_logger(__name__) # Blueprint 생성 webhook_bp = Blueprint('webhook', __name__, url_prefix='/webhook') @webhook_bp.route('/health', methods=['GET']) def health_check(): """헬스 체크""" return jsonify({ 'status': 'healthy', 'service': 'webhook-receiver' }) @webhook_bp.route('/notion', methods=['POST']) def handle_notion_webhook(): """ Notion 웹훅 수신 엔드포인트 Notion에서 발생한 이벤트를 수신하고 Mattermost로 알림을 발송합니다. Returns: 처리 결과 JSON """ # JSON 데이터 파싱 try: event_data = request.get_json() except Exception as e: logger.error(f"JSON 파싱 실패: {e}") return jsonify({'error': 'Invalid JSON'}), 400 if not event_data: return jsonify({'error': 'Empty request body'}), 400 # 이벤트 처리 try: handler = NotionWebhookHandler() message = handler.handle_event(event_data) if message: # Mattermost로 알림 발송 notifier = MattermostNotifier.from_config() sent = notifier.send_message(message) if sent: logger.info("Notion 이벤트 처리 및 알림 발송 완료") return jsonify({ 'status': 'success', 'message_sent': True }) else: logger.warning("알림 발송 실패") return jsonify({ 'status': 'partial', 'message_sent': False, 'reason': 'Message delivery failed' }) else: logger.info("이벤트 무시됨 (필터 조건 불충족)") return jsonify({ 'status': 'ignored', 'reason': 'Event filtered out' }) except Exception as e: logger.error(f"이벤트 처리 실패: {e}") return jsonify({'error': str(e)}), 500 @webhook_bp.route('/test', methods=['POST']) def test_webhook(): """ 테스트 웹훅 엔드포인트 수신된 데이터를 그대로 반환합니다. 디버깅 용도로 사용합니다. """ data = request.get_json() or {} logger.info(f"테스트 웹훅 수신: {data}") return jsonify({ 'status': 'received', 'data': data, 'headers': dict(request.headers) }) @webhook_bp.route('/notify', methods=['POST']) def send_notification(): """ 알림 발송 엔드포인트 직접 메시지를 발송할 수 있는 엔드포인트입니다. Request Body: message: 발송할 메시지 (필수) platform: 발송 플랫폼 (mattermost, telegram, synology) channel_id: 채널 ID (선택) Returns: 발송 결과 JSON """ try: data = request.get_json() except Exception: return jsonify({'error': 'Invalid JSON'}), 400 if not data or 'message' not in data: return jsonify({'error': 'Message is required'}), 400 message = data['message'] platform = data.get('platform', 'mattermost') channel_id = data.get('channel_id') try: if platform == 'mattermost': notifier = MattermostNotifier.from_config() success = notifier.send_message(message, channel_id=channel_id) else: # 다른 플랫폼은 MessageSender 사용 from core.message_sender import MessageSender sender = MessageSender.from_config() success = sender.send(message, platforms=[platform]) return jsonify({ 'status': 'success' if success else 'failed', 'platform': platform }) except Exception as e: logger.error(f"알림 발송 실패: {e}") return jsonify({'error': str(e)}), 500 @webhook_bp.errorhandler(HTTPException) def handle_exception(e): """HTTP 예외 핸들러""" return jsonify({ 'error': e.description, 'status_code': e.code }), e.code def create_app(config_override: dict = None) -> Flask: """ Flask 애플리케이션 팩토리 Args: config_override: 설정 덮어쓰기 딕셔너리 Returns: Flask 앱 인스턴스 """ app = Flask(__name__) # 설정 로드 config = get_config() app.config['SECRET_KEY'] = config.flask.get('secret_key', 'dev-secret') app.config['JSON_AS_ASCII'] = False if config_override: app.config.update(config_override) # 로깅 설정 setup_logging('apps.webhook', level=config.log_level) # Blueprint 등록 app.register_blueprint(webhook_bp) # 루트 엔드포인트 @app.route('/') def index(): return jsonify({ 'name': 'FGTools Webhook Receiver', 'version': '1.0.0', 'endpoints': [ '/webhook/health', '/webhook/notion', '/webhook/test', '/webhook/notify', ] }) logger.info("Webhook 앱 초기화 완료") return app def run_server(host: str = '0.0.0.0', port: int = 5002, debug: bool = False): """ 개발 서버 실행 Args: host: 바인딩 호스트 port: 포트 번호 debug: 디버그 모드 """ app = create_app() app.run(host=host, port=port, debug=debug) if __name__ == '__main__': config = get_config() run_server( host=config.flask.get('host', '0.0.0.0'), port=5002, debug=config.debug )