Update 2026-01-20 20:47:44
This commit is contained in:
255
backend/snapshots/20260120_consolidation/js/index.js
Normal file
255
backend/snapshots/20260120_consolidation/js/index.js
Normal file
@@ -0,0 +1,255 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 스크립트 선택 시 XML 드롭다운 토글
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const TARGET_SCRIPT = "02-set_config.py";
|
||||
const scriptSelect = document.getElementById('script');
|
||||
const xmlGroup = document.getElementById('xmlFileGroup');
|
||||
|
||||
function toggleXml() {
|
||||
if (!scriptSelect || !xmlGroup) return;
|
||||
if (scriptSelect.value === TARGET_SCRIPT) {
|
||||
xmlGroup.style.display = 'block';
|
||||
xmlGroup.classList.add('fade-in');
|
||||
} else {
|
||||
xmlGroup.style.display = '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 = `<i class="bi bi-file-text me-2"></i>${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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 진행바 업데이트
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
window.updateProgress = function (val) {
|
||||
const bar = document.getElementById('progressBar');
|
||||
if (!bar) return;
|
||||
const v = Math.max(0, Math.min(100, Number(val) || 0));
|
||||
bar.style.width = v + '%';
|
||||
bar.setAttribute('aria-valuenow', v);
|
||||
bar.innerHTML = `<span class="fw-semibold">${v}%</span>`;
|
||||
};
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// CSRF 토큰
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const csrfToken = document.querySelector('input[name="csrf_token"]')?.value || '';
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// IP 입력 데이터 보존 (Local Storage)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const ipTextarea = document.getElementById('ips');
|
||||
const ipForm = document.getElementById('ipForm');
|
||||
const STORAGE_KEY_IP = 'ip_input_draft';
|
||||
|
||||
if (ipTextarea) {
|
||||
// 1. 페이지 로드 시 저장된 값 복원
|
||||
const savedIps = localStorage.getItem(STORAGE_KEY_IP);
|
||||
if (savedIps) {
|
||||
ipTextarea.value = savedIps;
|
||||
// 라인 수 업데이트 트리거
|
||||
if (window.updateIpCount) window.updateIpCount();
|
||||
}
|
||||
|
||||
// 2. 입력 시마다 저장
|
||||
ipTextarea.addEventListener('input', () => {
|
||||
localStorage.setItem(STORAGE_KEY_IP, ipTextarea.value);
|
||||
// script.js에 있는 updateIpCount 호출 (있다면)
|
||||
if (window.updateIpCount) window.updateIpCount();
|
||||
});
|
||||
|
||||
// 3. 폼 제출 성공 시 초기화?
|
||||
// 사용자의 의도에 따라 다름: "변경이 되지 않는 이상 계속 가지고 있게"
|
||||
// -> 제출 후에도 유지하는 것이 요청 사항에 부합함.
|
||||
// 만약 '성공적으로 작업이 끝나면 지워달라'는 요청이 있으면 여기를 수정.
|
||||
// 현재 요청: "페이지가 리셋이되도 변경이 되지 않는이상 계속 가지고있게" -> 유지.
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 공통 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 };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// 알림 자동 닫기
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.alert').forEach(alert => {
|
||||
const bsAlert = new bootstrap.Alert(alert);
|
||||
bsAlert.close();
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// IP 스캔 로직 (Modal)
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const btnScan = document.getElementById('btnStartScan');
|
||||
if (btnScan) {
|
||||
btnScan.addEventListener('click', async () => {
|
||||
const startIp = '10.10.0.2';
|
||||
const endIp = '10.10.0.255';
|
||||
const ipsTextarea = document.getElementById('ips');
|
||||
const progressBar = document.getElementById('progressBar');
|
||||
|
||||
// UI 상태 변경 (로딩 중)
|
||||
const originalIcon = btnScan.innerHTML;
|
||||
btnScan.disabled = true;
|
||||
btnScan.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>';
|
||||
|
||||
// 메인 진행바 활용
|
||||
if (progressBar) {
|
||||
const progressContainer = progressBar.closest('.progress');
|
||||
if (progressContainer) {
|
||||
progressContainer.parentElement.classList.remove('d-none');
|
||||
}
|
||||
progressBar.style.width = '100%';
|
||||
progressBar.classList.add('progress-bar-striped', 'progress-bar-animated');
|
||||
progressBar.textContent = 'IP 스캔 중...';
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/utils/scan_network', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken
|
||||
},
|
||||
body: JSON.stringify({ start_ip: startIp, end_ip: endIp })
|
||||
});
|
||||
|
||||
// 1. 세션 만료로 인한 리다이렉트 감지
|
||||
if (res.redirected) {
|
||||
alert('세션이 만료되었습니다. 다시 로그인해주세요.');
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. JSON 응답인지 확인
|
||||
const contentType = res.headers.get("content-type");
|
||||
if (!contentType || !contentType.includes("application/json")) {
|
||||
const text = await res.text();
|
||||
if (text.includes("CSRF")) {
|
||||
throw new Error("보안 토큰(CSRF)이 만료되었습니다. 페이지를 새로고침해주세요.");
|
||||
}
|
||||
throw new Error(`서버 응답 오류 (HTTP ${res.status}): ${text.substring(0, 100)}...`);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (data.success) {
|
||||
if (data.active_ips && data.active_ips.length > 0) {
|
||||
ipsTextarea.value = data.active_ips.join('\n');
|
||||
// 이벤트 트리거
|
||||
ipsTextarea.dispatchEvent(new Event('input'));
|
||||
|
||||
alert(`스캔 완료: ${data.active_ips.length}개의 활성 IP를 찾았습니다.`);
|
||||
} else {
|
||||
alert('활성 IP를 발견하지 못했습니다.');
|
||||
}
|
||||
} else {
|
||||
throw new Error(data.error || 'Unknown error');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('오류가 발생했습니다: ' + (err.message || err));
|
||||
} finally {
|
||||
// 상태 복구
|
||||
btnScan.disabled = false;
|
||||
btnScan.innerHTML = originalIcon;
|
||||
|
||||
if (progressBar) {
|
||||
// 진행바 초기화
|
||||
progressBar.style.width = '0%';
|
||||
progressBar.textContent = '0%';
|
||||
progressBar.classList.remove('progress-bar-striped', 'progress-bar-animated');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// IP 입력 지우기 버튼
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
const btnClear = document.getElementById('btnClearIps');
|
||||
if (btnClear) {
|
||||
btnClear.addEventListener('click', () => {
|
||||
const ipsTextarea = document.getElementById('ips');
|
||||
if (ipsTextarea) {
|
||||
ipsTextarea.value = '';
|
||||
ipsTextarea.dispatchEvent(new Event('input')); // 로컬 스토리지 업데이트 및 카운트 갱신 트리거
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user