Spaces:
Running
Running
| <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> | |