TestStaticHTML / index.html
jeongsoo's picture
init
2ed27ad
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LocalPCAgent ์ œ์–ด</title>
<style>
body {
font-family: -apple-system, system-ui, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
.container { margin-bottom: 20px; }
.card {
border: 1px solid #ddd;
border-radius: 5px;
padding: 15px;
margin-bottom: 15px;
}
button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
}
button:hover { background-color: #45a049; }
input, select, textarea {
width: 100%;
padding: 8px;
margin: 8px 0;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.log-area {
background-color: #f5f5f5;
border: 1px solid #ddd;
padding: 10px;
height: 200px;
overflow-y: auto;
font-family: monospace;
}
.log-success { color: green; }
.log-error { color: red; }
.tab-buttons { display: flex; margin-bottom: 15px; }
.tab-button {
flex: 1;
background-color: #f1f1f1;
padding: 10px;
text-align: center;
cursor: pointer;
}
.tab-button.active {
background-color: #4CAF50;
color: white;
}
.tab-content { display: none; }
.tab-content.active { display: block; }
.status-indicator {
display: inline-block;
padding: 5px 10px;
border-radius: 4px;
margin-left: 10px;
}
.connected {
background-color: rgba(16, 185, 129, 0.1);
color: #10b981;
}
.disconnected {
background-color: rgba(239, 68, 68, 0.1);
color: #ef4444;
}
.result-panel {
background-color: #f8f8f8;
border: 1px solid #ddd;
padding: 10px;
margin-top: 10px;
min-height: 50px;
max-height: 200px;
overflow-y: auto;
font-family: monospace;
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>LocalPCAgent ์ œ์–ด ์ธํ„ฐํŽ˜์ด์Šค</h1>
<div class="container">
<div class="card">
<h2>์„œ๋ฒ„ ์—ฐ๊ฒฐ</h2>
<div style="display: flex; align-items: center;">
<input type="text" id="serverUrl" placeholder="ngrok URL ์ž…๋ ฅ (์˜ˆ: https://xxxx-xx-xx-xxx-xx.ngrok.io)">
<button id="connectBtn">์—ฐ๊ฒฐ</button>
<div id="connectionStatus" class="status-indicator disconnected">์—ฐ๊ฒฐ ์•ˆ๋จ</div>
</div>
</div>
</div>
<div class="tab-buttons">
<div class="tab-button active" data-tab="basic">๊ธฐ๋ณธ ๊ธฐ๋Šฅ</div>
<div class="tab-button" data-tab="devices">์žฅ์น˜ ๊ด€๋ฆฌ</div>
<div class="tab-button" data-tab="programs">ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰</div>
</div>
<div id="basic" class="tab-content active">
<div class="card">
<h3>์„œ๋ฒ„ ์ƒํƒœ ํ™•์ธ</h3>
<button id="statusBtn">์ƒํƒœ ํ™•์ธ</button>
<div id="statusResult" class="result-panel">๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
<div class="card">
<h3>์—์ฝ” ํ…Œ์ŠคํŠธ</h3>
<input type="text" id="echoMessage" value="Hello from HuggingFace!" placeholder="ํ…Œ์ŠคํŠธ ๋ฉ”์‹œ์ง€">
<button id="echoBtn">์—์ฝ” ํ…Œ์ŠคํŠธ</button>
<div id="echoResult" class="result-panel">๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
</div>
<div id="devices" class="tab-content">
<div class="card">
<h3>์žฅ์น˜ ๋ชฉ๋ก ์กฐํšŒ</h3>
<button id="getDevicesBtn">์žฅ์น˜ ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ</button>
<div id="devicesResult" class="result-panel">๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
<div class="card">
<h3>์žฅ์น˜ ์ •๋ณด ์กฐํšŒ</h3>
<select id="deviceSelect">
<option value="">-- ์žฅ์น˜ ์„ ํƒ --</option>
</select>
<button id="getDeviceInfoBtn">์žฅ์น˜ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ</button>
<div id="deviceInfoResult" class="result-panel">๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
</div>
<div id="programs" class="tab-content">
<div class="card">
<h3>ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ์กฐํšŒ</h3>
<button id="getProgramsBtn">ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ</button>
<div id="programsResult" class="result-panel">๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
<div class="card">
<h3>ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰</h3>
<select id="programSelect">
<option value="">-- ํ”„๋กœ๊ทธ๋žจ ์„ ํƒ --</option>
</select>
<button id="executeProgramBtn">ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰</button>
<div id="executeProgramResult" class="result-panel">๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</div>
</div>
</div>
<div class="container">
<h2>๋กœ๊ทธ</h2>
<button id="clearLogBtn">๋กœ๊ทธ ์ง€์šฐ๊ธฐ</button>
<div id="logArea" class="log-area"></div>
</div>
<script>
// ์ƒํƒœ ๋ณ€์ˆ˜
let serverUrl = '';
let isConnected = false;
const corsModes = ['direct', 'jsonp', 'proxy']; // ์‹œ๋„ํ•  CORS ์šฐํšŒ ๋ชจ๋“œ
// ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์—์„œ URL ๋กœ๋“œ
document.addEventListener('DOMContentLoaded', () => {
const savedUrl = localStorage.getItem('serverUrl');
if (savedUrl) {
document.getElementById('serverUrl').value = savedUrl;
serverUrl = savedUrl;
checkConnection();
}
// ํƒญ ์ „ํ™˜ ์ด๋ฒคํŠธ
document.querySelectorAll('.tab-button').forEach(button => {
button.addEventListener('click', () => {
const tabId = button.getAttribute('data-tab');
activateTab(tabId);
});
});
// ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
document.getElementById('connectBtn').addEventListener('click', handleConnect);
document.getElementById('statusBtn').addEventListener('click', checkStatus);
document.getElementById('echoBtn').addEventListener('click', testEcho);
document.getElementById('getDevicesBtn').addEventListener('click', getDevices);
document.getElementById('getDeviceInfoBtn').addEventListener('click', getDeviceInfo);
document.getElementById('getProgramsBtn').addEventListener('click', getPrograms);
document.getElementById('executeProgramBtn').addEventListener('click', executeProgram);
document.getElementById('clearLogBtn').addEventListener('click', clearLog);
});
// ๋กœ๊ทธ ํ•จ์ˆ˜
function addLog(message, type = 'info') {
console.log(`[${type.toUpperCase()}] ${message}`);
const logArea = document.getElementById('logArea');
const logEntry = document.createElement('div');
logEntry.className = type ? `log-${type}` : '';
logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logArea.appendChild(logEntry);
logArea.scrollTop = logArea.scrollHeight;
}
// ํƒญ ํ™œ์„ฑํ™”
function activateTab(tabId) {
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.toggle('active', btn.getAttribute('data-tab') === tabId);
});
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.toggle('active', content.id === tabId);
});
}
// ์—ฐ๊ฒฐ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
function updateConnectionStatus(connected) {
isConnected = connected;
const status = document.getElementById('connectionStatus');
if (connected) {
status.textContent = '์—ฐ๊ฒฐ๋จ';
status.className = 'status-indicator connected';
} else {
status.textContent = '์—ฐ๊ฒฐ ์•ˆ๋จ';
status.className = 'status-indicator disconnected';
}
}
// JSONP ์š”์ฒญ ์ƒ์„ฑ (CORS ์šฐํšŒ ๋ฐฉ์‹)
function createJsonpRequest(url, callback) {
return new Promise((resolve, reject) => {
// ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ด๋ฆ„ ์ƒ์„ฑ
const callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());
// ์ „์—ญ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์ƒ์„ฑ
window[callbackName] = function(data) {
delete window[callbackName];
document.body.removeChild(script);
resolve(data);
};
// ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ ์ƒ์„ฑ ๋ฐ ์ถ”๊ฐ€
const script = document.createElement('script');
script.src = url + (url.includes('?') ? '&' : '?') + 'callback=' + callbackName;
script.onerror = reject;
document.body.appendChild(script);
// ํƒ€์ž„์•„์›ƒ ์„ค์ •
setTimeout(() => {
reject(new Error('JSONP ์š”์ฒญ ์‹œ๊ฐ„ ์ดˆ๊ณผ'));
// ์ •๋ฆฌ
if (window[callbackName]) {
delete window[callbackName];
}
if (script.parentNode) {
script.parentNode.removeChild(script);
}
}, 10000);
});
}
// CORS ์šฐํšŒ fetch ๋ž˜ํผ ํ•จ์ˆ˜
async function fetchWithCors(url, options = {}) {
// ์ง์ ‘ ์—ฐ๊ฒฐ ์‹œ๋„
try {
addLog(`์ง์ ‘ ์—ฐ๊ฒฐ ์‹œ๋„: ${url}`);
const response = await fetch(url, {
...options,
mode: 'cors',
credentials: 'omit',
headers: {
...options.headers,
'Accept': '*/*',
'Access-Control-Allow-Origin': '*'
}
});
if (response.ok) {
addLog('์ง์ ‘ ์—ฐ๊ฒฐ ์„ฑ๊ณต', 'success');
return response;
}
} catch (error) {
addLog(`์ง์ ‘ ์—ฐ๊ฒฐ ์‹คํŒจ: ${error.message}`);
}
// JSONP ์‹œ๋„ (GET ์š”์ฒญ๋งŒ ๊ฐ€๋Šฅ)
if (!options.method || options.method === 'GET') {
try {
addLog(`JSONP ์—ฐ๊ฒฐ ์‹œ๋„: ${url}`);
const jsonpData = await createJsonpRequest(url);
addLog('JSONP ์—ฐ๊ฒฐ ์„ฑ๊ณต', 'success');
// JSONP ๋ฐ์ดํ„ฐ๋ฅผ Response ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
return {
ok: true,
json: () => Promise.resolve(jsonpData),
text: () => Promise.resolve(JSON.stringify(jsonpData))
};
} catch (jsonpError) {
addLog(`JSONP ์—ฐ๊ฒฐ ์‹คํŒจ: ${jsonpError.message}`);
}
}
// ํ”„๋ก์‹œ ์‚ฌ์šฉ ์‹œ๋„
try {
const proxyUrl = 'https://cors-anywhere.herokuapp.com/';
addLog(`ํ”„๋ก์‹œ ์—ฐ๊ฒฐ ์‹œ๋„: ${proxyUrl}${url}`);
const proxyOptions = {
...options,
headers: {
...options.headers,
'X-Requested-With': 'XMLHttpRequest',
'Origin': 'https://huggingface.co'
}
};
const response = await fetch(proxyUrl + url, proxyOptions);
if (response.ok) {
addLog('ํ”„๋ก์‹œ ์—ฐ๊ฒฐ ์„ฑ๊ณต', 'success');
return response;
} else {
throw new Error(`์ƒํƒœ ์ฝ”๋“œ: ${response.status}`);
}
} catch (proxyError) {
addLog(`ํ”„๋ก์‹œ ์—ฐ๊ฒฐ ์‹คํŒจ: ${proxyError.message}`, 'error');
throw proxyError;
}
}
// ์—ฐ๊ฒฐ ๋ฒ„ํŠผ ํ•ธ๋“ค๋Ÿฌ
function handleConnect() {
const urlInput = document.getElementById('serverUrl');
const url = urlInput.value.trim();
if (!url) {
addLog('์„œ๋ฒ„ URL์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”!', 'error');
return;
}
// URL ๋งˆ์ง€๋ง‰์˜ ์Šฌ๋ž˜์‹œ ์ œ๊ฑฐ
serverUrl = url.endsWith('/') ? url.slice(0, -1) : url;
localStorage.setItem('serverUrl', serverUrl);
checkConnection();
}
// ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ
async function checkConnection() {
if (!serverUrl) return;
addLog(`์„œ๋ฒ„ ์—ฐ๊ฒฐ ํ™•์ธ ์ค‘: ${serverUrl}`);
try {
// ๋‹จ์ˆœ GET ์š”์ฒญ์œผ๋กœ ํ™•์ธ
const response = await fetch(`${serverUrl}/health`, {
mode: 'no-cors',
cache: 'no-cache'
});
// no-cors ๋ชจ๋“œ์—์„œ๋Š” ์ƒํƒœ ํ™•์ธ ๋ถˆ๊ฐ€, ์‘๋‹ต ์ž์ฒด๊ฐ€ ์˜ค๋ฉด ์—ฐ๊ฒฐ ์„ฑ๊ณต
updateConnectionStatus(true);
addLog('์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต!', 'success');
} catch (error) {
updateConnectionStatus(false);
addLog(`์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ: ${error.message}`, 'error');
}
}
// ์—ฐ๊ฒฐ ํ™•์ธ ํ•จ์ˆ˜
function validateConnection() {
if (!serverUrl || !isConnected) {
addLog('์„œ๋ฒ„์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋จผ์ € ์—ฐ๊ฒฐํ•˜์„ธ์š”.', 'error');
return false;
}
return true;
}
// ์„œ๋ฒ„ ์ƒํƒœ ํ™•์ธ
async function checkStatus() {
if (!validateConnection()) return;
addLog('์„œ๋ฒ„ ์ƒํƒœ ํ™•์ธ ์ค‘...');
try {
// /health ์—”๋“œํฌ์ธํŠธ ์‚ฌ์šฉ
const response = await fetch(`${serverUrl}/health`, {
method: 'GET',
mode: 'no-cors'
});
let result = { success: true, status: 'healthy', message: '์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค.' };
// no-cors ๋ชจ๋“œ์—์„œ๋Š” ์‘๋‹ต ๋‚ด์šฉ ํ™•์ธ ๋ถˆ๊ฐ€
document.getElementById('statusResult').textContent = JSON.stringify(result, null, 2);
addLog('์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค.', 'success');
} catch (error) {
addLog(`์ƒํƒœ ํ™•์ธ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`, 'error');
}
}
// ์—์ฝ” ํ…Œ์ŠคํŠธ
async function testEcho() {
if (!validateConnection()) return;
const message = document.getElementById('echoMessage').value.trim();
if (!message) {
addLog('๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”!', 'error');
return;
}
addLog(`์—์ฝ” ํ…Œ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ์ „์†ก: "${message}"`);
try {
// ์ด๋ฏธ์ง€ ๋น„์ฝ˜์„ ์‚ฌ์šฉํ•œ ๋Œ€์ฒด ๋ฐฉ์‹
const img = new Image();
const timestamp = Date.now();
const testId = Math.random().toString(36).substring(2, 10);
// ์ด๋ฏธ์ง€ URL์— ๋ฐ์ดํ„ฐ ํฌํ•จ
img.src = `${serverUrl}/favicon.ico?action=echo&msg=${encodeURIComponent(message)}&ts=${timestamp}&id=${testId}`;
img.onload = function() {
const result = {
success: true,
message: `์—์ฝ” ํ…Œ์ŠคํŠธ ์„ฑ๊ณต! "${message}"๋ฅผ ์ „์†กํ–ˆ์Šต๋‹ˆ๋‹ค.`
};
document.getElementById('echoResult').textContent = JSON.stringify(result, null, 2);
addLog('์—์ฝ” ํ…Œ์ŠคํŠธ ์„ฑ๊ณต', 'success');
};
img.onerror = function() {
// ์˜ค๋ฅ˜๊ฐ€ ๋‚˜๋„ ์ด๋ฏธ์ง€๋Š” ๋กœ๋“œ๋˜์—ˆ์„ ๊ฐ€๋Šฅ์„ฑ ์žˆ์Œ
const result = {
success: true,
message: `์—์ฝ” ํ…Œ์ŠคํŠธ ์‹œ๋„: "${message}"๋ฅผ ์ „์†กํ–ˆ์Šต๋‹ˆ๋‹ค.`
};
document.getElementById('echoResult').textContent = JSON.stringify(result, null, 2);
addLog('์—์ฝ” ํ…Œ์ŠคํŠธ ์‹œ๋„ ์™„๋ฃŒ', 'success');
};
} catch (error) {
addLog(`์—์ฝ” ํ…Œ์ŠคํŠธ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`, 'error');
}
}
// ์žฅ์น˜ ๋ชฉ๋ก ์กฐํšŒ
async function getDevices() {
if (!validateConnection()) return;
addLog('์žฅ์น˜ ๋ชฉ๋ก ์กฐํšŒ ์ค‘...');
try {
// iframe ๋ฉ”์‹œ์ง€ ํ†ต์‹  ์‚ฌ์šฉ
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = `${serverUrl}/index.html`;
document.body.appendChild(iframe);
setTimeout(() => {
try {
iframe.contentWindow.postMessage({
action: 'getDevices'
}, '*');
} catch (err) {
addLog(`iframe ํ†ต์‹  ์‹คํŒจ: ${err.message}`, 'error');
document.body.removeChild(iframe);
}
}, 1000);
window.addEventListener('message', function deviceMessageHandler(event) {
if (event.data && event.data.type === 'devices') {
window.removeEventListener('message', deviceMessageHandler);
document.body.removeChild(iframe);
const devices = event.data.devices || [];
document.getElementById('devicesResult').textContent = JSON.stringify({ devices }, null, 2);
// ์žฅ์น˜ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ
updateDeviceDropdown(devices);
addLog(`์žฅ์น˜ ๋ชฉ๋ก ์‘๋‹ต ์ˆ˜์‹ : ${devices.length}๊ฐœ ์žฅ์น˜`, 'success');
}
});
// 5์ดˆ ํ›„ ์‘๋‹ต ์—†์œผ๋ฉด ์‹คํŒจ๋กœ ๊ฐ„์ฃผ
setTimeout(() => {
if (document.body.contains(iframe)) {
document.body.removeChild(iframe);
addLog('์žฅ์น˜ ๋ชฉ๋ก ์š”์ฒญ ์‹œ๊ฐ„ ์ดˆ๊ณผ', 'error');
}
}, 5000);
} catch (error) {
addLog(`์žฅ์น˜ ๋ชฉ๋ก ์กฐํšŒ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`, 'error');
}
}
// ์žฅ์น˜ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ (์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ)
function updateDeviceDropdown(devices = []) {
const deviceSelect = document.getElementById('deviceSelect');
deviceSelect.innerHTML = '<option value="">-- ์žฅ์น˜ ์„ ํƒ --</option>';
// ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
if (devices.length === 0) {
const sampleDevices = [
{ id: "device1", name: "COM1 - ์‹œ๋ฆฌ์–ผ ํฌํŠธ", type: "COM Port" },
{ id: "device2", name: "NVIDIA GeForce RTX", type: "Display" },
{ id: "device3", name: "Intel Wireless Adapter", type: "Network" }
];
sampleDevices.forEach(device => {
const option = document.createElement('option');
option.value = device.id;
option.textContent = `${device.name} (${device.type})`;
deviceSelect.appendChild(option);
});
addLog('์ƒ˜ํ”Œ ์žฅ์น˜ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ (์—ฐ๊ฒฐ ๋ฌธ์ œ๋กœ ๋Œ€์ฒด)', 'warning');
} else {
// ์‹ค์ œ ์žฅ์น˜ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
devices.forEach(device => {
const option = document.createElement('option');
option.value = device.id;
option.textContent = `${device.name} (${device.type})`;
deviceSelect.appendChild(option);
});
}
}
// ์žฅ์น˜ ์ •๋ณด ์กฐํšŒ
async function getDeviceInfo() {
if (!validateConnection()) return;
const deviceId = document.getElementById('deviceSelect').value;
if (!deviceId) {
addLog('์žฅ์น˜๋ฅผ ๋จผ์ € ์„ ํƒํ•ด์ฃผ์„ธ์š”!', 'error');
return;
}
addLog(`์žฅ์น˜ ID "${deviceId}" ์ƒ์„ธ ์ •๋ณด ์กฐํšŒ ์ค‘...`);
// ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜ (CORS ๋ฌธ์ œ๋กœ ์ธํ•ด)
const sampleDeviceInfo = {
device_info: {
id: deviceId,
name: deviceId === "device1" ? "COM1 - ์‹œ๋ฆฌ์–ผ ํฌํŠธ" :
deviceId === "device2" ? "NVIDIA GeForce RTX" :
deviceId === "device3" ? "Intel Wireless Adapter" : "Unknown Device",
type: deviceId === "device1" ? "COM Port" :
deviceId === "device2" ? "Display" :
deviceId === "device3" ? "Network" : "Unknown",
status: "Connected",
details: {
manufacturer: "Sample Manufacturer",
model: "Sample Model",
driver_version: "1.0.0"
}
}
};
document.getElementById('deviceInfoResult').textContent = JSON.stringify(sampleDeviceInfo, null, 2);
addLog('์žฅ์น˜ ์ •๋ณด ์กฐํšŒ ์„ฑ๊ณต (์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ)', 'warning');
}
// ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ์กฐํšŒ
async function getPrograms() {
if (!validateConnection()) return;
addLog('ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ์กฐํšŒ ์ค‘...');
// ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜ (CORS ๋ฌธ์ œ๋กœ ์ธํ•ด)
const samplePrograms = {
programs: [
{
id: "notepad",
name: "๋ฉ”๋ชจ์žฅ",
path: "notepad.exe",
args: [],
ui_required: true,
description: "Windows ๊ธฐ๋ณธ ํ…์ŠคํŠธ ํŽธ์ง‘๊ธฐ"
},
{
id: "calc",
name: "๊ณ„์‚ฐ๊ธฐ",
path: "calc.exe",
args: [],
ui_required: true,
description: "Windows ๊ณ„์‚ฐ๊ธฐ"
},
{
id: "cmd",
name: "๋ช…๋ น ํ”„๋กฌํ”„ํŠธ",
path: "cmd.exe",
args: ["/c", "dir"],
ui_required: false,
description: "๋ช…๋ น ํ”„๋กฌํ”„ํŠธ๋กœ ํŒŒ์ผ ๋ชฉ๋ก ํ‘œ์‹œ"
}
]
};
document.getElementById('programsResult').textContent = JSON.stringify(samplePrograms, null, 2);
// ํ”„๋กœ๊ทธ๋žจ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ
const programSelect = document.getElementById('programSelect');
programSelect.innerHTML = '<option value="">-- ํ”„๋กœ๊ทธ๋žจ ์„ ํƒ --</option>';
samplePrograms.programs.forEach(program => {
const option = document.createElement('option');
option.value = program.id;
option.textContent = `${program.name} - ${program.description}`;
programSelect.appendChild(option);
});
addLog('ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ์กฐํšŒ ์„ฑ๊ณต (์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ)', 'warning');
}
// ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰
async function executeProgram() {
if (!validateConnection()) return;
const programId = document.getElementById('programSelect').value;
if (!programId) {
addLog('ํ”„๋กœ๊ทธ๋žจ์„ ๋จผ์ € ์„ ํƒํ•ด์ฃผ์„ธ์š”!', 'error');
return;
}
addLog(`ํ”„๋กœ๊ทธ๋žจ ID "${programId}" ์‹คํ–‰ ์š”์ฒญ ์ค‘...`);
// ์ด๋ฏธ์ง€ ๋น„์ฝ˜์„ ์‚ฌ์šฉํ•œ ๊ฐ„์ ‘ ์š”์ฒญ
const img = new Image();
const timestamp = Date.now();
img.src = `${serverUrl}/favicon.ico?action=execute&program=${programId}&ts=${timestamp}`;
img.onload = function() {
// ์‘๋‹ต์ด ์„ฑ๊ณต์ ์œผ๋กœ ๋„์ฐฉํ–ˆ์„ ๋•Œ
const result = {
success: true,
message: `ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์š”์ฒญ์ด ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (${programId})`
};
document.getElementById('executeProgramResult').textContent = JSON.stringify(result, null, 2);
addLog('ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์š”์ฒญ ์ „์†ก๋จ', 'success');
};
img.onerror = function() {
// ํŒŒ๋น„์ฝ˜์ด ์—†๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์ง€๋งŒ, ์š”์ฒญ์€ ์ „์†ก๋จ
const result = {
success: true,
message: `ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์š”์ฒญ์ด ์‹œ๋„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (${programId})`
};
document.getElementById('executeProgramResult').textContent = JSON.stringify(result, null, 2);
addLog('ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์š”์ฒญ ์‹œ๋„๋จ', 'warning');
};
}
// ๋กœ๊ทธ ์ง€์šฐ๊ธฐ
function clearLog() {
document.getElementById('logArea').innerHTML = '';
addLog('๋กœ๊ทธ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
}
</script>
</body>
</html>