pve9-repo-fix/HEADSCALE_AUTO_REGISTER_PLAN.md
Claude 60be9daff4 docs: headscale 자동 등록 개선 계획 문서 추가
추가된 문서:
- SCRIPT_IMPROVEMENT_PLAN.md: 스크립트 개선 계획
- FARMQ_ADMIN_INTEGRATION_ANALYSIS.md: farmq-admin API 분석
- HEADSCALE_AUTO_REGISTER_PLAN.md: 초기 계획

주요 내용:
- Headscale VPN 등록 시 자동 DB 생성
- API 엔드포인트: demo.pharmq.kr, gateway.pharmq.kr

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 09:40:54 +00:00

30 KiB

Headscale 자동 등록 및 DB 생성 개선 기획서

📅 작성일

2025년 11월 14일


🎯 목표

Headscale 설치 스크립트(headscale-quick-install.sh)를 개선하여:

  1. Headscale VPN 등록 (현재 완료)
  2. farmq.db 자동 생성 (신규)
  3. gateway.db 자동 생성 (신규)
  4. 즉시 프론트엔드 로그인 가능 (신규)

최종 결과: 스크립트 실행 → 즉시 React 프론트엔드에서 로그인하여 사용 가능


📊 현재 상태 분석

현재 스크립트 흐름

1. OS 감지
2. Tailscale 설치
3. Tailscale 서비스 시작
4. Headscale 등록 (preauth key 사용)
5. VPN IP 할당 받음 (예: 100.64.0.15)
6. 연결 확인
7. 종료 ❌ (DB 생성 없음)

문제점

  • farmq.db에 약국 정보 없음
  • gateway.db에 사용자 계정 없음
  • 프론트엔드에서 로그인 불가능
  • 수동으로 DB 수정 필요

🔄 개선된 흐름

새로운 스크립트 흐름

1. OS 감지
2. Tailscale 설치
3. Tailscale 서비스 시작
4. Headscale 등록 (preauth key 사용)
5. VPN IP 할당 받음 (예: 100.64.0.15)
6. 연결 확인
7. ✨ 약국 정보 입력 받기 (대화형)
8. ✨ farmq.db에 약국 등록
9. ✨ gateway.db에 사용자 생성
10. ✨ 로그인 정보 출력
11. 종료 ✅ (완전 자동화)

🗄️ 데이터베이스 맵핑 구조

3계층 DB 구조

┌─────────────────────────────────────────────────────────┐
│                    gateway.db                           │
│  ┌───────────────────────────────────────────────────┐ │
│  │ users                                             │ │
│  │  - username: "약국코드_admin" (예: P0004_admin)   │ │
│  │  - password_hash: (자동 생성된 비밀번호)          │ │
│  │  - primary_pharmacy_code: "P0004" ◄───────┐       │ │
│  │  - role: "admin"                          │       │ │
│  │  - email: "p0004@pharmq.kr"              │       │ │
│  └───────────────────────────────────────────┼───────┘ │
│                                              │         │
│  ┌───────────────────────────────────────────┼───────┐ │
│  │ pharmacy_members                          │       │ │
│  │  - user_id: (위에서 생성된 ID)            │       │ │
│  │  - pharmacy_code: "P0004" ◄───────────────┘       │ │
│  │  - position: "대표약사"                           │ │
│  │  - access_level: "owner"                          │ │
│  └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                         ↓ pharmacy_code
┌─────────────────────────────────────────────────────────┐
│                    farmq.db                             │
│  ┌───────────────────────────────────────────────────┐ │
│  │ pharmacies                                        │ │
│  │  - pharmacy_code: "P0004" ◄─────────────────┐    │ │
│  │  - pharmacy_name: "사용자 입력"              │    │ │
│  │  - tailscale_ip: "100.64.0.15" (Headscale)  │    │ │
│  │  - api_port: 8082                           │    │ │
│  │  - status: "active"                         │    │ │
│  │  - owner_name: "사용자 입력"                 │    │ │
│  │  - owner_phone: "사용자 입력"                │    │ │
│  │  - headscale_user_name: "default"           │    │ │
│  └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                         ↓ vpn_ip
┌─────────────────────────────────────────────────────────┐
│              db.sqlite (Headscale - 읽기전용)           │
│  ┌───────────────────────────────────────────────────┐ │
│  │ nodes                                             │ │
│  │  - ipv4: "100.64.0.15"                           │ │
│  │  - last_seen: "실시간"                           │ │
│  │  - user_id: 1 (default)                         │ │
│  └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

📝 사용자 입력 항목

필수 입력 항목

1. 약국 이름 (pharmacy_name)
   예: "행복약국", "새서울약국"

2. 대표약사 이름 (owner_name)
   예: "홍길동"

3. 대표약사 전화번호 (owner_phone)
   예: "010-1234-5678"

4. 이메일 (owner_email) - 선택
   예: "happy@pharmq.kr"

자동 생성 항목

1. pharmacy_code
   로직: farmq.db에서 가장 큰 번호 + 1
   예: 마지막이 P0002 → P0003 생성

2. VPN IP (tailscale_ip)
   로직: tailscale ip -4 명령으로 자동 획득
   예: 100.64.0.15

3. username
   로직: {pharmacy_code}_admin
   예: P0003 → "p0003_admin"

4. password
   로직: 랜덤 8자리 생성 (영문+숫자)
   예: "aB3xK9mP"

5. email (입력 없을 시)
   로직: {pharmacy_code}@pharmq.kr
   예: "p0003@pharmq.kr"

🔧 기술 구현 방안

1. DB 접근 방법

Option A: SQLite CLI (간단)

sqlite3 /srv/headscale-tailscale-replacement/farmq-admin/farmq.db \
  "INSERT INTO pharmacies (pharmacy_code, pharmacy_name, ...) VALUES (...);"

장점:

  • 추가 의존성 없음
  • 스크립트에 바로 통합 가능

단점:

  • 복잡한 쿼리 작성 어려움
  • 에러 핸들링 제한적

Option B: Python 스크립트 (권장 )

python3 /srv/pharmq-gateway/scripts/register_pharmacy.py \
  --name "행복약국" \
  --owner "홍길동" \
  --phone "010-1234-5678" \
  --vpn-ip "100.64.0.15"

장점:

  • 복잡한 로직 구현 가능
  • 에러 핸들링 우수
  • 검증 로직 추가 가능
  • 비밀번호 해싱 자동

단점:

  • Python 의존성 필요 (대부분 시스템에 기본 설치됨)

2. 스크립트 구조

# headscale-quick-install.sh 개선안

main() {
    # 기존 코드 (1-6단계)
    detect_os
    check_requirements
    install_tailscale
    start_tailscale
    register_headscale
    verify_connection

    # ✨ 신규 추가 (7-10단계)
    collect_pharmacy_info      # 약국 정보 입력 받기
    register_to_farmq_db       # farmq.db 등록
    create_gateway_user        # gateway.db 사용자 생성
    show_login_info            # 로그인 정보 출력

    # 기존 마무리
    cleanup
    show_final_info
}

📋 상세 구현 계획

Step 7: 약국 정보 수집 (대화형)

collect_pharmacy_info() {
    print_header "약국 정보 입력"

    # VPN IP 자동 획득
    TAILSCALE_IP=$(tailscale ip -4 2>/dev/null)
    print_info "할당된 VPN IP: $TAILSCALE_IP"

    # 다음 약국 코드 자동 생성
    NEXT_CODE=$(get_next_pharmacy_code)
    print_info "새 약국 코드: $NEXT_CODE"

    # 사용자 입력
    read -p "약국 이름을 입력하세요: " PHARMACY_NAME
    read -p "대표약사 이름을 입력하세요: " OWNER_NAME
    read -p "대표약사 전화번호 (010-XXXX-XXXX): " OWNER_PHONE
    read -p "이메일 (선택, Enter로 건너뛰기): " OWNER_EMAIL

    # 기본값 설정
    if [ -z "$OWNER_EMAIL" ]; then
        OWNER_EMAIL="${NEXT_CODE,,}@pharmq.kr"  # 소문자로 변환
    fi

    # 확인
    print_info "입력 정보 확인:"
    echo "  약국 코드: $NEXT_CODE"
    echo "  약국 이름: $PHARMACY_NAME"
    echo "  대표약사: $OWNER_NAME"
    echo "  전화번호: $OWNER_PHONE"
    echo "  이메일: $OWNER_EMAIL"
    echo "  VPN IP: $TAILSCALE_IP"

    read -p "위 정보로 등록하시겠습니까? (Y/n): " CONFIRM
    if [[ ! $CONFIRM =~ ^[Yy]$ ]] && [ -n "$CONFIRM" ]; then
        print_error "등록이 취소되었습니다."
        exit 1
    fi
}

Step 8: farmq.db 등록

8-1. Python 헬퍼 스크립트 생성

파일: /srv/pharmq-gateway/scripts/register_pharmacy.py

#!/usr/bin/env python3
"""
약국 자동 등록 스크립트
Headscale 설치 후 farmq.db와 gateway.db에 약국 정보 자동 생성
"""
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 get_next_pharmacy_code(farmq_db_path):
    """다음 약국 코드 생성 (P0001, P0002, ...)"""
    conn = sqlite3.connect(farmq_db_path)
    cursor = conn.cursor()

    cursor.execute("""
        SELECT pharmacy_code FROM pharmacies
        WHERE pharmacy_code LIKE 'P%'
        ORDER BY pharmacy_code DESC
        LIMIT 1
    """)

    result = cursor.fetchone()
    conn.close()

    if result:
        # P0002 → 2 → 3 → P0003
        last_num = int(result[0][1:])
        next_num = last_num + 1
    else:
        next_num = 1

    return f"P{next_num:04d}"

def register_pharmacy(args):
    """farmq.db에 약국 등록"""
    print("=" * 60)
    print("📋 farmq.db 약국 등록")
    print("=" * 60)

    # pharmacy_code 생성
    if not args.code:
        args.code = get_next_pharmacy_code(args.farmq_db)

    print(f"\n약국 코드: {args.code}")
    print(f"약국 이름: {args.name}")
    print(f"VPN IP: {args.vpn_ip}")

    conn = sqlite3.connect(args.farmq_db)
    cursor = conn.cursor()

    try:
        cursor.execute("""
            INSERT INTO pharmacies (
                pharmacy_code,
                pharmacy_name,
                tailscale_ip,
                api_port,
                status,
                owner_name,
                owner_phone,
                owner_email,
                headscale_user_name,
                created_at,
                updated_at
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (
            args.code,
            args.name,
            args.vpn_ip,
            8082,  # 기본 API 포트
            'active',
            args.owner,
            args.phone,
            args.email or f"{args.code.lower()}@pharmq.kr",
            'default',  # Headscale user
            datetime.now().isoformat(),
            datetime.now().isoformat()
        ))

        conn.commit()
        print(f"\n✅ farmq.db 등록 완료 (pharmacy_code: {args.code})")

    except sqlite3.IntegrityError as e:
        print(f"\n❌ 오류: {e}")
        print(f"약국 코드 '{args.code}'가 이미 존재합니다.")
        sys.exit(1)
    finally:
        conn.close()

    return args.code

def create_gateway_user(args, pharmacy_code):
    """gateway.db에 사용자 생성"""
    print("\n" + "=" * 60)
    print("🔐 gateway.db 사용자 생성")
    print("=" * 60)

    # username 생성
    username = f"{pharmacy_code.lower()}_admin"

    # 랜덤 비밀번호 생성
    password = generate_password(8)
    password_hash = pbkdf2_sha256.hash(password)

    print(f"\nUsername: {username}")
    print(f"Password: {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 or f"{pharmacy_code.lower()}@pharmq.kr",
            password_hash,
            args.owner,
            args.phone,
            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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (
            pharmacy_code,
            user_id,
            '대표약사',
            'owner',
            True,
            True,
            True,
            True,
            datetime.now().isoformat(),
            datetime.now().isoformat()
        ))

        conn.commit()
        print(f"\n✅ gateway.db 사용자 생성 완료 (user_id: {user_id})")

    except sqlite3.IntegrityError as e:
        print(f"\n❌ 오류: {e}")
        sys.exit(1)
    finally:
        conn.close()

    return username, password

def main():
    parser = argparse.ArgumentParser(description='약국 자동 등록')
    parser.add_argument('--name', required=True, help='약국 이름')
    parser.add_argument('--owner', required=True, help='대표약사 이름')
    parser.add_argument('--phone', required=True, help='전화번호')
    parser.add_argument('--vpn-ip', required=True, help='VPN IP 주소')
    parser.add_argument('--email', help='이메일 (선택)')
    parser.add_argument('--code', help='약국 코드 (자동 생성)')
    parser.add_argument('--farmq-db',
                        default='/srv/headscale-tailscale-replacement/farmq-admin/farmq.db',
                        help='farmq.db 경로')
    parser.add_argument('--gateway-db',
                        default='/srv/pharmq-gateway/gateway.db',
                        help='gateway.db 경로')

    args = parser.parse_args()

    # 1. farmq.db 등록
    pharmacy_code = register_pharmacy(args)

    # 2. gateway.db 사용자 생성
    username, password = create_gateway_user(args, pharmacy_code)

    # 3. 최종 정보 출력
    print("\n" + "=" * 60)
    print("✅ 등록 완료!")
    print("=" * 60)
    print(f"\n📋 약국 정보:")
    print(f"   약국 코드: {pharmacy_code}")
    print(f"   약국 이름: {args.name}")
    print(f"   VPN IP: {args.vpn_ip}")
    print(f"   API 포트: 8082")

    print(f"\n🔑 로그인 정보:")
    print(f"   Username: {username}")
    print(f"   Password: {password}")
    print(f"   이메일: {args.email or f'{pharmacy_code.lower()}@pharmq.kr'}")

    print(f"\n🌐 프론트엔드 접속:")
    print(f"   https://pharmq.kr")
    print(f"   또는")
    print(f"   https://dev.pharmq.kr")

    print("\n⚠️  비밀번호를 안전한 곳에 보관하세요!")
    print("=" * 60)

if __name__ == '__main__':
    main()

8-2. Bash 스크립트에서 호출

register_to_farmq_and_gateway() {
    print_status "약국 정보 등록 중..."

    # Python 스크립트 실행
    REGISTER_OUTPUT=$(python3 /srv/pharmq-gateway/scripts/register_pharmacy.py \
        --name "$PHARMACY_NAME" \
        --owner "$OWNER_NAME" \
        --phone "$OWNER_PHONE" \
        --email "$OWNER_EMAIL" \
        --vpn-ip "$TAILSCALE_IP" 2>&1)

    if [ $? -eq 0 ]; then
        print_success "등록 완료!"
        echo "$REGISTER_OUTPUT"

        # 로그인 정보 추출
        USERNAME=$(echo "$REGISTER_OUTPUT" | grep "Username:" | awk '{print $2}')
        PASSWORD=$(echo "$REGISTER_OUTPUT" | grep "Password:" | awk '{print $2}')
        PHARMACY_CODE=$(echo "$REGISTER_OUTPUT" | grep "약국 코드:" | awk '{print $3}')

        # 환경 변수 저장
        export PHARMACY_CODE
        export USERNAME
        export PASSWORD
    else
        print_error "등록 실패!"
        echo "$REGISTER_OUTPUT"
        exit 1
    fi
}

🎨 최종 사용자 경험

실행 예시

# 1. 스크립트 실행
curl -fsSL https://git.0bin.in/thug0bin/pve9-repo-fix/raw/branch/main/headscale-quick-install.sh | bash

# 2. Headscale 등록 자동 진행...
# 3. VPN IP 할당: 100.64.0.15

# 4. 약국 정보 입력
============================================
          약국 정보 입력
============================================

할당된 VPN IP: 100.64.0.15
새 약국 코드: P0004

약국 이름을 입력하세요: 행복약국
대표약사 이름을 입력하세요: 홍길동
대표약사 전화번호 (010-XXXX-XXXX): 010-1234-5678
이메일 (선택, Enter로 건너뛰기):

입력 정보 확인:
  약국 코드: P0004
  약국 이름: 행복약국
  대표약사: 홍길동
  전화번호: 010-1234-5678
  이메일: p0004@pharmq.kr
  VPN IP: 100.64.0.15

위 정보로 등록하시겠습니까? (Y/n): Y

# 5. 자동 등록 진행...

============================================
          설치 완료!
============================================

✅ Headscale VPN 연결 완료
✅ farmq.db 약국 등록 완료
✅ gateway.db 사용자 생성 완료

📋 약국 정보:
   약국 코드: P0004
   약국 이름: 행복약국
   VPN IP: 100.64.0.15
   API 포트: 8082

🔑 로그인 정보:
   Username: p0004_admin
   Password: aB3xK9mP
   이메일: p0004@pharmq.kr

🌐 프론트엔드 접속:
   https://pharmq.kr
   또는
   https://dev.pharmq.kr

⚠️  비밀번호를 안전한 곳에 보관하세요!

============================================

프론트엔드 로그인

1. https://pharmq.kr 접속
2. Username: p0004_admin
3. Password: aB3xK9mP
4. 로그인 → 즉시 사용 가능! ✅

⚙️ 구현 우선순위

Phase 1: Python 스크립트 생성 (최우선)

/srv/pharmq-gateway/scripts/register_pharmacy.py
  • farmq.db INSERT
  • gateway.db users, pharmacy_members INSERT
  • 비밀번호 해싱
  • 에러 핸들링

Phase 2: Bash 스크립트 통합

headscale-quick-install.sh 수정
  • collect_pharmacy_info() 함수 추가
  • register_to_farmq_and_gateway() 함수 추가
  • show_login_info() 함수 추가

Phase 3: 테스트

1. 새 VM에서 스크립트 실행
2. DB 확인
3. 프론트엔드 로그인 테스트
4. API 통신 테스트

Phase 4: 문서화

- README 업데이트
- 스크립트 주석 추가
- 트러블슈팅 가이드

🛡️ 보안 고려사항

1. 비밀번호 생성

  • 8자리 이상
  • 영문 대소문자 + 숫자 조합
  • secrets 모듈 사용 (암호학적으로 안전)

2. 비밀번호 해싱

  • pbkdf2_sha256 사용
  • Salt 자동 생성
  • Rainbow table 공격 방지

3. DB 접근 권한

  • root 권한으로 스크립트 실행 필요
  • DB 파일 권한 확인
  • SQLite injection 방지 (parameterized query)

4. 로그인 정보 노출

  • ⚠️ 스크립트 실행 화면에 비밀번호 표시됨
  • 일회용 비밀번호 생성
  • 초기 로그인 후 비밀번호 변경 권장

📊 데이터 흐름 다이어그램

┌─────────────────────────────────────────────────────────────┐
│                    사용자 (터미널)                          │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  │ 1. 스크립트 실행
                  ↓
┌─────────────────────────────────────────────────────────────┐
│          headscale-quick-install.sh                         │
├─────────────────────────────────────────────────────────────┤
│  1. OS 감지                                                 │
│  2. Tailscale 설치                                          │
│  3. Headscale 등록                                          │
│  4. VPN IP 획득: 100.64.0.15                               │
│  5. 약국 정보 입력 받기 ────────────┐                       │
│                                     │                       │
│  6. Python 스크립트 호출 ───────────┼──────────┐            │
└─────────────────────────────────────┼──────────┼────────────┘
                                      │          │
                  ┌───────────────────┘          │
                  │                              │
                  ↓                              ↓
┌─────────────────────────────────┐  ┌─────────────────────────┐
│   register_pharmacy.py          │  │  register_pharmacy.py   │
├─────────────────────────────────┤  ├─────────────────────────┤
│ 1. 다음 코드 생성: P0004       │  │ 1. username 생성        │
│ 2. farmq.db INSERT             │  │    p0004_admin         │
│    - pharmacy_code: P0004      │  │ 2. password 생성        │
│    - pharmacy_name: 행복약국    │  │    aB3xK9mP            │
│    - tailscale_ip: 100.64.0.15 │  │ 3. password 해싱        │
│    - api_port: 8082            │  │ 4. gateway.db INSERT    │
│    - status: active            │  │    - users 테이블       │
│ 3. 성공 반환                   │  │    - pharmacy_members   │
└─────────────────┬───────────────┘  └──────────┬──────────────┘
                  │                             │
                  │                             │
                  └──────────┬──────────────────┘
                             │
                             ↓
┌─────────────────────────────────────────────────────────────┐
│                    최종 정보 출력                            │
├─────────────────────────────────────────────────────────────┤
│  약국 코드: P0004                                            │
│  약국 이름: 행복약국                                          │
│  VPN IP: 100.64.0.15                                         │
│  Username: p0004_admin                                       │
│  Password: aB3xK9mP                                          │
│  프론트엔드: https://pharmq.kr                                │
└─────────────────────────────────────────────────────────────┘
                             │
                             │ 사용자가 로그인 정보 복사
                             ↓
┌─────────────────────────────────────────────────────────────┐
│              React 프론트엔드 (https://pharmq.kr)            │
├─────────────────────────────────────────────────────────────┤
│  1. 로그인 페이지                                            │
│  2. Username: p0004_admin 입력                              │
│  3. Password: aB3xK9mP 입력                                 │
│  4. 로그인 버튼 클릭                                         │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  │ POST /api/auth/login
                  ↓
┌─────────────────────────────────────────────────────────────┐
│           Gateway API (gateway.pharmq.kr:8000)              │
├─────────────────────────────────────────────────────────────┤
│  1. gateway.db users 조회                                   │
│  2. 비밀번호 검증 (pbkdf2_sha256)                            │
│  3. JWT 토큰 생성                                            │
│     payload: {                                              │
│       username: "p0004_admin",                              │
│       pharmacy_code: "P0004",                               │
│       role: "admin"                                         │
│     }                                                        │
│  4. 토큰 반환                                                │
└─────────────────┬───────────────────────────────────────────┘
                  │
                  │ JWT Token
                  ↓
┌─────────────────────────────────────────────────────────────┐
│              React 프론트엔드                                │
├─────────────────────────────────────────────────────────────┤
│  ✅ 로그인 성공!                                             │
│  ✅ 대시보드 페이지 진입                                      │
│  ✅ API 요청 가능 (JWT 토큰 사용)                            │
└─────────────────────────────────────────────────────────────┘

🎯 성공 기준

기능적 요구사항

  • 스크립트 실행 후 5분 이내 완료
  • farmq.db에 약국 정보 자동 생성
  • gateway.db에 사용자 계정 자동 생성
  • 프론트엔드에서 즉시 로그인 가능
  • API 통신 정상 작동

비기능적 요구사항

  • 에러 발생 시 롤백
  • 중복 등록 방지
  • 입력 검증 (전화번호 형식 등)
  • 로그 파일 생성 (추후)

📚 참고 자료

관련 문서

필요한 Python 라이브러리

# 대부분 기본 설치되어 있음
python3 -c "import sqlite3, secrets, string, argparse"  # 기본 라이브러리

# passlib만 추가 설치 필요
pip3 install passlib

DB 경로

farmq.db:    /srv/headscale-tailscale-replacement/farmq-admin/farmq.db
gateway.db:  /srv/pharmq-gateway/gateway.db

체크리스트

개발

  • register_pharmacy.py 스크립트 작성
  • headscale-quick-install.sh 수정
  • 입력 검증 로직 추가
  • 에러 핸들링 추가

테스트

  • 로컬 테스트 (farmq.db, gateway.db)
  • 새 VM에서 전체 플로우 테스트
  • 프론트엔드 로그인 테스트
  • API 통신 테스트

문서화

  • README 업데이트
  • 주석 추가
  • 트러블슈팅 가이드 작성

배포

  • Gitea에 커밋
  • 기존 스크립트 백업
  • 프로덕션 배포

작성일: 2025년 11월 14일 작성자: Claude Code 버전: Headscale Auto-Register Plan v1.0