This commit is contained in:
2025-10-16 15:06:50 +09:00
parent 2fcca115d6
commit 230ea0890d
11 changed files with 1587 additions and 145 deletions

View File

@@ -116,11 +116,10 @@
class="btn btn-success">
GUID to Excel
</button>
<button type="submit" formaction="{{ url_for('utils.update_gpu_list') }}"
<button type="submit" formaction="{{ url_for('utils.update_gpu_list') }}"
class="btn btn-success">
GPU to Excel
</button>
</div>
</form>
</div>
@@ -148,79 +147,78 @@
</div>
</div>
{# 파일 관리 도구 #}
<div class="row mb-4">
<div class="col">
<div class="card border shadow-sm">
<div class="card-header bg-light border-0 py-2">
<h6 class="mb-0">
<i class="bi bi-tools me-2"></i>
파일 관리 도구
</h6>
</div>
{# 파일 관리 도구 #}
<div class="row mb-4">
<div class="col">
<div class="card border shadow-sm">
<div class="card-header bg-light border-0 py-2">
<h6 class="mb-0">
<i class="bi bi-tools me-2"></i>
파일 관리 도구
</h6>
</div>
<div class="card-body p-4 file-tools">
<div class="card-body p-4 file-tools">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xxl-5 g-3 align-items-end">
<!-- 한 줄로 정렬: 화면이 넓을 때 5등분 / 좁아지면 자동 줄바꿈 -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xxl-5 g-3 align-items-end">
<!-- ZIP 다운로드 -->
<div class="col">
<label class="form-label text-nowrap">ZIP 다운로드</label>
<form method="post" action="{{ url_for('main.download_zip') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="input-group">
<input type="text" class="form-control" name="zip_filename" placeholder="파일명" required>
<button class="btn btn-primary" type="submit">다운로드</button>
</div>
</form>
</div>
<!-- 파일 백업 -->
<div class="col">
<label class="form-label text-nowrap">파일 백업</label>
<form method="post" action="{{ url_for('main.backup_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="input-group">
<input type="text" class="form-control" name="backup_prefix" placeholder="PO로 시작">
<button class="btn btn-success" type="submit">백업</button>
</div>
</form>
</div>
<!-- MAC 파일 이동 -->
<div class="col">
<label class="form-label text-nowrap">MAC 파일 이동</label>
<form id="macMoveForm" method="post" action="{{ url_for('utils.move_mac_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-warning w-100" type="submit">MAC Move</button>
</form>
</div>
<!-- GUID 파일 이동 -->
<div class="col">
<label class="form-label text-nowrap">GUID 파일 이동</label>
<form id="guidMoveForm" method="post" action="{{ url_for('utils.move_guid_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-info w-100" type="submit">GUID Move</button>
</form>
</div>
<!-- GPU 파일 이동 -->
<div class="col">
<label class="form-label text-nowrap">GPU 파일 이동</label>
<form id="gpuMoveForm" method="post" action="{{ url_for('utils.move_gpu_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-info w-100" type="submit">GPU Move</button>
</form>
</div>
<!-- ZIP 다운로드 -->
<div class="col">
<label class="form-label text-nowrap">ZIP 다운로드</label>
<form method="post" action="{{ url_for('main.download_zip') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="input-group">
<input type="text" class="form-control" name="zip_filename" placeholder="파일명" required>
<button class="btn btn-primary" type="submit">다운로드</button>
</div>
</form>
</div>
<!-- 파일 백업 -->
<div class="col">
<label class="form-label text-nowrap">파일 백업</label>
<form method="post" action="{{ url_for('main.backup_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="input-group">
<input type="text" class="form-control" name="backup_prefix" placeholder="PO로 시작">
<button class="btn btn-success" type="submit">백업</button>
</div>
</form>
</div>
<!-- MAC 파일 이동 -->
<div class="col">
<label class="form-label text-nowrap">MAC 파일 이동</label>
<form id="macMoveForm" method="post" action="{{ url_for('utils.move_mac_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-warning w-100" type="submit">MAC Move</button>
</form>
</div>
<!-- GUID 파일 이동 -->
<div class="col">
<label class="form-label text-nowrap">GUID 파일 이동</label>
<form id="guidMoveForm" method="post" action="{{ url_for('utils.move_guid_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-info w-100" type="submit">GUID Move</button>
</form>
</div>
<!-- GPU 파일 이동 -->
<div class="col">
<label class="form-label text-nowrap">GPU 파일 이동</label>
<form id="gpuMoveForm" method="post" action="{{ url_for('utils.move_gpu_files') }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-info w-100" type="submit">GPU Move</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{# 처리된 파일 목록 - 목록별 버튼 스타일 분리 (processed-list) #}
{# 처리된 파일 목록 #}
<div class="row mb-4 processed-list">
<div class="col">
<div class="card border shadow-sm">
@@ -272,7 +270,7 @@
</div>
</div>
{# 백업된 파일 목록 - 목록별 버튼 스타일 분리 (backup-list) #}
{# 백업된 파일 목록 #}
<div class="row backup-list">
<div class="col">
<div class="card border shadow-sm">
@@ -305,8 +303,8 @@
{% for file in info.files %}
<div class="col-auto">
<div class="file-card-compact border rounded p-2 text-center">
<a href="{{ url_for('main.download_backup_file', date=date, filename=file) }}
" class="text-decoration-none text-dark fw-semibold d-block mb-2 text-nowrap px-2"
<a href="{{ url_for('main.download_backup_file', date=date, filename=file) }}"
class="text-decoration-none text-dark fw-semibold d-block mb-2 text-nowrap px-2"
download title="{{ file }}">
{{ file.rsplit('.', 1)[0] }}
</a>
@@ -473,7 +471,10 @@
<script>
document.addEventListener('DOMContentLoaded', () => {
// ─────────────────────────────────────────────────────────────
// 스크립트 선택 시 XML 드롭다운 토글
// ─────────────────────────────────────────────────────────────
const TARGET_SCRIPT = "02-set_config.py";
const scriptSelect = document.getElementById('script');
const xmlGroup = document.getElementById('xmlFileGroup');
@@ -493,7 +494,10 @@ document.addEventListener('DOMContentLoaded', () => {
scriptSelect.addEventListener('change', toggleXml);
}
// ─────────────────────────────────────────────────────────────
// 파일 보기 모달
// ─────────────────────────────────────────────────────────────
const modalEl = document.getElementById('fileViewModal');
const titleEl = document.getElementById('fileViewModalLabel');
const contentEl = document.getElementById('fileViewContent');
@@ -525,7 +529,10 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
// ─────────────────────────────────────────────────────────────
// 진행바 업데이트
// ─────────────────────────────────────────────────────────────
window.updateProgress = function (val) {
const bar = document.getElementById('progressBar');
if (!bar) return;
@@ -535,10 +542,16 @@ document.addEventListener('DOMContentLoaded', () => {
bar.innerHTML = `<span class="fw-semibold">${v}%</span>`;
};
// ─────────────────────────────────────────────────────────────
// CSRF 토큰
// ─────────────────────────────────────────────────────────────
const csrfToken = document.querySelector('input[name="csrf_token"]')?.value || '';
// ─────────────────────────────────────────────────────────────
// 공통 POST 함수
// ─────────────────────────────────────────────────────────────
async function postFormAndHandle(url) {
const res = await fetch(url, {
method: 'POST',
@@ -562,7 +575,10 @@ document.addEventListener('DOMContentLoaded', () => {
return { success: true, html: true };
}
// ─────────────────────────────────────────────────────────────
// MAC 파일 이동
// ─────────────────────────────────────────────────────────────
const macForm = document.getElementById('macMoveForm');
if (macForm) {
macForm.addEventListener('submit', async (e) => {
@@ -582,7 +598,10 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
// ─────────────────────────────────────────────────────────────
// GUID 파일 이동
// ─────────────────────────────────────────────────────────────
const guidForm = document.getElementById('guidMoveForm');
if (guidForm) {
guidForm.addEventListener('submit', async (e) => {
@@ -602,83 +621,20 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
// 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 = '<span class="spinner-border spinner-border-sm me-2"></span>처리 중...';
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 {
// job_id가 없으면 완료로 간주
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);
}
// 완료 시 (100%)
if (data.progress >= 100) {
clearInterval(interval);
window.updateProgress(100);
// 페이지 새로고침
setTimeout(() => location.reload(), 1500);
}
} catch (err) {
console.error('진행률 확인 중 오류:', err);
clearInterval(interval);
}
}, 500); // 0.5초마다 확인
}
// ─────────────────────────────────────────────────────────────
// 알림 자동 닫기
// ─────────────────────────────────────────────────────────────
setTimeout(() => {
document.querySelectorAll('.alert').forEach(alert => {
const bsAlert = new bootstrap.Alert(alert);
bsAlert.close();
});
}, 5000);
});
</script>
<!-- 외부 script.js 파일만 로드 -->
<!-- 외부 script.js 파일 (IP 폼 처리 로직 포함) -->
<script src="{{ url_for('static', filename='script.js') }}"></script>
{% endblock %}
{% endblock %}