🚀 Pharmq.kr Live Production Setup Complete

 Headscale + farmq-admin 라이브 서버 구축 완료
- Docker Headscale 서버 설정 (포트 8070)
- farmq-admin 웹 GUI 연동 완료 (포트 5001)
- head.pharmq.kr 도메인 연결
- 1년 유효 재사용 가능 preauth key 생성
- 클라이언트 자동 등록 스크립트 완성

🔧 farmq-admin 설정:
- Headscale CLI API 래퍼 구현
- 실시간 노드/사용자 관리 GUI
- 데이터베이스 경로 수정 (/srv/headscale-tailscale-replacement/data/)
- 안전한 JSON 파싱 및 에러 처리 개선

📋 클라이언트 등록:
- register-client-pharmq-live.sh 스크립트
- head.pharmq.kr 도메인 사용
- 자동 Tailscale 설치 및 등록
- 테스트 완료 (100.64.0.1 IP 할당됨)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
PharmQ Admin 2025-09-22 11:17:10 +00:00
parent 7aa08682b8
commit 36a4dca165
5 changed files with 366 additions and 22 deletions

View File

@ -24,7 +24,7 @@ from sqlalchemy import or_
import subprocess
from utils.proxmox_client import ProxmoxClient
from utils.vnc_proxy import init_vnc_proxy, get_vnc_proxy
from utils.vnc_websocket_proxy import vnc_proxy
# from utils.vnc_websocket_proxy import vnc_proxy # VNC proxy removed temporarily
import websockets
import requests
from urllib3.exceptions import InsecureRequestWarning
@ -100,7 +100,7 @@ def create_app(config_name=None):
# 데이터베이스 초기화
init_databases(
headscale_db_uri='sqlite:////srv/headscale-setup/data/db.sqlite',
headscale_db_uri='sqlite:////srv/headscale-tailscale-replacement/data/db.sqlite',
farmq_db_uri='sqlite:///farmq.db'
)
@ -1015,9 +1015,26 @@ def create_app(config_name=None):
text=True,
check=True
)
users_data = json.loads(result.stdout)
# JSON 파싱 및 안전한 처리
try:
if result.stdout and result.stdout.strip():
users_data = json.loads(result.stdout)
else:
users_data = []
except (json.JSONDecodeError, ValueError):
users_data = []
# users_data가 None이거나 리스트가 아닌 경우 처리
if users_data is None:
users_data = []
elif not isinstance(users_data, list):
# dict 형태로 단일 사용자가 올 수도 있음
if isinstance(users_data, dict):
users_data = [users_data]
else:
users_data = []
# FARMQ 약국 정보와 매칭 (명시적으로 매핑된 것만)
farmq_session = get_farmq_session()
try:
@ -1027,10 +1044,10 @@ def create_app(config_name=None):
for p in pharmacies:
if p.headscale_user_name and p.headscale_user_name.strip():
pharmacy_map[p.headscale_user_name] = p
# 사용자별 노드 수 조회
for user in users_data:
user_name = user.get('name', '')
user_name = user.get('name', '') if isinstance(user, dict) else ''
# 약국 정보 매칭 - 명시적으로 연결된 것만
pharmacy = pharmacy_map.get(user_name)
@ -1041,14 +1058,31 @@ def create_app(config_name=None):
} if pharmacy else None
# 해당 사용자의 노드 수 조회
node_result = subprocess.run(
['docker', 'exec', 'headscale', 'headscale', 'nodes', 'list', '-o', 'json'],
capture_output=True,
text=True,
check=True
)
nodes_data = json.loads(node_result.stdout)
user['node_count'] = len([n for n in nodes_data if n.get('user', {}).get('name') == user_name])
try:
node_result = subprocess.run(
['docker', 'exec', 'headscale', 'headscale', 'nodes', 'list', '-o', 'json'],
capture_output=True,
text=True,
check=True
)
# JSON 파싱 및 안전한 처리
if node_result.stdout and node_result.stdout.strip():
nodes_data = json.loads(node_result.stdout)
else:
nodes_data = []
if nodes_data is None:
nodes_data = []
elif not isinstance(nodes_data, list):
if isinstance(nodes_data, dict):
nodes_data = [nodes_data]
else:
nodes_data = []
user['node_count'] = len([n for n in nodes_data if isinstance(n, dict) and n.get('user', {}).get('name') == user_name])
except (subprocess.CalledProcessError, json.JSONDecodeError, Exception):
user['node_count'] = 0
return jsonify({'success': True, 'users': users_data})

View File

@ -10,14 +10,14 @@ class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'farmq-secret-key-change-in-production'
# 데이터베이스 설정 (기존 Headscale SQLite DB 사용)
DATABASE_PATH = '/srv/headscale-setup/data/db.sqlite'
DATABASE_PATH = '/srv/headscale-tailscale-replacement/data/db.sqlite'
SQLALCHEMY_DATABASE_URI = f'sqlite:///{DATABASE_PATH}'
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 기존 Headplane 연동 설정
HEADPLANE_URL = os.environ.get('HEADPLANE_URL') or 'http://localhost:3000'
HEADSCALE_URL = os.environ.get('HEADSCALE_URL') or 'http://localhost:8070'
HEADSCALE_API_KEY = os.environ.get('HEADSCALE_API_KEY') or '8qRr1IB.tV95CmA0fLaCiGGIgBfeoN9daHceFkzI'
HEADSCALE_API_KEY = os.environ.get('HEADSCALE_API_KEY') or '0HHiZBM.lULHUaFbCQPQqU7-3tUCqFuB9JeueS9r'
# 모니터링 설정
MONITORING_INTERVAL = 30 # 30초마다 데이터 수집

View File

@ -28,7 +28,7 @@ def init_databases(headscale_db_uri: str, farmq_db_uri: str = None):
# FARMQ 전용 데이터베이스 (외래키 제약조건 없음)
if farmq_db_uri is None:
farmq_db_uri = "sqlite:///farmq-admin/farmq.sqlite"
farmq_db_uri = "sqlite:///farmq.db"
farmq_manager = create_farmq_database_manager(farmq_db_uri)
print(f"✅ FARMQ 데이터베이스 초기화: {farmq_db_uri}")

309
giteamd.md Normal file
View File

@ -0,0 +1,309 @@
# Gitea 리포지토리 생성 및 푸시 가이드
## 🏠 서버 정보
- **Gitea 서버**: `git.0bin.in`
- **사용자명**: `thug0bin`
- **이메일**: `thug0bin@gmail.com`
- **액세스 토큰**: `d83f70b219c6028199a498fb94009f4c1debc9a9`
## 🚀 새 리포지토리 생성 및 푸시 과정
### 1. 로컬 Git 리포지토리 초기화
```bash
# 프로젝트 디렉토리로 이동
cd /path/to/your/project
# Git 초기화
git init
# .gitignore 파일 생성 (필요시)
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Build outputs
dist/
build/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDE
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
# Logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
# Database
*.db
*.sqlite
*.sqlite3
EOF
```
### 2. Git 사용자 설정 확인
```bash
# Git 사용자 정보 확인
git config --list | grep -E "user"
# 설정되지 않은 경우 설정
git config --global user.name "시골약사"
git config --global user.email "thug0bin@gmail.com"
```
### 3. 첫 번째 커밋
```bash
# 모든 파일 스테이징
git add .
# 첫 커밋 (상세한 커밋 메시지 예시)
git commit -m "$(cat <<'EOF'
Initial commit: [프로젝트명]
✨ [주요 기능 설명]
- 기능 1
- 기능 2
- 기능 3
🛠️ 기술 스택:
- 사용된 기술들 나열
🔧 주요 구성:
- 프로젝트 구조 설명
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
EOF
)"
```
### 4. 원격 리포지토리 연결 및 푸시
```bash
# 원격 리포지토리 추가 (리포지토리명을 실제 이름으로 변경)
git remote add origin https://thug0bin:d83f70b219c6028199a498fb94009f4c1debc9a9@git.0bin.in/thug0bin/[REPOSITORY_NAME].git
# 브랜치를 main으로 변경
git branch -M main
# 원격 리포지토리로 푸시
git push -u origin main
```
## 📝 리포지토리명 네이밍 규칙
### 권장 네이밍 패턴:
- **프론트엔드 프로젝트**: `project-name-frontend`
- **백엔드 프로젝트**: `project-name-backend`
- **풀스택 프로젝트**: `project-name-fullstack`
- **도구/유틸리티**: `tool-name-utils`
- **문서/가이드**: `project-name-docs`
### 예시:
- `figma-admin-dashboard`
- `anipharm-api-server`
- `inventory-management-system`
- `member-portal-frontend`
## 🔄 기존 리포지토리에 추가 커밋
```bash
# 변경사항 확인
git status
# 변경된 파일 스테이징
git add .
# 또는 특정 파일만 스테이징
git add path/to/specific/file
# 커밋
git commit -m "커밋 메시지"
# 푸시
git push origin main
```
## 🌿 브랜치 작업
```bash
# 새 브랜치 생성 및 전환
git checkout -b feature/new-feature
# 브랜치에서 작업 후 커밋
git add .
git commit -m "Feature: 새로운 기능 추가"
# 브랜치 푸시
git push -u origin feature/new-feature
# main 브랜치로 돌아가기
git checkout main
# 브랜치 병합 (필요시)
git merge feature/new-feature
```
## 🛠️ 자주 사용하는 Git 명령어
```bash
# 현재 상태 확인
git status
# 변경 내역 확인
git diff
# 커밋 히스토리 확인
git log --oneline
# 원격 리포지토리 정보 확인
git remote -v
# 특정 포트 프로세스 종료 (개발 서버 관련)
lsof -ti:PORT_NUMBER | xargs -r kill -9
```
## 🔧 포트 관리 스크립트
```bash
# 특정 포트 종료 함수 추가 (bashrc에 추가 가능)
killport() {
if [ -z "$1" ]; then
echo "Usage: killport <port_number>"
return 1
fi
lsof -ti:$1 | xargs -r kill -9
echo "Killed processes on port $1"
}
# 사용 예시
# killport 7738
# killport 5000
```
## 📋 VS Code 워크스페이스 설정
여러 리포지토리를 동시에 관리하려면 워크스페이스 파일을 생성하세요:
```json
{
"folders": [
{
"name": "Main Repository",
"path": "."
},
{
"name": "New Project",
"path": "./new-project-folder"
}
],
"settings": {
"git.enableSmartCommit": true,
"git.confirmSync": false,
"git.autofetch": true
}
}
```
## 🚨 문제 해결
### 1. 인증 실패
```bash
# 토큰이 만료된 경우, 새 토큰으로 원격 URL 업데이트
git remote set-url origin https://thug0bin:NEW_TOKEN@git.0bin.in/thug0bin/repo-name.git
```
### 2. 푸시 거부
```bash
# 원격 변경사항을 먼저 가져오기
git pull origin main --rebase
# 충돌 해결 후 푸시
git push origin main
```
### 3. 대용량 파일 문제
```bash
# Git LFS 설정 (필요시)
git lfs install
git lfs track "*.zip"
git lfs track "*.gz"
git add .gitattributes
```
## 📊 커밋 메시지 템플릿
### 기본 템플릿:
```
타입: 간단한 설명
상세한 설명 (선택사항)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
```
### 타입별 예시:
- `✨ feat: 새로운 기능 추가`
- `🐛 fix: 버그 수정`
- `📝 docs: 문서 업데이트`
- `🎨 style: 코드 포맷팅`
- `♻️ refactor: 코드 리팩토링`
- `⚡ perf: 성능 개선`
- `✅ test: 테스트 추가`
- `🔧 chore: 빌드 설정 변경`
## 🔗 유용한 링크
- **Gitea 웹 인터페이스**: https://git.0bin.in/
- **내 리포지토리 목록**: https://git.0bin.in/thug0bin
- **새 리포지토리 생성**: https://git.0bin.in/repo/create
## 💡 팁과 모범 사례
1. **정기적인 커밋**: 작은 단위로 자주 커밋하세요
2. **의미있는 커밋 메시지**: 변경 사항을 명확히 설명하세요
3. **브랜치 활용**: 기능별로 브랜치를 나누어 작업하세요
4. **.gitignore 활용**: 불필요한 파일은 제외하세요
5. **문서화**: README.md와 같은 문서를 항상 업데이트하세요
---
**작성일**: 2025년 7월 29일
**마지막 업데이트**: 토큰 및 서버 정보 최신화
**참고**: 이 가이드는 재사용 가능하도록 작성되었습니다. 새 프로젝트마다 참고하세요.
> 💡 **중요**: 액세스 토큰은 보안이 중요한 정보입니다. 공개 저장소에 업로드하지 마세요!

View File

@ -1,13 +1,14 @@
#!/bin/bash
# 팜큐(FARMQ) Headscale 클라이언트 등록 스크립트
# 사용법: ./register-client.sh
# 팜큐(FARMQ) Headscale 클라이언트 등록 스크립트 - LIVE 서버용
# 사용법: ./register-client-pharmq-live.sh
# 대상 서버: head.pharmq.kr (Live Production)
set -e
# 설정
HEADSCALE_SERVER="https://head.0bin.in"
PREAUTH_KEY="fc4f2dc55ee00c5352823d156129b9ce2df4db02f1d76a21"
HEADSCALE_SERVER="http://head.pharmq.kr"
PREAUTH_KEY="b46923995afeaec90e588168f2e1bf99801775e8657ce003"
# 색상 출력 함수
print_status() {