VNC WebSocket 인증 문제 해결 및 사용자 포털 계획 추가
- Proxmox VNC 티켓 생성 시 패스워드 생성 활성화 - VNC 세션에 생성된 패스워드 저장 및 전달 - noVNC 클라이언트에서 실제 패스워드 사용으로 인증 문제 해결 - ES6 모듈 방식으로 noVNC 라이브러리 로드 - HTML 엔티티 디코딩으로 WebSocket URL 문제 해결 - PharmQ 사용자 포털 서비스 계획서 추가 (KakaoTalk SSO, TossPayments) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2cfe37fd53
commit
895b7a8ee7
404
PharmQ-User-Portal-Service-Plan.md
Normal file
404
PharmQ-User-Portal-Service-Plan.md
Normal file
@ -0,0 +1,404 @@
|
||||
# PharmQ 사용자 포털 및 구독 서비스 기획서
|
||||
|
||||
## 1. 프로젝트 개요
|
||||
|
||||
### 1.1 목적
|
||||
- 약국 사용자가 PharmQ 서비스를 쉽게 이해하고 가입할 수 있는 포털 제공
|
||||
- 카카오 SSO 기반 간편 가입 및 로그인 시스템
|
||||
- 토스페이먼츠 연동을 통한 안전한 구독 결제 서비스
|
||||
- 구독 후 사용자가 실제 서비스를 이용할 수 있는 사용자 대시보드
|
||||
|
||||
### 1.2 서비스 구조
|
||||
```
|
||||
PharmQ 생태계
|
||||
├── 🏢 Super Admin (PSA) - 관리자용 (현재 구현됨)
|
||||
├── 🌐 Public Portal - 서비스 소개 및 가입 페이지 (신규 개발)
|
||||
└── 👤 User Dashboard - 구독자 전용 관리 페이지 (신규 개발)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Public Portal (서비스 소개 및 가입 페이지)
|
||||
|
||||
### 2.1 페이지 구성
|
||||
|
||||
#### 2.1.1 메인 페이지 (`/`)
|
||||
- **헤더**: PharmQ 로고, 네비게이션, 로그인 버튼
|
||||
- **히어로 섹션**: 캐치프레이즈 및 주요 가치 제안
|
||||
- **서비스 소개**: 3가지 서비스 (클라우드 PC, AI CCTV, CRM) 간단 소개
|
||||
- **고객 후기**: 기존 고객 사례 (익명화)
|
||||
- **가격 안내**: 서비스별 월 구독료
|
||||
- **CTA 버튼**: "무료 상담 신청" 또는 "지금 시작하기"
|
||||
|
||||
#### 2.1.2 서비스 상세 페이지 (`/services/{service_code}`)
|
||||
- **클라우드 PC** (`/services/cloud-pc`)
|
||||
- Proxmox 기반 가상 데스크톱 서비스 설명
|
||||
- 원격 근무, 보안, 백업의 장점
|
||||
- 스크린샷 및 데모 영상
|
||||
|
||||
- **AI CCTV** (`/services/ai-cctv`)
|
||||
- 인공지능 기반 보안 모니터링 설명
|
||||
- 실시간 알림, 이상행동 감지 기능
|
||||
- 설치 사례 및 효과
|
||||
|
||||
- **CRM 시스템** (`/services/crm`)
|
||||
- 고객 관계 관리 및 매출 분석 도구
|
||||
- 고객 데이터 관리, 리포트 기능
|
||||
- ROI 계산기
|
||||
|
||||
#### 2.1.3 가격 및 플랜 페이지 (`/pricing`)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ PharmQ 서비스 플랜 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 🖥️ 클라우드 PC 📹 AI CCTV 📊 CRM 시스템 │
|
||||
│ ₩60,000/월 ₩80,000/월 ₩40,000/월 │
|
||||
│ • 가상 데스크톱 제공 • 24시간 모니터링 • 고객 DB 관리 │
|
||||
│ • 자동 백업 • AI 이상행동 감지 • 매출 분석 리포트 │
|
||||
│ • 원격 접속 지원 • 실시간 알림 • 마케팅 도구 │
|
||||
│ │
|
||||
│ ⭐ 추천: 전체 패키지 ₩150,000/월 (₩30,000 할인) │
|
||||
│ [무료 상담 신청] [패키지 선택하기] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 2.1.4 회사 소개 페이지 (`/about`)
|
||||
- Medivault & PharmQ 소개
|
||||
- 팀 소개 및 비전
|
||||
- 오시는 길
|
||||
|
||||
#### 2.1.5 고객센터 (`/support`)
|
||||
- FAQ (자주 묻는 질문)
|
||||
- 문의하기 폼
|
||||
- 연락처 정보
|
||||
|
||||
### 2.2 사용자 가입 프로세스
|
||||
|
||||
#### 2.2.1 카카오 SSO 로그인 (`/auth/kakao`)
|
||||
```
|
||||
사용자 클릭: "카카오로 시작하기"
|
||||
↓
|
||||
카카오 OAuth 인증 페이지 이동
|
||||
↓
|
||||
사용자 카카오 로그인 및 동의
|
||||
↓
|
||||
PharmQ로 리다이렉트 + 사용자 정보 수신
|
||||
↓
|
||||
신규 사용자 → 추가 정보 입력 페이지
|
||||
기존 사용자 → 사용자 대시보드 이동
|
||||
```
|
||||
|
||||
#### 2.2.2 추가 정보 입력 페이지 (`/signup/pharmacy-info`)
|
||||
사용자가 카카오 로그인 후 입력해야 할 추가 정보:
|
||||
|
||||
```sql
|
||||
-- 사용자 기본 정보 (카카오에서 받음)
|
||||
kakao_user_id: VARCHAR(100) -- 카카오 고유 ID
|
||||
name: VARCHAR(50) -- 실명
|
||||
email: VARCHAR(100) -- 이메일
|
||||
phone: VARCHAR(20) -- 휴대폰 번호
|
||||
|
||||
-- 약국 정보 (사용자 직접 입력 또는 약준모 약사면허인증 API활용)
|
||||
pharmacy_name: VARCHAR(100) -- 약국명 *
|
||||
business_license: VARCHAR(20) -- 사업자등록번호 *
|
||||
pharmacy_address: TEXT -- 약국 주소 *
|
||||
pharmacist_license: VARCHAR(30) -- 약사 면허번호 *
|
||||
establishment_date: DATE -- 개업일
|
||||
pharmacy_phone: VARCHAR(20) -- 약국 전화번호
|
||||
fax_number: VARCHAR(20) -- 팩스번호
|
||||
|
||||
-- 선택 정보
|
||||
staff_count: INTEGER -- 직원 수
|
||||
monthly_customers: INTEGER -- 월 평균 고객 수
|
||||
current_pos_system: VARCHAR(50) -- 현재 사용 중인 POS 시스템
|
||||
special_requirements: TEXT -- 특별 요구사항
|
||||
```
|
||||
|
||||
#### 2.2.3 약관 동의
|
||||
- **필수 동의**
|
||||
- 서비스 이용약관
|
||||
- 개인정보 처리방침
|
||||
- 약국 정보 수집 및 이용 동의
|
||||
|
||||
- **선택 동의**
|
||||
- 마케팅 정보 수신 동의 (SMS, 이메일)
|
||||
- 서비스 개선을 위한 데이터 활용 동의
|
||||
|
||||
#### 2.2.4 서비스 선택 및 구독 (`/subscription/select`)
|
||||
1. **서비스 선택**
|
||||
- 개별 서비스 선택 (클라우드 PC, AI CCTV, CRM)
|
||||
- 패키지 선택 (전체 패키지 할인)
|
||||
- 구독 기간 선택 (월간, 연간 - 연간 10% 할인)
|
||||
|
||||
2. **결제 정보 확인**
|
||||
- 선택 서비스 및 금액 확인
|
||||
- 할인 적용 여부 확인
|
||||
- 첫 달 무료 체험 적용
|
||||
|
||||
3. **토스페이먼츠 결제** (`/payment/process`)
|
||||
```javascript
|
||||
// 토스페이먼츠 연동 플로우
|
||||
const payment = TossPayments(clientKey);
|
||||
|
||||
payment.requestPayment('카드', {
|
||||
amount: subscription.amount,
|
||||
orderId: generate_order_id(),
|
||||
orderName: `PharmQ ${services.join(', ')} 구독`,
|
||||
customerName: user.name,
|
||||
customerEmail: user.email,
|
||||
successUrl: 'https://pharmq.co.kr/payment/success',
|
||||
failUrl: 'https://pharmq.co.kr/payment/fail',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. User Dashboard (구독자 전용 관리 페이지)
|
||||
|
||||
### 3.1 인증 및 접근 제어
|
||||
- **로그인**: 카카오 SSO를 통한 로그인만 허용
|
||||
- **권한 체크**: 활성 구독이 있는 사용자만 접근 가능
|
||||
- **세션 관리**: JWT 토큰 기반 세션 관리
|
||||
|
||||
### 3.2 대시보드 구성 (`/dashboard`)
|
||||
|
||||
#### 3.2.1 메인 대시보드
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 👋 안녕하세요, [약국명] [대표자명]님 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 📊 구독 현황 │
|
||||
│ • 🖥️ 클라우드 PC: 활성 (다음 결제: 2025.10.15) │
|
||||
│ • 📹 AI CCTV: 활성 (다음 결제: 2025.10.01) │
|
||||
│ • 📊 CRM: 구독 안함 [구독하기] │
|
||||
│ │
|
||||
│ 💳 이번 달 구독료: ₩140,000 │
|
||||
│ 📈 서비스 이용 현황 │
|
||||
│ • 클라우드 PC 접속: 15회 (이번 달) │
|
||||
│ • AI CCTV 알림: 3건 (최근 7일) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 3.2.2 서비스별 관리 페이지
|
||||
|
||||
**클라우드 PC 관리** (`/dashboard/cloud-pc`)
|
||||
- 가상 머신 상태 확인
|
||||
- 원격 접속 링크 (VNC)
|
||||
- 백업 현황 및 복구 요청
|
||||
- 사용량 통계
|
||||
|
||||
**AI CCTV 관리** (`/dashboard/ai-cctv`)
|
||||
- 실시간 모니터링 화면
|
||||
- 알림 히스토리
|
||||
- 이상행동 감지 로그
|
||||
- 카메라 설정 관리
|
||||
|
||||
**CRM 시스템** (`/dashboard/crm`)
|
||||
- 고객 데이터 관리
|
||||
- 매출 분석 리포트
|
||||
- 마케팅 캠페인 관리
|
||||
- 데이터 내보내기
|
||||
|
||||
#### 3.2.3 구독 관리 (`/dashboard/subscription`)
|
||||
- 현재 구독 서비스 확인
|
||||
- 구독 업그레이드/다운그레이드
|
||||
- 결제 이력 조회
|
||||
- 구독 해지 (해지 사유 조사)
|
||||
|
||||
#### 3.2.4 계정 설정 (`/dashboard/settings`)
|
||||
- 약국 정보 수정
|
||||
- 비밀번호 변경 (카카오 연동이므로 제한적)
|
||||
- 알림 설정 (이메일, SMS)
|
||||
- 개인정보 수정
|
||||
|
||||
#### 3.2.5 고객지원 (`/dashboard/support`)
|
||||
- 1:1 문의하기
|
||||
- 문의 내역 조회
|
||||
- FAQ 및 도움말
|
||||
- 원격 지원 요청
|
||||
|
||||
---
|
||||
|
||||
## 4. 데이터베이스 설계 확장
|
||||
|
||||
### 4.1 신규 테이블 추가
|
||||
|
||||
#### 4.1.1 사용자 계정 관리
|
||||
```sql
|
||||
CREATE TABLE user_accounts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
kakao_user_id VARCHAR(100) UNIQUE NOT NULL,
|
||||
email VARCHAR(100) NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
phone VARCHAR(20),
|
||||
profile_image_url TEXT,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
last_login_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE user_pharmacy_info (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
pharmacy_name VARCHAR(100) NOT NULL,
|
||||
business_license VARCHAR(20) NOT NULL,
|
||||
pharmacy_address TEXT NOT NULL,
|
||||
pharmacist_license VARCHAR(30) NOT NULL,
|
||||
establishment_date DATE,
|
||||
pharmacy_phone VARCHAR(20),
|
||||
fax_number VARCHAR(20),
|
||||
staff_count INTEGER,
|
||||
monthly_customers INTEGER,
|
||||
current_pos_system VARCHAR(50),
|
||||
special_requirements TEXT,
|
||||
verification_status VARCHAR(20) DEFAULT 'PENDING', -- PENDING, VERIFIED, REJECTED
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES user_accounts(id)
|
||||
);
|
||||
```
|
||||
|
||||
#### 4.1.2 구독 및 결제 관리
|
||||
```sql
|
||||
CREATE TABLE user_subscriptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
subscription_status VARCHAR(20) NOT NULL, -- ACTIVE, CANCELLED, SUSPENDED
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE,
|
||||
next_billing_date DATE,
|
||||
monthly_fee DECIMAL(10,2) NOT NULL,
|
||||
billing_cycle VARCHAR(10) DEFAULT 'MONTHLY', -- MONTHLY, YEARLY
|
||||
auto_renewal BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES user_accounts(id),
|
||||
FOREIGN KEY (product_id) REFERENCES service_products(id)
|
||||
);
|
||||
|
||||
CREATE TABLE payment_transactions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
subscription_id INTEGER,
|
||||
toss_payment_key VARCHAR(200) UNIQUE,
|
||||
toss_order_id VARCHAR(100) UNIQUE,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
payment_method VARCHAR(50),
|
||||
payment_status VARCHAR(20), -- SUCCESS, FAILED, CANCELLED, REFUNDED
|
||||
paid_at TIMESTAMP,
|
||||
failed_reason TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES user_accounts(id),
|
||||
FOREIGN KEY (subscription_id) REFERENCES user_subscriptions(id)
|
||||
);
|
||||
```
|
||||
|
||||
#### 4.1.3 서비스 사용 로그
|
||||
```sql
|
||||
CREATE TABLE service_usage_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
subscription_id INTEGER NOT NULL,
|
||||
service_type VARCHAR(20) NOT NULL, -- CLOUD_PC, AI_CCTV, CRM
|
||||
action_type VARCHAR(50) NOT NULL, -- LOGIN, ACCESS, DOWNLOAD, etc.
|
||||
session_duration INTEGER, -- 세션 지속 시간 (초)
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES user_accounts(id),
|
||||
FOREIGN KEY (subscription_id) REFERENCES user_subscriptions(id)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 기술 스택 및 구현
|
||||
|
||||
### 5.1 백엔드 기술 스택
|
||||
- **Framework**: Flask (기존 시스템과 통합)
|
||||
- **Authentication**: 카카오 OAuth 2.0
|
||||
- **Payment**: 토스페이먼츠 API
|
||||
- **Database**: SQLite (개발) → PostgreSQL (운영)
|
||||
- **Session**: Flask-Session + Redis
|
||||
|
||||
### 5.2 프론트엔드 기술 스택
|
||||
- **Template Engine**: Jinja2
|
||||
- **CSS Framework**: Bootstrap 5
|
||||
- **JavaScript**: Vanilla JS + Chart.js
|
||||
- **Icons**: Font Awesome
|
||||
- **Responsive**: Mobile-First 디자인
|
||||
|
||||
### 5.3 보안 고려사항
|
||||
- **데이터 암호화**: 민감 정보 AES-256 암호화
|
||||
- **API 보안**: JWT 토큰 + Rate Limiting
|
||||
- **결제 보안**: 토스페이먼츠 Webhook 검증
|
||||
- **개인정보 보호**: GDPR 준수, 최소한의 정보 수집
|
||||
|
||||
---
|
||||
|
||||
## 6. 개발 로드맵
|
||||
|
||||
### Phase 1: 기본 포털 구축 (2-3주)
|
||||
- [ ] Public Portal 메인 페이지 구현
|
||||
- [ ] 서비스 소개 페이지 구현
|
||||
- [ ] 가격 안내 페이지 구현
|
||||
- [ ] 카카오 SSO 연동
|
||||
|
||||
### Phase 2: 사용자 가입 및 구독 (3-4주)
|
||||
- [ ] 사용자 가입 플로우 구현
|
||||
- [ ] 약국 정보 입력 및 검증 시스템
|
||||
- [ ] 토스페이먼츠 결제 연동
|
||||
- [ ] 구독 생성 및 관리 API
|
||||
|
||||
### Phase 3: 사용자 대시보드 (4-5주)
|
||||
- [ ] 사용자 대시보드 메인 페이지
|
||||
- [ ] 서비스별 관리 페이지 구현
|
||||
- [ ] 구독 관리 및 결제 이력 페이지
|
||||
- [ ] 계정 설정 및 고객 지원
|
||||
|
||||
### Phase 4: 고도화 및 운영 (2-3주)
|
||||
- [ ] 사용량 추적 및 분석 시스템
|
||||
- [ ] 이메일/SMS 알림 시스템
|
||||
- [ ] 관리자 승인 워크플로우
|
||||
- [ ] 성능 최적화 및 보안 강화
|
||||
|
||||
---
|
||||
|
||||
## 7. 예상 효과
|
||||
|
||||
### 7.1 비즈니스 효과
|
||||
- **자동화된 고객 획득**: 24/7 온라인 가입 가능
|
||||
- **결제 자동화**: 토스페이먼츠를 통한 안정적인 정기 결제
|
||||
- **고객 셀프 서비스**: 고객 지원 비용 절감
|
||||
- **데이터 기반 의사결정**: 사용자 행동 분석을 통한 서비스 개선
|
||||
|
||||
### 7.2 사용자 경험 개선
|
||||
- **간편한 가입**: 카카오 SSO로 1분 내 가입 완료
|
||||
- **투명한 가격**: 명확한 가격 정보 및 할인 혜택
|
||||
- **통합 관리**: 모든 서비스를 하나의 대시보드에서 관리
|
||||
- **실시간 지원**: 채팅 및 원격 지원을 통한 빠른 문제 해결
|
||||
|
||||
---
|
||||
|
||||
**작성일**: 2025년 9월 11일
|
||||
**작성자**: PharmQ Development Team
|
||||
**버전**: v1.0
|
||||
|
||||
## 8. 추가 고려사항
|
||||
|
||||
### 8.1 법적 컴플라이언스
|
||||
- **약사법** 준수: 약국 정보 관리 시 관련 법규 준수
|
||||
- **개인정보보호법**: 고객 데이터 수집, 처리, 보관 시 법적 요구사항 충족
|
||||
- **전자상거래법**: 구독 서비스 약관 및 취소 정책 명시
|
||||
|
||||
### 8.2 확장성 고려
|
||||
- **멀티테넌트 아키텍처**: 향후 다양한 업종 확장 대비
|
||||
- **API 우선 설계**: 모바일 앱 및 제3자 연동 대비
|
||||
- **클라우드 네이티브**: 서비스 확장에 따른 인프라 자동 스케일링
|
||||
|
||||
### 8.3 운영 및 모니터링
|
||||
- **서비스 상태 모니터링**: 각 서비스별 실시간 상태 체크
|
||||
- **사용자 행동 분석**: 가입 전환율, 이탈률, 서비스 사용 패턴 분석
|
||||
- **고객 만족도 조사**: 정기적인 NPS 조사 및 피드백 수집
|
||||
@ -547,6 +547,7 @@ def create_app(config_name=None):
|
||||
'vmid': vmid,
|
||||
'vm_name': vm_name,
|
||||
'websocket_url': vnc_data['websocket_url'],
|
||||
'password': vnc_data.get('password', ''), # VNC 패스워드 추가
|
||||
'created_at': datetime.now()
|
||||
}
|
||||
|
||||
@ -571,16 +572,13 @@ def create_app(config_name=None):
|
||||
|
||||
session_data = vnc_sessions[session_id]
|
||||
|
||||
# Proxmox 기본 noVNC URL로 리다이렉트
|
||||
proxmox_vnc_url = f"https://{PROXMOX_HOST}:443/?console=kvm&vmid={session_data['vmid']}&node={session_data['node']}"
|
||||
|
||||
# 리다이렉트 페이지 표시
|
||||
return render_template('vnc_redirect.html',
|
||||
# 직접 WebSocket VNC 연결 (noVNC)
|
||||
return render_template('vnc_console.html',
|
||||
vm_name=session_data['vm_name'],
|
||||
vmid=session_data['vmid'],
|
||||
node=session_data['node'],
|
||||
proxmox_url=proxmox_vnc_url,
|
||||
host=PROXMOX_HOST)
|
||||
websocket_url=session_data['websocket_url'],
|
||||
password=session_data.get('password', ''))
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ VNC 콘솔 오류: {e}")
|
||||
|
||||
@ -107,17 +107,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CDN에서 noVNC 로드 -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/noVNC/1.3.0/core/rfb.min.js"></script>
|
||||
|
||||
<!-- Bootstrap Icons -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
|
||||
<script>
|
||||
// VNC 연결 설정
|
||||
const websocketUrl = '{{ websocket_url }}';
|
||||
<script type="module">
|
||||
// noVNC 라이브러리 import
|
||||
import RFB from 'https://cdn.jsdelivr.net/npm/@novnc/novnc/core/rfb.js';
|
||||
|
||||
// VNC 연결 설정 (HTML 엔티티 디코딩)
|
||||
const rawWebsocketUrl = '{{ websocket_url|safe }}';
|
||||
const websocketUrl = rawWebsocketUrl.replace(/&/g, '&');
|
||||
const vmName = '{{ vm_name }}';
|
||||
const vncPassword = '{{ password }}';
|
||||
|
||||
let rfb;
|
||||
let isConnected = false;
|
||||
@ -129,15 +131,27 @@
|
||||
// VNC 연결 시작
|
||||
function connectVNC() {
|
||||
try {
|
||||
console.log('VNC 연결 시도:', websocketUrl);
|
||||
console.log('Raw WebSocket URL:', rawWebsocketUrl);
|
||||
console.log('Decoded WebSocket URL:', websocketUrl);
|
||||
console.log('VNC Password:', vncPassword);
|
||||
console.log('RFB 클래스 사용 가능:', !!RFB);
|
||||
|
||||
// RFB 객체 생성 (간소화된 설정)
|
||||
rfb = new RFB(canvas, websocketUrl);
|
||||
// URL 유효성 검사
|
||||
if (!websocketUrl || !websocketUrl.startsWith('wss://')) {
|
||||
throw new Error('유효하지 않은 WebSocket URL');
|
||||
}
|
||||
|
||||
// RFB 객체 생성
|
||||
rfb = new RFB(canvas, websocketUrl, {
|
||||
credentials: { password: vncPassword },
|
||||
shared: true
|
||||
});
|
||||
|
||||
// 이벤트 리스너 등록
|
||||
rfb.addEventListener('connect', onConnected);
|
||||
rfb.addEventListener('disconnect', onDisconnected);
|
||||
rfb.addEventListener('credentialsrequired', onCredentialsRequired);
|
||||
rfb.addEventListener('securityfailure', onSecurityFailure);
|
||||
|
||||
// 화면 크기 자동 조정
|
||||
rfb.scaleViewport = true;
|
||||
@ -175,6 +189,12 @@
|
||||
showStatus('🔐 인증 필요', 'VNC 서버에서 인증이 필요합니다.', 'warning');
|
||||
}
|
||||
|
||||
// 보안 실패
|
||||
function onSecurityFailure(e) {
|
||||
console.log('VNC 보안 실패:', e.detail);
|
||||
showStatus('🔒 보안 실패', 'VNC 보안 인증에 실패했습니다: ' + (e.detail.reason || 'Unknown'), 'danger');
|
||||
}
|
||||
|
||||
// 상태 표시
|
||||
function showStatus(title, message, type = 'info') {
|
||||
statusDiv.innerHTML = `
|
||||
@ -221,15 +241,9 @@
|
||||
window.close();
|
||||
};
|
||||
|
||||
// 페이지 로드 후 연결 시작
|
||||
window.addEventListener('load', function() {
|
||||
// noVNC 라이브러리 로드 확인
|
||||
if (typeof RFB === 'undefined') {
|
||||
showStatus('❌ 라이브러리 오류', 'noVNC 라이브러리를 로드할 수 없습니다.', 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
// 잠시 후 연결 시작
|
||||
// 페이지 로드 후 연결 시작 (ES6 모듈은 즉시 사용 가능)
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('DOM 로드 완료, VNC 연결 시작...');
|
||||
setTimeout(connectVNC, 1000);
|
||||
});
|
||||
|
||||
|
||||
@ -106,7 +106,10 @@ class ProxmoxClient:
|
||||
def get_vnc_ticket(self, node: str, vmid: int) -> Optional[Dict]:
|
||||
"""VNC 접속 티켓 생성"""
|
||||
try:
|
||||
data = {'websocket': '1'}
|
||||
data = {
|
||||
'websocket': '1',
|
||||
'generate-password': '1' # 패스워드 생성 활성화
|
||||
}
|
||||
response = self.session.post(
|
||||
f"{self.base_url}/nodes/{node}/qemu/{vmid}/vncproxy",
|
||||
data=data,
|
||||
@ -115,15 +118,21 @@ class ProxmoxClient:
|
||||
|
||||
if response.status_code == 200:
|
||||
vnc_data = response.json()['data']
|
||||
print(f"✅ VNC 티켓 생성 성공: {vnc_data}")
|
||||
|
||||
# WebSocket URL 생성
|
||||
encoded_ticket = quote_plus(vnc_data['ticket'])
|
||||
vnc_data['websocket_url'] = f"wss://{self.host}:443/api2/json/nodes/{node}/qemu/{vmid}/vncwebsocket?port={vnc_data['port']}&vncticket={encoded_ticket}"
|
||||
|
||||
# 디버깅 정보 추가
|
||||
print(f"🔗 WebSocket URL: {vnc_data['websocket_url']}")
|
||||
return vnc_data
|
||||
else:
|
||||
print(f"❌ VNC 티켓 생성 HTTP 오류: {response.status_code}")
|
||||
print(f"Response: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"VNC 티켓 생성 실패: {e}")
|
||||
print(f"❌ VNC 티켓 생성 실패: {e}")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user