feat: initial commit - unified FGTools from static, weather, mattermost-noti
This commit is contained in:
15
apps/__init__.py
Normal file
15
apps/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# ===================================================================
|
||||
# apps/__init__.py
|
||||
# FGTools 애플리케이션 패키지 초기화
|
||||
# ===================================================================
|
||||
# 웹 애플리케이션 엔드포인트들을 제공합니다:
|
||||
# - dashboard: 정적 데이터 대시보드
|
||||
# - weather_api: 날씨 API 서버
|
||||
# - webhook: 웹훅 수신 서버
|
||||
# ===================================================================
|
||||
|
||||
__all__ = [
|
||||
'dashboard',
|
||||
'weather_api',
|
||||
'webhook',
|
||||
]
|
||||
8
apps/dashboard/__init__.py
Normal file
8
apps/dashboard/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
# ===================================================================
|
||||
# apps/dashboard/__init__.py
|
||||
# 대시보드 앱 패키지 초기화
|
||||
# ===================================================================
|
||||
|
||||
from .app import create_app, dashboard_bp, run_server
|
||||
|
||||
__all__ = ['create_app', 'dashboard_bp', 'run_server']
|
||||
205
apps/dashboard/app.py
Normal file
205
apps/dashboard/app.py
Normal file
@ -0,0 +1,205 @@
|
||||
# ===================================================================
|
||||
# apps/dashboard/app.py
|
||||
# 정적 데이터 대시보드 웹 애플리케이션
|
||||
# ===================================================================
|
||||
# 방문객, 날씨, 대기질, 예측 데이터를 시각화하는 대시보드입니다.
|
||||
# Flask Blueprint 기반으로 구현되어 통합 앱에 포함할 수 있습니다.
|
||||
# ===================================================================
|
||||
"""
|
||||
정적 데이터 대시보드 웹 애플리케이션
|
||||
|
||||
방문객 통계, 날씨 정보, 대기질 데이터 등을 조회하고
|
||||
시각화하는 대시보드를 제공합니다.
|
||||
|
||||
사용 예시:
|
||||
from apps.dashboard.app import create_app
|
||||
|
||||
app = create_app()
|
||||
app.run()
|
||||
"""
|
||||
|
||||
import os
|
||||
from flask import Flask, Blueprint, jsonify, request, render_template
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from core.config import get_config
|
||||
from core.logging_utils import get_logger, setup_logging
|
||||
from core.database import get_engine, DBSession
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# Blueprint 생성
|
||||
dashboard_bp = Blueprint('dashboard', __name__, url_prefix='/api/dashboard')
|
||||
|
||||
|
||||
@dashboard_bp.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""
|
||||
헬스 체크 엔드포인트
|
||||
|
||||
Returns:
|
||||
상태 정보 JSON
|
||||
"""
|
||||
return jsonify({
|
||||
'status': 'healthy',
|
||||
'service': 'dashboard'
|
||||
})
|
||||
|
||||
|
||||
@dashboard_bp.route('/stats', methods=['GET'])
|
||||
def get_stats():
|
||||
"""
|
||||
기본 통계 조회
|
||||
|
||||
Query Parameters:
|
||||
start_date: 시작 날짜 (YYYY-MM-DD)
|
||||
end_date: 종료 날짜 (YYYY-MM-DD)
|
||||
|
||||
Returns:
|
||||
통계 데이터 JSON
|
||||
"""
|
||||
start_date = request.args.get('start_date')
|
||||
end_date = request.args.get('end_date')
|
||||
|
||||
# TODO: 실제 통계 조회 구현
|
||||
return jsonify({
|
||||
'start_date': start_date,
|
||||
'end_date': end_date,
|
||||
'total_visitors': 0,
|
||||
'average_visitors': 0,
|
||||
'data': []
|
||||
})
|
||||
|
||||
|
||||
@dashboard_bp.route('/weather/forecast', methods=['GET'])
|
||||
def get_weather_forecast():
|
||||
"""
|
||||
날씨 예보 조회
|
||||
|
||||
Returns:
|
||||
날씨 예보 데이터 JSON
|
||||
"""
|
||||
from services.weather import get_daily_vilage_forecast
|
||||
|
||||
config = get_config()
|
||||
service_key = config.data_api.get('service_key', '')
|
||||
|
||||
if not service_key:
|
||||
return jsonify({'error': 'API 키가 설정되지 않았습니다.'}), 500
|
||||
|
||||
try:
|
||||
forecast = get_daily_vilage_forecast(service_key)
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'data': forecast
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"날씨 예보 조회 실패: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@dashboard_bp.route('/weather/precipitation', methods=['GET'])
|
||||
def get_precipitation():
|
||||
"""
|
||||
강수량 예보 조회
|
||||
|
||||
Query Parameters:
|
||||
format: 출력 형식 (json, html, text)
|
||||
|
||||
Returns:
|
||||
강수량 데이터
|
||||
"""
|
||||
from services.weather import PrecipitationService
|
||||
|
||||
output_format = request.args.get('format', 'json')
|
||||
|
||||
try:
|
||||
service = PrecipitationService()
|
||||
data = service.get_precipitation_info(output_format=output_format)
|
||||
|
||||
if output_format == 'html':
|
||||
return data, 200, {'Content-Type': 'text/html; charset=utf-8'}
|
||||
elif output_format == 'text':
|
||||
return data, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||
else:
|
||||
return jsonify(data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"강수량 조회 실패: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@dashboard_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.dashboard', level=config.log_level)
|
||||
|
||||
# Blueprint 등록
|
||||
app.register_blueprint(dashboard_bp)
|
||||
|
||||
# 루트 엔드포인트
|
||||
@app.route('/')
|
||||
def index():
|
||||
return jsonify({
|
||||
'name': 'FGTools Dashboard API',
|
||||
'version': '1.0.0',
|
||||
'endpoints': [
|
||||
'/api/dashboard/health',
|
||||
'/api/dashboard/stats',
|
||||
'/api/dashboard/weather/forecast',
|
||||
'/api/dashboard/weather/precipitation',
|
||||
]
|
||||
})
|
||||
|
||||
logger.info("Dashboard 앱 초기화 완료")
|
||||
return app
|
||||
|
||||
|
||||
def run_server(host: str = '0.0.0.0', port: int = 5000, 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=config.flask.get('port', 5000),
|
||||
debug=config.debug
|
||||
)
|
||||
8
apps/weather_api/__init__.py
Normal file
8
apps/weather_api/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
# ===================================================================
|
||||
# apps/weather_api/__init__.py
|
||||
# 날씨 API 앱 패키지 초기화
|
||||
# ===================================================================
|
||||
|
||||
from .app import create_app, weather_bp, run_server
|
||||
|
||||
__all__ = ['create_app', 'weather_bp', 'run_server']
|
||||
243
apps/weather_api/app.py
Normal file
243
apps/weather_api/app.py
Normal file
@ -0,0 +1,243 @@
|
||||
# ===================================================================
|
||||
# apps/weather_api/app.py
|
||||
# 날씨 API 서버 애플리케이션
|
||||
# ===================================================================
|
||||
# 기상청 API 데이터를 제공하는 REST API 서버입니다.
|
||||
# 강수량 예보, 날씨 캡처 등의 기능을 제공합니다.
|
||||
# ===================================================================
|
||||
"""
|
||||
날씨 API 서버 애플리케이션
|
||||
|
||||
기상청 API 데이터를 조회하고 가공하여 제공하는 API 서버입니다.
|
||||
|
||||
사용 예시:
|
||||
from apps.weather_api.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
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# Blueprint 생성
|
||||
weather_bp = Blueprint('weather', __name__, url_prefix='/api/weather')
|
||||
|
||||
|
||||
@weather_bp.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""헬스 체크"""
|
||||
return jsonify({
|
||||
'status': 'healthy',
|
||||
'service': 'weather-api'
|
||||
})
|
||||
|
||||
|
||||
@weather_bp.route('/precipitation', methods=['GET'])
|
||||
def get_precipitation():
|
||||
"""
|
||||
시간별 강수량 예보 조회
|
||||
|
||||
Query Parameters:
|
||||
date: 조회 날짜 (YYYYMMDD, 기본: 오늘)
|
||||
format: 출력 형식 (json, html, text)
|
||||
|
||||
Returns:
|
||||
강수량 예보 데이터
|
||||
"""
|
||||
from services.weather import PrecipitationService
|
||||
|
||||
target_date = request.args.get('date')
|
||||
output_format = request.args.get('format', 'json')
|
||||
|
||||
try:
|
||||
service = PrecipitationService()
|
||||
data = service.get_precipitation_info(
|
||||
target_date=target_date,
|
||||
output_format=output_format
|
||||
)
|
||||
|
||||
if output_format == 'html':
|
||||
return data, 200, {'Content-Type': 'text/html; charset=utf-8'}
|
||||
elif output_format == 'text':
|
||||
return data, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
||||
else:
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'data': data
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"강수량 조회 실패: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@weather_bp.route('/forecast/ultra', methods=['GET'])
|
||||
def get_ultra_forecast():
|
||||
"""
|
||||
초단기예보 조회
|
||||
|
||||
Returns:
|
||||
초단기예보 데이터
|
||||
"""
|
||||
from services.weather import get_daily_ultra_forecast
|
||||
|
||||
config = get_config()
|
||||
service_key = config.data_api.get('service_key') or config.weather_service.get('service_key', '')
|
||||
|
||||
if not service_key:
|
||||
return jsonify({'error': 'API 키가 설정되지 않았습니다.'}), 500
|
||||
|
||||
try:
|
||||
data = get_daily_ultra_forecast(service_key)
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'type': 'ultra',
|
||||
'data': data
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"초단기예보 조회 실패: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@weather_bp.route('/forecast/vilage', methods=['GET'])
|
||||
def get_vilage_forecast():
|
||||
"""
|
||||
단기예보 조회
|
||||
|
||||
Returns:
|
||||
단기예보 데이터
|
||||
"""
|
||||
from services.weather import get_daily_vilage_forecast
|
||||
|
||||
config = get_config()
|
||||
service_key = config.data_api.get('service_key') or config.weather_service.get('service_key', '')
|
||||
|
||||
if not service_key:
|
||||
return jsonify({'error': 'API 키가 설정되지 않았습니다.'}), 500
|
||||
|
||||
try:
|
||||
data = get_daily_vilage_forecast(service_key)
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'type': 'vilage',
|
||||
'data': data
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"단기예보 조회 실패: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@weather_bp.route('/forecast/midterm', methods=['GET'])
|
||||
def get_midterm_forecast():
|
||||
"""
|
||||
중기예보 조회
|
||||
|
||||
Query Parameters:
|
||||
reg_id: 지역 코드 (기본: 11B20305)
|
||||
|
||||
Returns:
|
||||
중기예보 데이터
|
||||
"""
|
||||
from services.weather import get_midterm_forecast as fetch_midterm
|
||||
|
||||
config = get_config()
|
||||
service_key = config.data_api.get('service_key') or config.weather_service.get('service_key', '')
|
||||
reg_id = request.args.get('reg_id', '11B20305')
|
||||
|
||||
if not service_key:
|
||||
return jsonify({'error': 'API 키가 설정되지 않았습니다.'}), 500
|
||||
|
||||
try:
|
||||
precip_probs, raw_data = fetch_midterm(service_key, reg_id)
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'type': 'midterm',
|
||||
'precipitation_probability': precip_probs,
|
||||
'raw_data': raw_data
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"중기예보 조회 실패: {e}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
|
||||
@weather_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.weather_api', level=config.log_level)
|
||||
|
||||
# Blueprint 등록
|
||||
app.register_blueprint(weather_bp)
|
||||
|
||||
# 루트 엔드포인트
|
||||
@app.route('/')
|
||||
def index():
|
||||
return jsonify({
|
||||
'name': 'FGTools Weather API',
|
||||
'version': '1.0.0',
|
||||
'endpoints': [
|
||||
'/api/weather/health',
|
||||
'/api/weather/precipitation',
|
||||
'/api/weather/forecast/ultra',
|
||||
'/api/weather/forecast/vilage',
|
||||
'/api/weather/forecast/midterm',
|
||||
]
|
||||
})
|
||||
|
||||
logger.info("Weather API 앱 초기화 완료")
|
||||
return app
|
||||
|
||||
|
||||
def run_server(host: str = '0.0.0.0', port: int = 5001, 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=5001,
|
||||
debug=config.debug
|
||||
)
|
||||
8
apps/webhook/__init__.py
Normal file
8
apps/webhook/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
# ===================================================================
|
||||
# apps/webhook/__init__.py
|
||||
# 웹훅 수신 앱 패키지 초기화
|
||||
# ===================================================================
|
||||
|
||||
from .app import create_app, webhook_bp, run_server
|
||||
|
||||
__all__ = ['create_app', 'webhook_bp', 'run_server']
|
||||
238
apps/webhook/app.py
Normal file
238
apps/webhook/app.py
Normal file
@ -0,0 +1,238 @@
|
||||
# ===================================================================
|
||||
# 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
|
||||
)
|
||||
Reference in New Issue
Block a user