#!/usr/bin/env python3 """ 팜큐 약국 관리 시스템 - Flask 애플리케이션 Headscale + Headplane 고도화 관리자 페이지 """ from flask import Flask, render_template, jsonify, request, redirect, url_for import os from datetime import datetime from config import config from utils.database_new import ( init_databases, get_farmq_session, get_dashboard_stats, get_all_pharmacies_with_stats, get_all_machines_with_details, get_machine_detail, get_pharmacy_detail, get_active_alerts, sync_machines_from_headscale, sync_users_from_headscale ) def create_app(config_name=None): """Flask 애플리케이션 팩토리""" app = Flask(__name__) # 설정 로드 config_name = config_name or os.environ.get('FLASK_ENV', 'default') app.config.from_object(config[config_name]) # 데이터베이스 초기화 init_databases( headscale_db_uri='sqlite:////srv/headscale-setup/data/db.sqlite', farmq_db_uri='sqlite:///farmq.db' ) # 데이터 동기화 실행 sync_users_from_headscale() sync_machines_from_headscale() # 메인 대시보드 @app.route('/') def dashboard(): """메인 대시보드""" try: # 새로운 통합 통계 함수 사용 stats = get_dashboard_stats() stats['alerts'] = get_active_alerts()[:5] # 최신 5개만 stats['performance'] = {'status': 'good', 'summary': '모든 시스템이 정상 작동 중입니다.'} # 약국별 상태 (상위 10개) pharmacies = get_all_pharmacies_with_stats()[:10] return render_template('dashboard/index.html', stats=stats, pharmacies=pharmacies) except Exception as e: print(f"❌ Dashboard error: {e}") return render_template('error.html', error=str(e)), 500 # 약국 관리 @app.route('/pharmacy') def pharmacy_list(): """약국 목록""" try: pharmacies = get_all_pharmacies_with_stats() return render_template('pharmacy/list.html', pharmacies=pharmacies) except Exception as e: return render_template('error.html', error=str(e)), 500 @app.route('/pharmacy/') def pharmacy_detail(pharmacy_id): """약국 상세 정보""" try: detail_data = get_pharmacy_detail(pharmacy_id) if not detail_data: return render_template('error.html', error='약국을 찾을 수 없습니다.'), 404 return render_template('pharmacy/detail.html', pharmacy=detail_data['pharmacy'], machines=detail_data['machines']) except Exception as e: print(f"❌ Pharmacy detail error: {e}") return render_template('error.html', error=str(e)), 500 # 머신 관리 @app.route('/machines') def machine_list(): """머신 목록""" try: machines = get_all_machines_with_details() return render_template('machines/list.html', machines=machines) except Exception as e: print(f"❌ Machine list error: {e}") return render_template('error.html', error=str(e)), 500 @app.route('/machines/') def machine_detail(machine_id): """머신 상세 정보""" try: print(f"🔍 Getting details for machine ID: {machine_id}") details = get_machine_detail(machine_id) if not details: print(f"❌ No details found for machine ID: {machine_id}") return render_template('error.html', error='머신을 찾을 수 없습니다.'), 404 hostname = details.get('hostname', 'Unknown') print(f"✅ Rendering detail page for machine: {hostname}") return render_template('machines/detail.html', machine=details) except Exception as e: print(f"❌ Error in machine_detail route: {e}") import traceback traceback.print_exc() return render_template('error.html', error=f'머신 상세 정보 로드 중 오류: {str(e)}'), 500 # API 엔드포인트 @app.route('/api/dashboard/stats') def api_dashboard_stats(): """대시보드 통계 API""" try: stats = get_dashboard_stats() stats['performance'] = {'status': 'good', 'summary': '모든 시스템이 정상 작동 중입니다.'} return jsonify(stats) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/alerts') def api_alerts(): """실시간 알림 API""" try: alerts = get_active_alerts() return jsonify(alerts) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/machines//monitoring') def api_machine_monitoring(machine_id): """머신 모니터링 데이터 API""" try: details = get_machine_detail(machine_id) if not details: return jsonify({'error': '머신을 찾을 수 없습니다.'}), 404 # 최근 모니터링 데이터 반환 metrics_history = details.get('metrics_history', []) return jsonify(metrics_history[:20]) # 최근 20개 데이터 except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/sync/machines') def api_sync_machines(): """Headscale에서 머신 정보 동기화 API""" try: result = sync_machines_from_headscale() return jsonify(result) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/sync/users') def api_sync_users(): """Headscale에서 사용자 정보 동기화 API""" try: result = sync_users_from_headscale() return jsonify(result) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/pharmacy//update', methods=['PUT']) def api_update_pharmacy(pharmacy_id): """약국 정보 업데이트 API""" try: from utils.database_new import get_farmq_session from models.farmq_models import PharmacyInfo data = request.get_json() session = get_farmq_session() try: pharmacy = session.query(PharmacyInfo).filter( PharmacyInfo.id == pharmacy_id ).first() if not pharmacy: return jsonify({'error': '약국을 찾을 수 없습니다.'}), 404 # 업데이트 가능한 필드들 if 'pharmacy_name' in data: pharmacy.pharmacy_name = data['pharmacy_name'] if 'business_number' in data: pharmacy.business_number = data['business_number'] if 'manager_name' in data: pharmacy.manager_name = data['manager_name'] if 'phone' in data: pharmacy.phone = data['phone'] if 'address' in data: pharmacy.address = data['address'] pharmacy.updated_at = datetime.now() session.commit() return jsonify({ 'message': '약국 정보가 업데이트되었습니다.', 'pharmacy': pharmacy.to_dict() }) finally: session.close() except Exception as e: return jsonify({'error': str(e)}), 500 # 에러 핸들러 @app.errorhandler(404) def not_found_error(error): return render_template('error.html', error='페이지를 찾을 수 없습니다.', error_code=404), 404 @app.errorhandler(500) def internal_error(error): return render_template('error.html', error='내부 서버 오류가 발생했습니다.', error_code=500), 500 return app # 개발 서버 실행 if __name__ == '__main__': app = create_app() # 개발 환경에서만 실행 if app.config.get('DEBUG'): print("🚀 Starting FARMQ Admin System...") print(f"📊 Dashboard: http://localhost:5001") print(f"🏥 Pharmacy Management: http://localhost:5001/pharmacy") print(f"💻 Machine Management: http://localhost:5001/machines") print("─" * 60) app.run( host='0.0.0.0', port=5001, debug=True, use_reloader=True )