- Added support for multiple Proxmox hosts (pve7.0bin.in:443, Healthport PVE:8006) - Enhanced VM management APIs to accept host parameter - Fixed WebSocket URL generation bug (dynamic port handling) - Added comprehensive SSL certificate trust help system - Implemented host selection dropdown in UI - Added VNC connection failure detection and automatic SSL help redirection - Updated session management to store host_key information - Enhanced error handling for different Proxmox configurations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
242 lines
7.9 KiB
HTML
242 lines
7.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ vm_name }} - VNC 콘솔 (프록시)</title>
|
|
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
background-color: dimgrey;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
html {
|
|
height: 100%;
|
|
}
|
|
|
|
#top_bar {
|
|
background-color: #6e84a3;
|
|
color: white;
|
|
font: bold 12px Helvetica;
|
|
padding: 6px 5px 4px 5px;
|
|
border-bottom: 1px outset;
|
|
}
|
|
#status {
|
|
text-align: center;
|
|
}
|
|
#sendCtrlAltDelButton {
|
|
position: fixed;
|
|
top: 0px;
|
|
right: 120px;
|
|
border: 1px outset;
|
|
padding: 5px 5px 4px 5px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
#connectButton {
|
|
position: fixed;
|
|
top: 0px;
|
|
right: 0px;
|
|
border: 1px outset;
|
|
padding: 5px 5px 4px 5px;
|
|
cursor: pointer;
|
|
background-color: #28a745;
|
|
color: white;
|
|
}
|
|
|
|
#screen {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.error {
|
|
color: #ff6b6b;
|
|
background-color: #ffe6e6;
|
|
padding: 10px;
|
|
margin: 10px;
|
|
border-radius: 4px;
|
|
border: 1px solid #ff6b6b;
|
|
}
|
|
|
|
.success {
|
|
color: #28a745;
|
|
background-color: #e6ffe6;
|
|
padding: 10px;
|
|
margin: 10px;
|
|
border-radius: 4px;
|
|
border: 1px solid #28a745;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="top_bar">
|
|
<div id="status">로딩 중...</div>
|
|
<div id="sendCtrlAltDelButton">Ctrl+Alt+Del 전송</div>
|
|
<div id="connectButton">VNC 연결</div>
|
|
</div>
|
|
<div id="screen">
|
|
<div id="connectionMessages"></div>
|
|
</div>
|
|
|
|
<!-- Socket.IO 클라이언트 -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js"></script>
|
|
|
|
<!-- noVNC 라이브러리 -->
|
|
<script type="module" crossorigin="anonymous">
|
|
import RFB from '/static/novnc/core/rfb.js';
|
|
|
|
let rfb;
|
|
let desktopName;
|
|
let socket;
|
|
|
|
// 상태 표시 함수
|
|
function status(text) {
|
|
document.getElementById('status').textContent = text;
|
|
}
|
|
|
|
function showMessage(message, type = 'info') {
|
|
const messagesDiv = document.getElementById('connectionMessages');
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = type;
|
|
messageDiv.textContent = message;
|
|
messagesDiv.appendChild(messageDiv);
|
|
|
|
// 5초 후 메시지 제거
|
|
setTimeout(() => {
|
|
if (messageDiv.parentNode) {
|
|
messageDiv.parentNode.removeChild(messageDiv);
|
|
}
|
|
}, 5000);
|
|
}
|
|
|
|
// noVNC 이벤트 핸들러
|
|
function connectedToServer(e) {
|
|
status("연결됨");
|
|
showMessage("VNC 서버에 성공적으로 연결되었습니다.", 'success');
|
|
}
|
|
|
|
function disconnectedFromServer(e) {
|
|
if (e.detail.clean) {
|
|
status("연결 종료");
|
|
showMessage("VNC 연결이 정상적으로 종료되었습니다.", 'info');
|
|
} else {
|
|
status("연결 실패");
|
|
showMessage("VNC 연결이 예기치 않게 종료되었습니다.", 'error');
|
|
}
|
|
}
|
|
|
|
function credentialsAreRequired(e) {
|
|
status("인증 필요");
|
|
showMessage("VNC 서버에서 추가 인증을 요구합니다.", 'error');
|
|
}
|
|
|
|
function updateDesktopName(e) {
|
|
desktopName = e.detail.name;
|
|
status("연결됨: " + desktopName);
|
|
}
|
|
|
|
function onSecurityFailure(e) {
|
|
status("보안 인증 실패");
|
|
showMessage("VNC 보안 인증에 실패했습니다: " + e.detail.reason, 'error');
|
|
}
|
|
|
|
// Socket.IO 연결 및 이벤트 핸들러
|
|
function initSocketIO() {
|
|
socket = io();
|
|
|
|
socket.on('connect', function() {
|
|
console.log('Socket.IO 연결됨');
|
|
status("서버 연결됨 - VNC 연결 대기 중");
|
|
});
|
|
|
|
socket.on('disconnect', function() {
|
|
console.log('Socket.IO 연결 종료');
|
|
status("서버 연결 종료");
|
|
});
|
|
|
|
socket.on('vnc_ready', function(data) {
|
|
console.log('VNC 연결 준비 완료:', data);
|
|
status("VNC 연결 중...");
|
|
|
|
// noVNC로 직접 연결 (테스트용)
|
|
try {
|
|
const websocketUrl = data.websocket_url;
|
|
const password = data.password;
|
|
|
|
console.log('WebSocket URL:', websocketUrl);
|
|
console.log('VNC Password:', password);
|
|
|
|
rfb = new RFB(document.getElementById('screen'), websocketUrl,
|
|
{ credentials: { password: password } });
|
|
|
|
// 이벤트 리스너 등록
|
|
rfb.addEventListener("connect", connectedToServer);
|
|
rfb.addEventListener("disconnect", disconnectedFromServer);
|
|
rfb.addEventListener("credentialsrequired", credentialsAreRequired);
|
|
rfb.addEventListener("desktopname", updateDesktopName);
|
|
rfb.addEventListener("securityfailure", onSecurityFailure);
|
|
|
|
showMessage("VNC 연결을 시도 중입니다...", 'info');
|
|
|
|
} catch (error) {
|
|
console.error('VNC 연결 오류:', error);
|
|
showMessage("VNC 연결 중 오류가 발생했습니다: " + error.message, 'error');
|
|
}
|
|
});
|
|
|
|
socket.on('vnc_error', function(data) {
|
|
console.error('VNC 프록시 오류:', data);
|
|
status("VNC 연결 실패");
|
|
showMessage("VNC 연결 실패: " + data.error, 'error');
|
|
});
|
|
}
|
|
|
|
// VNC 연결 시작
|
|
function connectVNC() {
|
|
if (socket && socket.connected) {
|
|
showMessage("VNC 연결을 요청 중입니다...", 'info');
|
|
status("VNC 연결 요청 중...");
|
|
|
|
socket.emit('vnc_connect', {
|
|
vm_id: {{ vmid }},
|
|
node: '{{ node }}',
|
|
vm_name: '{{ vm_name }}'
|
|
});
|
|
} else {
|
|
showMessage("서버 연결이 필요합니다. 잠시 후 다시 시도해주세요.", 'error');
|
|
}
|
|
}
|
|
|
|
// Ctrl+Alt+Del 전송
|
|
function sendCtrlAltDel() {
|
|
if (rfb) {
|
|
rfb.sendCtrlAltDel();
|
|
showMessage("Ctrl+Alt+Del을 전송했습니다.", 'info');
|
|
}
|
|
}
|
|
|
|
// 이벤트 리스너 설정
|
|
document.getElementById('connectButton').onclick = connectVNC;
|
|
document.getElementById('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
|
|
|
|
// 페이지 로드 시 Socket.IO 초기화
|
|
window.addEventListener('load', function() {
|
|
initSocketIO();
|
|
});
|
|
|
|
// 페이지 언로드 시 연결 정리
|
|
window.addEventListener('beforeunload', function() {
|
|
if (rfb) {
|
|
rfb.disconnect();
|
|
}
|
|
if (socket) {
|
|
socket.disconnect();
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |