// script.js - 정리된 버전 document.addEventListener('DOMContentLoaded', () => { // ───────────────────────────────────────────────────────────── // CSRF 토큰 // ───────────────────────────────────────────────────────────── const csrfToken = document.querySelector('input[name="csrf_token"]')?.value || ''; // ───────────────────────────────────────────────────────────── // 진행바 업데이트 // ───────────────────────────────────────────────────────────── window.updateProgress = function(percent) { const bar = document.getElementById('progressBar'); if (!bar) return; const v = Math.max(0, Math.min(100, Number(percent) || 0)); bar.style.width = v + '%'; bar.setAttribute('aria-valuenow', v); bar.innerHTML = `${v}%`; }; // ───────────────────────────────────────────────────────────── // 줄 수 카운터 // ───────────────────────────────────────────────────────────── function updateLineCount(textareaId, badgeId) { const textarea = document.getElementById(textareaId); const badge = document.getElementById(badgeId); if (!textarea || !badge) return; const updateCount = () => { const text = textarea.value.trim(); if (text === '') { badge.textContent = '0줄'; return; } const lines = text.split('\n').filter(line => line.trim().length > 0); badge.textContent = `${lines.length}줄`; }; updateCount(); textarea.addEventListener('input', updateCount); textarea.addEventListener('change', updateCount); textarea.addEventListener('keyup', updateCount); textarea.addEventListener('paste', () => setTimeout(updateCount, 10)); } updateLineCount('ips', 'ipLineCount'); updateLineCount('server_list_content', 'serverLineCount'); // ───────────────────────────────────────────────────────────── // 스크립트 선택 시 XML 드롭다운 토글 // ───────────────────────────────────────────────────────────── const TARGET_SCRIPT = "02-set_config.py"; const scriptSelect = document.getElementById('script'); const xmlGroup = document.getElementById('xmlFileGroup'); function toggleXml() { if (!scriptSelect || !xmlGroup) return; xmlGroup.style.display = (scriptSelect.value === TARGET_SCRIPT) ? 'block' : 'none'; } if (scriptSelect) { toggleXml(); scriptSelect.addEventListener('change', toggleXml); } // ───────────────────────────────────────────────────────────── // 파일 보기 모달 // ───────────────────────────────────────────────────────────── const modalEl = document.getElementById('fileViewModal'); const titleEl = document.getElementById('fileViewModalLabel'); const contentEl = document.getElementById('fileViewContent'); if (modalEl) { modalEl.addEventListener('show.bs.modal', async (ev) => { const btn = ev.relatedTarget; const folder = btn?.getAttribute('data-folder') || ''; const date = btn?.getAttribute('data-date') || ''; const filename = btn?.getAttribute('data-filename') || ''; titleEl.innerHTML = `${filename || '파일'}`; contentEl.textContent = '불러오는 중...'; const params = new URLSearchParams(); if (folder) params.set('folder', folder); if (date) params.set('date', date); if (filename) params.set('filename', filename); try { const res = await fetch(`/view_file?${params.toString()}`, { cache: 'no-store' }); if (!res.ok) throw new Error('HTTP ' + res.status); const data = await res.json(); contentEl.textContent = data?.content ?? '(빈 파일)'; } catch (e) { contentEl.textContent = '파일을 불러오지 못했습니다: ' + (e?.message || e); } }); } // ───────────────────────────────────────────────────────────── // 공통 POST 함수 // ───────────────────────────────────────────────────────────── async function postFormAndHandle(url) { const res = await fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'X-CSRFToken': csrfToken, 'Accept': 'application/json, text/html;q=0.9,*/*;q=0.8', }, }); const ct = (res.headers.get('content-type') || '').toLowerCase(); if (ct.includes('application/json')) { const data = await res.json(); if (data.success === false) { throw new Error(data.error || ('HTTP ' + res.status)); } return data; } return { success: true, html: true }; } // ───────────────────────────────────────────────────────────── // MAC 파일 이동 // ───────────────────────────────────────────────────────────── const macForm = document.getElementById('macMoveForm'); if (macForm) { macForm.addEventListener('submit', async (e) => { e.preventDefault(); const btn = macForm.querySelector('button'); const originalHtml = btn.innerHTML; btn.disabled = true; btn.innerHTML = '처리 중...'; try { await postFormAndHandle(macForm.action); location.reload(); } catch (err) { alert('MAC 이동 중 오류: ' + (err?.message || err)); btn.disabled = false; btn.innerHTML = originalHtml; } }); } // ───────────────────────────────────────────────────────────── // GUID 파일 이동 // ───────────────────────────────────────────────────────────── const guidForm = document.getElementById('guidMoveForm'); if (guidForm) { guidForm.addEventListener('submit', async (e) => { e.preventDefault(); const btn = guidForm.querySelector('button'); const originalHtml = btn.innerHTML; btn.disabled = true; btn.innerHTML = '처리 중...'; try { await postFormAndHandle(guidForm.action); location.reload(); } catch (err) { alert('GUID 이동 중 오류: ' + (err?.message || err)); btn.disabled = false; btn.innerHTML = originalHtml; } }); } // ───────────────────────────────────────────────────────────── // IP 폼 제출 및 진행률 폴링 // ───────────────────────────────────────────────────────────── const ipForm = document.getElementById("ipForm"); if (ipForm) { ipForm.addEventListener("submit", async (ev) => { ev.preventDefault(); const formData = new FormData(ipForm); const btn = ipForm.querySelector('button[type="submit"]'); const originalHtml = btn.innerHTML; btn.disabled = true; btn.innerHTML = '처리 중...'; try { const res = await fetch(ipForm.action, { method: "POST", body: formData }); if (!res.ok) throw new Error("HTTP " + res.status); const data = await res.json(); console.log("[DEBUG] process_ips 응답:", data); if (data.job_id) { pollProgress(data.job_id); } else { window.updateProgress(100); setTimeout(() => location.reload(), 1000); } } catch (err) { console.error("처리 중 오류:", err); alert("처리 중 오류 발생: " + err.message); btn.disabled = false; btn.innerHTML = originalHtml; } }); } // ───────────────────────────────────────────────────────────── // 진행률 폴링 함수 // ───────────────────────────────────────────────────────────── function pollProgress(jobId) { const interval = setInterval(async () => { try { const res = await fetch(`/progress_status/${jobId}`); if (!res.ok) { clearInterval(interval); return; } const data = await res.json(); if (data.progress !== undefined) { window.updateProgress(data.progress); } if (data.progress >= 100) { clearInterval(interval); window.updateProgress(100); setTimeout(() => location.reload(), 1500); } } catch (err) { console.error('진행률 확인 중 오류:', err); clearInterval(interval); } }, 500); } // ───────────────────────────────────────────────────────────── // 알림 자동 닫기 (5초 후) // ───────────────────────────────────────────────────────────── setTimeout(() => { document.querySelectorAll('.alert').forEach(alert => { const bsAlert = new bootstrap.Alert(alert); bsAlert.close(); }); }, 5000); });