# farmq-admin 통합 분석 및 개선 방안 ## 📅 작성일 **2025년 11월 14일** --- ## 🎯 발견 사항 ### farmq-admin이 이미 존재하고 실행 중! ```bash 프로세스: /srv/headscale-tailscale-replacement/farmq-admin/venv/bin/python app.py 포트: 5001 상태: 실행 중 (Nov 05부터 계속 실행) ``` **중요:** farmq-admin은 Flask 기반 웹 애플리케이션으로, **farmq.db를 관리하는 API를 이미 제공**하고 있습니다! --- ## 📊 farmq-admin 구조 분석 ### 1. 실행 정보 ```bash 위치: /srv/headscale-tailscale-replacement/farmq-admin/ 메인: app.py (Flask 애플리케이션) 포트: 5001 DB: farmq.db (SQLAlchemy ORM 사용) ``` ### 2. 주요 API 엔드포인트 #### 약국 관리 API ```python POST /api/pharmacy # 새 약국 생성 ✨ GET /api/pharmacy/ # 약국 정보 조회 PUT /api/pharmacy/ # 약국 정보 수정 DELETE /api/pharmacy/ # 약국 삭제 ``` ### 3. 약국 생성 API 상세 (`POST /api/pharmacy`) **요청 예시:** ```json POST http://localhost:5001/api/pharmacy Content-Type: application/json { "pharmacy_name": "행복약국", "owner_name": "홍길동", "owner_phone": "010-1234-5678", "owner_email": "happy@pharmq.kr", "phone": "02-1234-5678", "address": "서울시 강남구...", "api_port": 8082 } ``` **응답 예시:** ```json { "success": true, "message": "약국 \"행복약국\" (코드: P004) 생성 완료", "pharmacy": { "id": 4, "pharmacy_code": "P004", "pharmacy_name": "행복약국", "tailscale_ip": null, "api_port": 8082, "status": "active", "owner_name": "홍길동", "owner_phone": "010-1234-5678", "owner_email": "happy@pharmq.kr" } } ``` ### 4. 자동 생성 로직 (이미 구현됨!) ```python # app.py Line 418-433 # pharmacy_code 자동 생성 (P001~P999) last_pharmacy = farmq_session.query(PharmacyInfo)\ .filter(PharmacyInfo.pharmacy_code.like('P%'))\ .order_by(PharmacyInfo.pharmacy_code.desc())\ .first() if last_pharmacy and last_pharmacy.pharmacy_code: try: last_num = int(last_pharmacy.pharmacy_code[1:]) new_num = last_num + 1 except: new_num = 1 else: new_num = 1 pharmacy_code = f"P{new_num:03d}" # P001, P002, P003... ``` **결과:** 마지막 약국 코드를 찾아서 자동으로 +1 증가! --- ## 🔄 개선된 통합 방안 ### 기존 계획 vs 새로운 발견 #### ❌ 기존 계획 (불필요) ``` Python 스크립트로 직접 farmq.db INSERT → SQL 쿼리 작성 → 에러 핸들링 직접 구현 → 검증 로직 직접 구현 ``` #### ✅ 새로운 방안 (API 활용) ``` farmq-admin API 호출 → 이미 모든 로직 구현됨 → 검증, 에러 핸들링 완료 → pharmacy_code 자동 생성 ``` --- ## 🚀 최종 개선 방안 ### 방법 1: farmq-admin API 활용 (권장 ⭐) #### 장점 - ✅ 이미 구현된 API 활용 - ✅ pharmacy_code 자동 생성 - ✅ 검증 로직 포함 - ✅ 에러 핸들링 완료 - ✅ 유지보수 용이 #### 구현 ```bash # headscale-quick-install.sh에서 호출 register_to_farmq_api() { print_status "farmq-admin API를 통해 약국 등록 중..." # JSON 데이터 생성 JSON_DATA=$(cat <만 있음 # 임시 방안: 직접 DB 업데이트 python3 << EOF import sqlite3 conn = sqlite3.connect('/srv/headscale-tailscale-replacement/farmq-admin/farmq.db') cursor = conn.cursor() cursor.execute(""" UPDATE pharmacies SET tailscale_ip = ? WHERE pharmacy_code = ? """, ("$VPN_IP", "$PHARMACY_CODE")) conn.commit() conn.close() print("VPN IP 업데이트 완료: $PHARMACY_CODE → $VPN_IP") EOF } ``` ### 방법 2: 하이브리드 방식 (대안) ```bash register_pharmacy_hybrid() { # 1. farmq-admin API로 기본 정보 등록 PHARMACY_CODE=$(call_farmq_api_create) # 2. VPN IP는 직접 업데이트 (API에 없는 필드) update_vpn_ip_direct "$PHARMACY_CODE" "$TAILSCALE_IP" # 3. gateway.db는 Python 스크립트로 생성 create_gateway_user "$PHARMACY_CODE" } ``` --- ## 📋 수정된 전체 흐름 ### 새로운 스크립트 흐름 ``` 1. OS 감지 2. Tailscale 설치 3. Tailscale 서비스 시작 4. Headscale 등록 (preauth key 사용) 5. VPN IP 할당 받음 (예: 100.64.0.15) 6. 연결 확인 7. ✨ 약국 정보 입력 받기 (대화형) 8. ✨ farmq-admin API 호출 → farmq.db 등록 - pharmacy_code 자동 생성 (API가 처리) - 기본 정보 저장 9. ✨ VPN IP 업데이트 (직접 DB 또는 API 개선) 10. ✨ gateway.db에 사용자 생성 (Python 스크립트) 11. ✨ 로그인 정보 출력 12. 종료 ✅ ``` --- ## 🛠️ 필요한 개선 사항 ### farmq-admin API 개선 (선택사항) #### 1. VPN IP 업데이트 API 추가 ```python # app.py에 추가 @app.route('/api/pharmacy//vpn-ip', methods=['PUT']) def api_update_pharmacy_vpn_ip(pharmacy_code): """약국 VPN IP 업데이트""" try: data = request.get_json() vpn_ip = data.get('vpn_ip', '').strip() if not vpn_ip: return jsonify({ 'success': False, 'error': 'VPN IP는 필수입니다.' }), 400 farmq_session = get_farmq_session() try: pharmacy = farmq_session.query(PharmacyInfo).filter( PharmacyInfo.pharmacy_code == pharmacy_code ).first() if not pharmacy: return jsonify({ 'success': False, 'error': '약국을 찾을 수 없습니다.' }), 404 pharmacy.tailscale_ip = vpn_ip farmq_session.commit() return jsonify({ 'success': True, 'message': f'VPN IP 업데이트 완료: {pharmacy_code} → {vpn_ip}' }) finally: farmq_session.close() except Exception as e: return jsonify({ 'success': False, 'error': f'서버 오류: {str(e)}' }), 500 ``` **사용:** ```bash curl -X PUT http://localhost:5001/api/pharmacy/P004/vpn-ip \ -H "Content-Type: application/json" \ -d '{"vpn_ip": "100.64.0.15"}' ``` #### 2. pharmacy_code로 조회 API 추가 ```python @app.route('/api/pharmacy/code/', methods=['GET']) def api_get_pharmacy_by_code(pharmacy_code): """약국 코드로 약국 정보 조회""" try: farmq_session = get_farmq_session() try: pharmacy = farmq_session.query(PharmacyInfo).filter( PharmacyInfo.pharmacy_code == pharmacy_code ).first() if not pharmacy: return jsonify({ 'success': False, 'error': '약국을 찾을 수 없습니다.' }), 404 return jsonify({ 'success': True, 'pharmacy': pharmacy.to_dict() }) finally: farmq_session.close() except Exception as e: return jsonify({ 'success': False, 'error': f'서버 오류: {str(e)}' }), 500 ``` --- ## 📊 비교표 | 항목 | 직접 DB 접근 | farmq-admin API | |------|-------------|-----------------| | 구현 난이도 | 중 | 쉬움 ⭐ | | pharmacy_code 생성 | 직접 구현 필요 | 자동 처리 ✅ | | 검증 로직 | 직접 구현 필요 | 이미 구현됨 ✅ | | 에러 핸들링 | 직접 구현 필요 | 이미 구현됨 ✅ | | 유지보수 | 어려움 | 쉬움 ✅ | | VPN IP 업데이트 | 직접 가능 | API 개선 필요 | | 의존성 | SQLite3만 | curl 필요 | --- ## 🎯 최종 권장 사항 ### Phase 1: 최소 변경 (즉시 적용 가능) ```bash # headscale-quick-install.sh 수정 register_pharmacy() { print_status "약국 등록 중..." # 1. farmq-admin API로 약국 생성 RESPONSE=$(curl -s -X POST \ http://localhost:5001/api/pharmacy \ -H "Content-Type: application/json" \ -d "{ \"pharmacy_name\": \"$PHARMACY_NAME\", \"owner_name\": \"$OWNER_NAME\", \"owner_phone\": \"$OWNER_PHONE\", \"owner_email\": \"$OWNER_EMAIL\", \"api_port\": 8082 }") # 2. pharmacy_code 추출 PHARMACY_CODE=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('pharmacy',{}).get('pharmacy_code',''))") # 3. VPN IP 업데이트 (직접 DB) python3 << EOF import sqlite3 conn = sqlite3.connect('/srv/headscale-tailscale-replacement/farmq-admin/farmq.db') cursor = conn.cursor() cursor.execute("UPDATE pharmacies SET tailscale_ip = ? WHERE pharmacy_code = ?", ("$TAILSCALE_IP", "$PHARMACY_CODE")) conn.commit() conn.close() EOF # 4. gateway.db 사용자 생성 (별도 스크립트) python3 /srv/pharmq-gateway/scripts/create_gateway_user.py \ --pharmacy-code "$PHARMACY_CODE" \ --owner "$OWNER_NAME" \ --phone "$OWNER_PHONE" \ --email "$OWNER_EMAIL" } ``` ### Phase 2: farmq-admin API 개선 (선택) app.py에 다음 추가: 1. `PUT /api/pharmacy//vpn-ip` - VPN IP 업데이트 2. `GET /api/pharmacy/code/` - 코드로 조회 ### Phase 3: 완전 통합 gateway.db 생성도 API로 통합 (gateway API 서버에서) --- ## 📝 구현 예시 (최소 변경) ### 1. gateway 사용자 생성 스크립트 **파일:** `/srv/pharmq-gateway/scripts/create_gateway_user.py` ```python #!/usr/bin/env python3 import sqlite3 import sys import argparse from datetime import datetime import secrets import string from passlib.hash import pbkdf2_sha256 def generate_password(length=8): alphabet = string.ascii_letters + string.digits return ''.join(secrets.choice(alphabet) for _ in range(length)) def create_gateway_user(args): username = f"{args.pharmacy_code.lower()}_admin" password = generate_password(8) password_hash = pbkdf2_sha256.hash(password) conn = sqlite3.connect(args.gateway_db) cursor = conn.cursor() try: # users 테이블 cursor.execute(""" INSERT INTO users ( username, email, password_hash, name, phone, primary_pharmacy_code, role, status, failed_login_attempts, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( username, args.email, password_hash, args.owner, args.phone, args.pharmacy_code, 'admin', 'active', 0, datetime.now().isoformat(), datetime.now().isoformat() )) user_id = cursor.lastrowid # pharmacy_members 테이블 cursor.execute(""" INSERT INTO pharmacy_members ( pharmacy_code, user_id, position, access_level, can_view_sales, can_view_inventory, can_manage_staff, is_active, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( args.pharmacy_code, user_id, '대표약사', 'owner', True, True, True, True, datetime.now().isoformat(), datetime.now().isoformat() )) conn.commit() # 결과 출력 (JSON) import json print(json.dumps({ 'success': True, 'username': username, 'password': password, 'pharmacy_code': args.pharmacy_code })) except Exception as e: import json print(json.dumps({ 'success': False, 'error': str(e) })) sys.exit(1) finally: conn.close() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--pharmacy-code', required=True) parser.add_argument('--owner', required=True) parser.add_argument('--phone', required=True) parser.add_argument('--email', required=True) parser.add_argument('--gateway-db', default='/srv/pharmq-gateway/gateway.db') args = parser.parse_args() create_gateway_user(args) ``` --- ## ✅ 결론 ### 핵심 발견 **farmq-admin이 이미 약국 생성 API를 제공하고 있음!** - pharmacy_code 자동 생성 ✅ - 검증 로직 완료 ✅ - 에러 핸들링 완료 ✅ ### 개선 방안 1. **farmq.db**: farmq-admin API 활용 (POST /api/pharmacy) 2. **VPN IP**: 직접 DB 업데이트 (또는 API 추가) 3. **gateway.db**: Python 스크립트로 생성 ### 장점 - ✅ 기존 시스템 재사용 - ✅ 중복 코드 방지 - ✅ 유지보수 용이 - ✅ 빠른 구현 가능 --- **작성일:** 2025년 11월 14일 **작성자:** Claude Code **버전:** farmq-admin Integration Analysis v1.0