From a7126d4e51dd4a8568f4180a4342f0164de33c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?= <78488229+viktor138irk@users.noreply.github.com> Date: Thu, 7 May 2026 06:20:45 +0900 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D0=BC=D0=B5=D0=BD=D0=B0=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20=D0=B4=D0=B8=D1=81?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=B0=20=D0=B1=D1=8B=D0=BB=D0=B0=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/templates/disk_images.html | 55 ++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/web/templates/disk_images.html b/web/templates/disk_images.html index 861c14c..fdf2dfa 100644 --- a/web/templates/disk_images.html +++ b/web/templates/disk_images.html @@ -118,6 +118,8 @@ const loadedLine = document.getElementById('upload-loaded'); const speedLine = document.getElementById('upload-speed'); const allowedExtensions = ['.img', '.raw', '.qcow2', '.img.xz', '.zip', '.tar.gz', '.tgz']; + let activeXhr = null; + let uploadActive = false; function bytesText(bytes) { if (!bytes) return '0 MB'; @@ -140,6 +142,26 @@ errorBox.hidden = !message; } + function setUploadMode(active) { + uploadActive = active; + if (active) { + button.disabled = false; + button.type = 'button'; + button.textContent = 'Отменить загрузку'; + button.classList.remove('primary'); + button.classList.add('danger'); + fileInput.disabled = true; + } else { + activeXhr = null; + button.type = 'submit'; + button.textContent = 'Загрузить образ диска'; + button.classList.add('primary'); + button.classList.remove('danger'); + fileInput.disabled = false; + button.disabled = !validDiskFile(fileInput.files[0]); + } + } + function resetProgress(title, detail) { bar.style.width = '0%'; percent.textContent = '0%'; @@ -149,6 +171,10 @@ } async function pollConvert(operationId) { + setUploadMode(false); + fileInput.disabled = true; + button.disabled = true; + button.textContent = 'Идёт конвертация…'; resetProgress('Конвертация образа в qcow2…', '0%'); while (true) { const response = await fetch('/api/operations/' + operationId, {cache: 'no-store'}); @@ -171,6 +197,10 @@ if (op.status === 'error') { statusLine.textContent = 'Ошибка конвертации: ' + (op.message || 'см. журнал операций'); button.disabled = false; + button.type = 'submit'; + button.textContent = 'Загрузить образ диска'; + button.classList.add('primary'); + button.classList.remove('danger'); fileInput.disabled = false; return; } @@ -198,8 +228,14 @@ hint.textContent = file.name + ' · ' + bytesText(file.size); }); + button.addEventListener('click', function () { + if (!uploadActive) return; + if (activeXhr) activeXhr.abort(); + }); + form.addEventListener('submit', function (event) { event.preventDefault(); + if (uploadActive) return; const file = fileInput.files[0]; setFileError(''); if (!validDiskFile(file)) { @@ -210,12 +246,12 @@ } const xhr = new XMLHttpRequest(); + activeXhr = xhr; const data = new FormData(form); const started = Date.now(); box.hidden = false; - button.disabled = true; - fileInput.disabled = true; + setUploadMode(true); resetProgress('Передача файла на сервер…', '0 MB из ' + bytesText(file.size)); xhr.upload.onprogress = function (event) { @@ -247,15 +283,22 @@ window.location.href = '/disk-images'; } else { statusLine.textContent = 'Ошибка загрузки: HTTP ' + xhr.status; - button.disabled = false; - fileInput.disabled = false; + setUploadMode(false); } }; + xhr.onabort = function () { + statusLine.textContent = 'Загрузка отменена пользователем.'; + bar.style.width = '0%'; + percent.textContent = '0%'; + loadedLine.textContent = '0 MB'; + speedLine.textContent = 'отменено'; + setUploadMode(false); + }; + xhr.onerror = function () { statusLine.textContent = 'Ошибка сети во время загрузки.'; - button.disabled = false; - fileInput.disabled = false; + setUploadMode(false); }; xhr.open('POST', form.action);