📋 기획 및 설계: - PharmQ SaaS 서비스 기획서 작성 - 구독 서비스 라인업 정의 (클라우드PC, AI CCTV, CRM) - DB 스키마 설계 및 API 아키텍처 설계 🗄️ 데이터베이스 구조: - service_products: 서비스 상품 마스터 테이블 - pharmacy_subscriptions: 약국별 구독 현황 테이블 - subscription_usage_logs: 서비스 이용 로그 테이블 - billing_history: 결제 이력 테이블 - 샘플 데이터 자동 생성 (21개 구독, 월 118만원 매출) 🔧 백엔드 API 구현: - 구독 현황 통계 API (/api/subscriptions/stats) - 약국별 구독 조회 API (/api/pharmacies/subscriptions) - 구독 상세 정보 API (/api/pharmacy/{id}/subscriptions) - 구독 생성/해지 API (/api/subscriptions) 🖥️ 프론트엔드 UI 구현: - 대시보드 구독 현황 카드 (월 매출, 구독 수, 구독률 등) - 약국 목록에 구독 상태 아이콘 및 월 구독료 표시 - 약국 상세 페이지 구독 서비스 섹션 추가 - 실시간 구독 생성/해지 기능 구현 ✨ 주요 특징: - 서비스별 색상 코딩 및 이모지 아이콘 시스템 - 실시간 업데이트 (구독 생성/해지 즉시 반영) - 반응형 디자인 (모바일/태블릿 최적화) - 툴팁 기반 상세 정보 표시 📊 현재 구독 현황: - 총 월 매출: ₩1,180,000 - 구독 약국: 10/14개 (71.4%) - AI CCTV: 6개 약국, CRM: 10개 약국, 클라우드PC: 5개 약국 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
376 lines
12 KiB
Markdown
376 lines
12 KiB
Markdown
# 팜큐(FARMQ) Proxmox VNC 통합 시스템 기획서
|
|
|
|
## 🎯 프로젝트 개요
|
|
|
|
### 목표
|
|
Flask Admin 웹 인터페이스에서 **한 번의 클릭**으로 Proxmox VM의 VNC 화면에 접속할 수 있는 통합 시스템 구축
|
|
|
|
### 핵심 아이디어
|
|
1. **Headscale 네트워크**를 통해 모든 약국의 Proxmox 호스트에 접근 가능
|
|
2. **Proxmox VNC API**를 활용하여 VM 화면 원격 제어
|
|
3. **브라우저 기반 VNC 클라이언트** (Guacamole/noVNC)로 즉시 접속
|
|
4. **Flask Admin 버튼** → **VNC 화면** 원클릭 연결
|
|
|
|
## 🏗️ 시스템 아키텍처
|
|
|
|
```
|
|
[Flask Admin Dashboard]
|
|
↓ (클릭)
|
|
[VNC 연결 요청 API]
|
|
↓
|
|
[Headscale 네트워크]
|
|
↓ (100.64.0.x)
|
|
[Proxmox Host Server]
|
|
↓ (VNC API)
|
|
[VM VNC Console]
|
|
↓
|
|
[noVNC/Guacamole Web Client]
|
|
↓
|
|
[사용자 브라우저]
|
|
```
|
|
|
|
## 📋 기술 스택
|
|
|
|
### Frontend
|
|
- **noVNC**: HTML5 VNC 클라이언트 (가벼움, 쉬운 통합)
|
|
- **Apache Guacamole**: 더 고급 기능 (클립보드, 파일 전송)
|
|
- **Bootstrap 5**: UI 프레임워크
|
|
|
|
### Backend
|
|
- **Flask**: 웹 서버 및 API
|
|
- **Proxmox VE API**: VM 관리 및 VNC 토큰 생성
|
|
- **WebSocket Proxy**: VNC 트래픽 중계
|
|
|
|
### Network
|
|
- **Headscale**: 팜큐 네트워크 (100.64.0.0/10)
|
|
- **Tailscale**: 각 Proxmox 호스트 연결
|
|
|
|
## 🔧 구현 단계
|
|
|
|
### Phase 1: Proxmox API 통합 (1-2일)
|
|
|
|
#### 1.1 Proxmox API 클라이언트 구현
|
|
```python
|
|
# utils/proxmox_client.py
|
|
class ProxmoxClient:
|
|
def __init__(self, host, username, password):
|
|
self.host = host # 100.64.0.x (Headscale IP)
|
|
|
|
def get_vm_list(self):
|
|
"""VM 목록 조회"""
|
|
|
|
def get_vnc_ticket(self, vmid):
|
|
"""VNC 접속 티켓 생성"""
|
|
|
|
def get_vm_status(self, vmid):
|
|
"""VM 상태 확인"""
|
|
```
|
|
|
|
#### 1.2 데이터베이스 모델 확장
|
|
```python
|
|
# models/farmq_models.py
|
|
class ProxmoxVM(FarmqBase):
|
|
__tablename__ = 'proxmox_vms'
|
|
|
|
id = Column(Integer, primary_key=True)
|
|
pharmacy_id = Column(Integer, ForeignKey('pharmacy_info.id'))
|
|
proxmox_host_ip = Column(String(15)) # 100.64.0.x
|
|
vmid = Column(Integer)
|
|
vm_name = Column(String(100))
|
|
vm_type = Column(String(50)) # windows, linux
|
|
status = Column(String(20)) # running, stopped
|
|
cpu_cores = Column(Integer)
|
|
memory_mb = Column(Integer)
|
|
created_at = Column(DateTime, default=datetime.now)
|
|
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
|
|
```
|
|
|
|
### Phase 2: VNC 웹 클라이언트 구현 (2-3일)
|
|
|
|
#### 2.1 noVNC 통합 (권장)
|
|
```html
|
|
<!-- templates/vnc/novnc.html -->
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>{{ vm_name }} - VNC Console</title>
|
|
<script src="/static/novnc/vnc.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="vnc-container">
|
|
<canvas id="vnc-canvas"></canvas>
|
|
</div>
|
|
|
|
<script>
|
|
const rfb = new RFB(document.getElementById('vnc-canvas'),
|
|
'ws://{{ flask_server }}/vnc/{{ session_id }}');
|
|
</script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
#### 2.2 WebSocket Proxy 서버
|
|
```python
|
|
# vnc_proxy.py
|
|
import websockets
|
|
import asyncio
|
|
|
|
class VNCProxy:
|
|
async def proxy_vnc_connection(self, websocket, path):
|
|
# Flask → Proxmox VNC 연결 중계
|
|
session_id = extract_session_id(path)
|
|
proxmox_vnc = await connect_to_proxmox_vnc(session_id)
|
|
|
|
# 양방향 데이터 중계
|
|
await asyncio.gather(
|
|
relay_websocket_to_vnc(websocket, proxmox_vnc),
|
|
relay_vnc_to_websocket(proxmox_vnc, websocket)
|
|
)
|
|
```
|
|
|
|
### Phase 3: Flask Admin 통합 (1일)
|
|
|
|
#### 3.1 VNC 연결 API 엔드포인트
|
|
```python
|
|
# app.py
|
|
@app.route('/api/vm/<int:vm_id>/vnc', methods=['POST'])
|
|
def connect_vm_vnc(vm_id):
|
|
"""VM VNC 연결 세션 생성"""
|
|
try:
|
|
vm = get_vm_by_id(vm_id)
|
|
proxmox = ProxmoxClient(vm.proxmox_host_ip, username, password)
|
|
|
|
# VNC 티켓 생성
|
|
vnc_ticket = proxmox.get_vnc_ticket(vm.vmid)
|
|
|
|
# 세션 생성
|
|
session_id = create_vnc_session(vm_id, vnc_ticket)
|
|
|
|
return jsonify({
|
|
'session_id': session_id,
|
|
'vnc_url': f'/vnc/console/{session_id}',
|
|
'vm_info': vm.to_dict()
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@app.route('/vnc/console/<session_id>')
|
|
def vnc_console(session_id):
|
|
"""VNC 콘솔 페이지"""
|
|
session = get_vnc_session(session_id)
|
|
return render_template('vnc/novnc.html',
|
|
session=session,
|
|
vm_name=session['vm_name'])
|
|
```
|
|
|
|
#### 3.2 약국 관리 페이지 업데이트
|
|
```html
|
|
<!-- templates/pharmacy/detail.html -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>가상 머신 목록</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>VM 이름</th>
|
|
<th>상태</th>
|
|
<th>타입</th>
|
|
<th>리소스</th>
|
|
<th>액션</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for vm in pharmacy_vms %}
|
|
<tr>
|
|
<td>{{ vm.vm_name }}</td>
|
|
<td>
|
|
{% if vm.status == 'running' %}
|
|
<span class="badge bg-success">실행 중</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">정지</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ vm.vm_type }}</td>
|
|
<td>{{ vm.cpu_cores }}C / {{ vm.memory_mb }}MB</td>
|
|
<td>
|
|
{% if vm.status == 'running' %}
|
|
<button class="btn btn-primary btn-sm"
|
|
onclick="openVNC({{ vm.id }})">
|
|
<i class="fas fa-desktop"></i> VNC 접속
|
|
</button>
|
|
{% endif %}
|
|
<button class="btn btn-info btn-sm"
|
|
onclick="showVMDetails({{ vm.id }})">
|
|
<i class="fas fa-info-circle"></i> 상세
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function openVNC(vmId) {
|
|
try {
|
|
showSpinner('VNC 연결 준비 중...');
|
|
|
|
const response = await fetch(`/api/vm/${vmId}/vnc`, {
|
|
method: 'POST'
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
// 새 탭에서 VNC 콘솔 열기
|
|
window.open(data.vnc_url, '_blank',
|
|
'width=1024,height=768,scrollbars=yes,resizable=yes');
|
|
} else {
|
|
showToast(data.error, 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('VNC 연결 실패: ' + error.message, 'error');
|
|
} finally {
|
|
hideSpinner();
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
## 📊 데이터 흐름
|
|
|
|
### 1. VM 목록 동기화
|
|
```
|
|
Proxmox API → Flask Backend → Database → Admin Dashboard
|
|
```
|
|
|
|
### 2. VNC 연결 프로세스
|
|
```
|
|
1. 사용자가 "VNC 접속" 버튼 클릭
|
|
2. Flask API가 Proxmox API 호출하여 VNC 티켓 생성
|
|
3. WebSocket 프록시 세션 생성
|
|
4. 새 브라우저 탭에서 noVNC 클라이언트 실행
|
|
5. 실시간 VM 화면 표시
|
|
```
|
|
|
|
## 🔐 보안 고려사항
|
|
|
|
### 인증 및 권한
|
|
```python
|
|
# 약국별 VM 접근 권한 검증
|
|
def check_vm_access_permission(user_id, vm_id):
|
|
"""사용자가 해당 VM에 접근 권한이 있는지 확인"""
|
|
user_pharmacy = get_user_pharmacy(user_id)
|
|
vm_pharmacy = get_vm_pharmacy(vm_id)
|
|
return user_pharmacy.id == vm_pharmacy.id
|
|
|
|
# VNC 세션 시간 제한
|
|
VNC_SESSION_TIMEOUT = 3600 # 1시간
|
|
```
|
|
|
|
### 네트워크 보안
|
|
- **Headscale 네트워크 내부**에서만 Proxmox 접근
|
|
- **HTTPS/WSS** 암호화 통신
|
|
- **세션 기반** 일회성 VNC 토큰
|
|
|
|
## 🎨 UI/UX 설계
|
|
|
|
### 메인 대시보드
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ 📊 팜큐 관리 대시보드 │
|
|
├─────────────────────────────────────────┤
|
|
│ 약국: 세종온누리약국 │
|
|
│ ┌─────────┬──────────┬────────┬─────────┐ │
|
|
│ │ VM 이름 │ 상태 │ 타입 │ 액션 │ │
|
|
│ ├─────────┼──────────┼────────┼─────────┤ │
|
|
│ │ POS-01 │ 🟢 실행중 │ Win11 │ [VNC접속]│ │
|
|
│ │ SERVER │ 🟢 실행중 │ Ubuntu │ [VNC접속]│ │
|
|
│ │ BACKUP │ ⚪ 정지 │ Win10 │ [시작] │ │
|
|
│ └─────────┴──────────┴────────┴─────────┘ │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
### VNC 콘솔 화면
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ 🖥️ POS-01 (Windows 11) - VNC Console │
|
|
├─────────────────────────────────────────┤
|
|
│ [전체화면] [클립보드] [Ctrl+Alt+Del] │
|
|
├─────────────────────────────────────────┤
|
|
│ │
|
|
│ VM 화면이 여기에 표시 │
|
|
│ (noVNC Canvas) │
|
|
│ │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
## 📋 구현 체크리스트
|
|
|
|
### Backend (Flask)
|
|
- [ ] Proxmox API 클라이언트 구현
|
|
- [ ] ProxmoxVM 데이터 모델 생성
|
|
- [ ] VNC 세션 관리 시스템
|
|
- [ ] WebSocket 프록시 서버
|
|
- [ ] API 엔드포인트 구현
|
|
|
|
### Frontend (Templates)
|
|
- [ ] noVNC 라이브러리 통합
|
|
- [ ] VNC 콘솔 페이지 템플릿
|
|
- [ ] 약국 상세 페이지 VM 섹션
|
|
- [ ] JavaScript VNC 연결 함수
|
|
|
|
### 시스템 통합
|
|
- [ ] VM 목록 자동 동기화
|
|
- [ ] 권한 검증 시스템
|
|
- [ ] 에러 처리 및 로깅
|
|
- [ ] 성능 최적화
|
|
|
|
## 🚀 배포 계획
|
|
|
|
### 개발 환경 테스트
|
|
1. **로컬 Proxmox 테스트**: VirtualBox/VMware로 Proxmox VE 설치
|
|
2. **noVNC 연동 테스트**: 기본 VNC 연결 확인
|
|
3. **Headscale 네트워크 테스트**: 원격 Proxmox 접근
|
|
|
|
### 운영 환경 적용
|
|
1. **점진적 배포**: 1개 약국부터 테스트
|
|
2. **모니터링 시스템**: VNC 연결 로그 및 성능 측정
|
|
3. **백업 접근 방법**: VNC 실패 시 SSH/RDP 대안
|
|
|
|
## 💡 추가 기능 아이디어
|
|
|
|
### Phase 2 고급 기능
|
|
- **다중 모니터 지원**: VM이 여러 화면을 사용하는 경우
|
|
- **클립보드 공유**: 로컬 PC ↔ 원격 VM 텍스트 복사
|
|
- **파일 전송**: 드래그앤드롭 파일 업로드
|
|
- **스크린샷 캡처**: 문제 해결을 위한 화면 저장
|
|
- **세션 녹화**: 작업 과정 기록
|
|
|
|
### 모니터링 및 분석
|
|
- **VNC 사용 통계**: 접속 시간, 빈도 분석
|
|
- **VM 성능 모니터링**: CPU, 메모리 사용률 실시간 표시
|
|
- **접속 이력 관리**: 언제, 누가, 어떤 VM에 접속했는지 로그
|
|
|
|
## 🎯 예상 효과
|
|
|
|
### 업무 효율성
|
|
- **즉시 원격 지원**: 약국 직원 도움 요청 시 바로 화면 접속
|
|
- **중앙 집중 관리**: 모든 약국 VM을 한 곳에서 관리
|
|
- **문제 해결 시간 단축**: 전화 설명 → 직접 화면 제어
|
|
|
|
### 기술적 장점
|
|
- **Headscale 네트워크 활용**: 기존 인프라 최대한 활용
|
|
- **브라우저 기반**: 별도 소프트웨어 설치 불필요
|
|
- **확장성**: 새로운 약국 추가 시 자동 연동
|
|
|
|
---
|
|
|
|
## 📞 문의 및 지원
|
|
|
|
이 시스템 구현 과정에서 기술적 이슈나 추가 요구사항이 있으면 언제든 문의하세요.
|
|
|
|
**핵심 목표**: 🖱️ **원클릭** → 🖥️ **VM 화면 접속** |