#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 系統健康檢查 API Author: PANJIT IT Team Created: 2024-01-28 Modified: 2024-01-28 """ from datetime import datetime from flask import Blueprint, jsonify from app.utils.helpers import create_response from app.utils.logger import get_logger from app.models.job import TranslationJob from app.utils.timezone import format_taiwan_time, now_taiwan health_bp = Blueprint('health', __name__, url_prefix='/health') logger = get_logger(__name__) @health_bp.route('', methods=['GET']) def health_check(): """系統健康檢查""" try: status = { 'timestamp': format_taiwan_time(datetime.utcnow(), "%Y-%m-%d %H:%M:%S"), 'status': 'healthy', 'services': {} } # 資料庫檢查 try: from app import db from sqlalchemy import text db.session.execute(text('SELECT 1')) status['services']['database'] = {'status': 'healthy'} except Exception as e: status['services']['database'] = { 'status': 'unhealthy', 'error': str(e) } status['status'] = 'unhealthy' # Redis 檢查 try: import redis from flask import current_app redis_client = redis.from_url(current_app.config['REDIS_URL']) redis_client.ping() status['services']['redis'] = {'status': 'healthy'} except Exception as e: status['services']['redis'] = { 'status': 'unhealthy', 'error': str(e) } # Redis 暫時異常不影響整體狀態(如果沒有使用 Celery) # LDAP 檢查 try: from app.utils.ldap_auth import LDAPAuthService ldap_service = LDAPAuthService() if ldap_service.test_connection(): status['services']['ldap'] = {'status': 'healthy'} else: status['services']['ldap'] = {'status': 'unhealthy', 'error': 'Connection failed'} except Exception as e: status['services']['ldap'] = { 'status': 'unhealthy', 'error': str(e) } # LDAP 異常會影響整體狀態 status['status'] = 'unhealthy' # 檔案系統檢查 try: from pathlib import Path from flask import current_app upload_folder = Path(current_app.config['UPLOAD_FOLDER']) # 檢查上傳目錄是否可寫 test_file = upload_folder / 'health_check.tmp' test_file.write_text('health_check') test_file.unlink() status['services']['filesystem'] = {'status': 'healthy'} except Exception as e: status['services']['filesystem'] = { 'status': 'unhealthy', 'error': str(e) } status['status'] = 'unhealthy' # 檢查 Dify API(如果配置了) try: from flask import current_app if current_app.config.get('DIFY_API_KEY') and current_app.config.get('DIFY_API_BASE_URL'): # 這裡會在實作 Dify 服務時加入連線測試 status['services']['dify_api'] = {'status': 'not_tested'} else: status['services']['dify_api'] = {'status': 'not_configured'} except Exception as e: status['services']['dify_api'] = { 'status': 'error', 'error': str(e) } return jsonify(status), 200 if status['status'] == 'healthy' else 503 except Exception as e: logger.error(f"Health check error: {str(e)}") return jsonify({ 'timestamp': format_taiwan_time(datetime.utcnow(), "%Y-%m-%d %H:%M:%S"), 'status': 'error', 'error': str(e) }), 500 @health_bp.route('/metrics', methods=['GET']) def get_metrics(): """系統指標""" try: # 統計任務狀態 from app import db from sqlalchemy import func job_stats = db.session.query( TranslationJob.status, func.count(TranslationJob.id) ).group_by(TranslationJob.status).all() job_counts = {status: count for status, count in job_stats} # 系統指標 metrics_data = { 'timestamp': format_taiwan_time(datetime.utcnow(), "%Y-%m-%d %H:%M:%S"), 'jobs': { 'pending': job_counts.get('PENDING', 0), 'processing': job_counts.get('PROCESSING', 0), 'completed': job_counts.get('COMPLETED', 0), 'failed': job_counts.get('FAILED', 0), 'retry': job_counts.get('RETRY', 0), 'total': sum(job_counts.values()) } } # 添加最近24小時的統計 from datetime import timedelta yesterday = datetime.utcnow() - timedelta(days=1) recent_jobs = db.session.query( TranslationJob.status, func.count(TranslationJob.id) ).filter( TranslationJob.created_at >= yesterday ).group_by(TranslationJob.status).all() recent_counts = {status: count for status, count in recent_jobs} metrics_data['recent_24h'] = { 'pending': recent_counts.get('PENDING', 0), 'processing': recent_counts.get('PROCESSING', 0), 'completed': recent_counts.get('COMPLETED', 0), 'failed': recent_counts.get('FAILED', 0), 'retry': recent_counts.get('RETRY', 0), 'total': sum(recent_counts.values()) } return jsonify(create_response( success=True, data=metrics_data )) except Exception as e: logger.error(f"Get metrics error: {str(e)}") return jsonify(create_response( success=False, error='SYSTEM_ERROR', message='取得系統指標失敗' )), 500 @health_bp.route('/version', methods=['GET']) def get_version(): """取得版本資訊""" try: version_info = { 'application': 'PANJIT Document Translator', 'version': '1.0.0', 'build_date': '2024-01-28', 'python_version': None, 'flask_version': None } # 取得 Python 版本 import sys version_info['python_version'] = sys.version # 取得 Flask 版本 import flask version_info['flask_version'] = flask.__version__ return jsonify(create_response( success=True, data=version_info )) except Exception as e: logger.error(f"Get version error: {str(e)}") return jsonify(create_response( success=False, error='SYSTEM_ERROR', message='取得版本資訊失敗' )), 500 @health_bp.route('/ping', methods=['GET']) def ping(): """簡單的 ping 檢查""" return jsonify({ 'status': 'ok', 'timestamp': format_taiwan_time(datetime.utcnow(), "%Y-%m-%d %H:%M:%S"), 'message': 'pong' })