/** * Dell iDRAC 멀티 서버 관리 JavaScript * backend/static/js/idrac_main.js */ // SocketIO 연결 (혹시 모를 추후 사용을 위해 유지하나, 현재는 직접적 이벤트 없음) const socket = io(); // 전역 변수 let servers = []; let selectedServers = new Set(); // ======================================== // 유틸리티 (CSRF, Fetch, Toast) // ======================================== function getCSRFToken() { const metaTag = document.querySelector('meta[name="csrf-token"]'); if (metaTag) return metaTag.getAttribute('content'); const cookies = document.cookie.split(';'); for (let cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'csrf_token') return value; } return null; } async function fetchWithCSRF(url, options = {}) { const csrfToken = getCSRFToken(); if (!options.headers) options.headers = {}; if (csrfToken) { options.headers['X-CSRFToken'] = csrfToken; options.headers['X-CSRF-Token'] = csrfToken; } if (options.body) { if (options.body instanceof FormData) { // 자동 처리 } else if (typeof options.body === 'string') { if (!options.headers['Content-Type']) { options.headers['Content-Type'] = 'application/json'; } } else if (typeof options.body === 'object') { options.headers['Content-Type'] = 'application/json'; options.body = JSON.stringify(options.body); } } return fetch(url, options); } function showMessage(message, type = 'info') { showToast(message, type); console.log(`[${type}] ${message}`); } function showToast(message, type = 'info') { const existingToast = document.querySelector('.toast'); if (existingToast) { existingToast.remove(); } const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => toast.classList.add('show'), 100); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, 3000); } // ======================================== // 초기화 // ======================================== document.addEventListener('DOMContentLoaded', function () { console.log('iDRAC 멀티 서버 관리 시스템 시작'); refreshServers(); loadGroups(); }); // ======================================== // 서버 관리 (목록, 렌더링) // ======================================== async function refreshServers() { try { const groupEl = document.getElementById('group-filter'); const group = groupEl ? groupEl.value : 'all'; const url = `/idrac/api/servers${group !== 'all' ? '?group=' + group : ''}`; const response = await fetchWithCSRF(url); const data = await response.json(); if (data.success) { servers = data.servers; renderServerList(); updateServerCount(); } else { showMessage(data.message, 'error'); } } catch (error) { showMessage('서버 목록 로드 실패: ' + error, 'error'); } } function renderServerList() { const tbody = document.getElementById('server-list'); if (!tbody) return; if (servers.length === 0) { tbody.innerHTML = '등록된 서버가 없습니다'; return; } tbody.innerHTML = servers.map(server => ` ${server.name} ${server.ip_address} ${server.group_name || '-'} ${getStatusText(server.status)} ${server.model || '-'} ${server.last_connected ? formatDateTime(server.last_connected) : '-'} `).join(''); } function getStatusText(status) { const map = { registered: '등록됨', online: '온라인', offline: '오프라인', updating: '업데이트중' }; return map[status] || status; } function formatDateTime(dateStr) { if (!dateStr) return '-'; const date = new Date(dateStr); return date.toLocaleString('ko-KR', { year: '2-digit', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); } function updateServerCount() { const el = document.getElementById('server-count'); if (el) el.textContent = `${servers.length}대`; } // ======================================== // 서버 선택 관리 // ======================================== function toggleSelectAll() { const checkbox = document.getElementById('select-all'); const checkboxes = document.querySelectorAll('.server-checkbox'); selectedServers.clear(); checkboxes.forEach(cb => { cb.checked = checkbox.checked; if (checkbox.checked) selectedServers.add(parseInt(cb.value)); }); updateSelectedCount(); } function toggleServerSelection(serverId) { if (selectedServers.has(serverId)) selectedServers.delete(serverId); else selectedServers.add(serverId); updateSelectedCount(); const all = document.querySelectorAll('.server-checkbox'); const checked = document.querySelectorAll('.server-checkbox:checked'); const selectAll = document.getElementById('select-all'); if (selectAll) selectAll.checked = all.length > 0 && all.length === checked.length; } function updateSelectedCount() { const el = document.getElementById('selected-count'); if (el) el.textContent = `선택: ${selectedServers.size}대`; } function getSelectedServerIds() { return Array.from(selectedServers); } // ======================================== // 그룹 관리 // ======================================== async function loadGroups() { try { const response = await fetchWithCSRF('/idrac/api/groups'); const data = await response.json(); if (data.success) { const select = document.getElementById('group-filter'); if (!select) return; const currentValue = select.value; select.innerHTML = ''; data.groups.forEach(g => select.innerHTML += ``); if (currentValue) select.value = currentValue; } } catch (e) { console.error('그룹 로드 실패:', e); } } function filterByGroup() { refreshServers(); } // ======================================== // 서버 CRUD // ======================================== function showAddServerModal() { document.getElementById('add-server-modal').style.display = 'flex'; } function closeAddServerModal() { const modal = document.getElementById('add-server-modal'); if (modal) modal.style.display = 'none'; setTimeout(() => clearServerForm(), 0); } function clearServerForm() { const fieldIds = [ 'server-name', 'server-ip', 'server-username', 'server-password', 'server-group', 'server-model' ]; fieldIds.forEach(id => { const el = document.getElementById(id); if (el) { if (el.type === 'checkbox') el.checked = false; else if (el.type === 'file') el.value = null; else el.value = ''; } }); } function editServer(serverId) { showMessage('서버 수정 기능은 현재 준비 중입니다.', 'info'); // 추후 구현: 모달에 데이터 로드 후 띄우기 } async function addServer() { const serverData = { name: document.getElementById('server-name').value, ip_address: document.getElementById('server-ip').value, username: document.getElementById('server-username').value, password: document.getElementById('server-password').value, group_name: document.getElementById('server-group').value, model: document.getElementById('server-model').value }; if (!serverData.name || !serverData.ip_address || !serverData.password) { showMessage('필수 필드를 입력하세요 (서버명, IP, 비밀번호)', 'error'); return; } try { const response = await fetchWithCSRF('/idrac/api/servers', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(serverData) }); const data = await response.json(); if (data.success) { showMessage(data.message, 'success'); closeAddServerModal(); refreshServers(); loadGroups(); } else showMessage(data.message, 'error'); } catch (e) { showMessage('서버 추가 실패: ' + e, 'error'); } } async function deleteServer(serverId) { if (!confirm('이 서버를 삭제하시겠습니까?')) return; try { const res = await fetchWithCSRF(`/idrac/api/servers/${serverId}`, { method: 'DELETE' }); const data = await res.json(); if (data.success) { showMessage(data.message, 'success'); refreshServers(); } else showMessage(data.message, 'error'); } catch (e) { showMessage('서버 삭제 실패: ' + e, 'error'); } } // ======================================== // 연결 테스트 및 제어 // ======================================== async function testConnection(serverId) { try { const res = await fetchWithCSRF(`/idrac/api/servers/${serverId}/test`, { method: 'POST' }); const data = await res.json(); showMessage(data.message, data.success ? 'success' : 'error'); refreshServers(); } catch (e) { showMessage('연결 테스트 실패: ' + e, 'error'); } } async function testSelectedConnections() { const serverIds = getSelectedServerIds(); if (serverIds.length === 0) { showMessage('서버를 선택하세요', 'warning'); return; } showMessage(`선택된 ${serverIds.length}대 서버 연결 테스트 중...`, 'info'); try { const res = await fetchWithCSRF('/idrac/api/servers/test-multi', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ server_ids: serverIds }) }); const data = await res.json(); if (data.success) { const summary = data.summary; showMessage(`연결 성공: ${summary.success}대 / 실패: ${summary.failed}대`, 'success'); } else { showMessage(data.message, 'error'); } refreshServers(); } catch (error) { showMessage('연결 테스트 실패: ' + error, 'error'); } } async function rebootSelected() { const serverIds = getSelectedServerIds(); if (serverIds.length === 0) { showMessage('서버를 선택하세요', 'warning'); return; } if (!confirm(`선택한 ${serverIds.length}대 서버를 재부팅하시겠습니까?`)) { return; } try { const response = await fetchWithCSRF('/idrac/api/servers/reboot-multi', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ server_ids: serverIds, type: 'GracefulRestart' }) }); const data = await response.json(); if (data.success) { const summary = data.summary; showMessage(`재부팅 시작: 성공 ${summary.success}대, 실패 ${summary.failed}대`, 'success'); } else { showMessage(data.message, 'error'); } refreshServers(); } catch (error) { showMessage('재부팅 실패: ' + error, 'error'); } }