Защита загрузок от переходов по меню была добавлена
Этот коммит содержится в:
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
app_dir = Path(sys.argv[1]).resolve().parent if len(sys.argv) > 1 else Path('/opt/virtuality/web')
|
||||||
|
templates_dir = app_dir / 'templates'
|
||||||
|
changed = []
|
||||||
|
|
||||||
|
guard_script = r'''
|
||||||
|
|
||||||
|
function setUploadNavigationGuard(active) {
|
||||||
|
window.virtualityUploadActive = Boolean(active);
|
||||||
|
document.body.classList.toggle('upload-locked', window.virtualityUploadActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', function (event) {
|
||||||
|
if (!window.virtualityUploadActive) return;
|
||||||
|
event.preventDefault();
|
||||||
|
event.returnValue = 'Идёт загрузка файла. Переход остановит передачу.';
|
||||||
|
return event.returnValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', function (event) {
|
||||||
|
if (!window.virtualityUploadActive) return;
|
||||||
|
const target = event.target.closest('a, button, input[type="submit"]');
|
||||||
|
if (!target) return;
|
||||||
|
if (target.id === 'iso-upload-button' || target.id === 'disk-upload-button') return;
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
alert('Идёт загрузка файла. Дождись завершения или нажми «Отменить загрузку». Переход по меню сейчас заблокирован, чтобы файл не оборвался.');
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
document.addEventListener('submit', function (event) {
|
||||||
|
if (!window.virtualityUploadActive) return;
|
||||||
|
if (event.target && (event.target.id === 'iso-upload-form' || event.target.id === 'disk-upload-form')) return;
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
alert('Идёт загрузка файла. Дождись завершения или нажми «Отменить загрузку».');
|
||||||
|
}, true);
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
def patch_template(path: Path, active_token: str, form_id: str) -> None:
|
||||||
|
if not path.exists():
|
||||||
|
return
|
||||||
|
text = path.read_text()
|
||||||
|
original = text
|
||||||
|
|
||||||
|
if 'function setUploadNavigationGuard(active)' not in text:
|
||||||
|
marker = ' function bytesText(bytes) {'
|
||||||
|
if marker not in text:
|
||||||
|
raise SystemExit(f'bytesText marker not found in {path}')
|
||||||
|
text = text.replace(marker, guard_script + '\n' + marker, 1)
|
||||||
|
|
||||||
|
text = text.replace(' uploadActive = active;\n', ' uploadActive = active;\n setUploadNavigationGuard(active);\n')
|
||||||
|
|
||||||
|
# During server-side post-processing after upload, file transfer is done, so page navigation is safe again.
|
||||||
|
text = text.replace(" window.location.href = '/iso';", " setUploadNavigationGuard(false);\n window.location.href = '/iso';")
|
||||||
|
text = text.replace(" window.location.href = '/disk-images';", " setUploadNavigationGuard(false);\n window.location.href = '/disk-images';")
|
||||||
|
|
||||||
|
# Conversion polling is not an active browser upload anymore; allow navigation while backend operation continues.
|
||||||
|
text = text.replace(" setUploadMode(false);\n fileInput.disabled = true;", " setUploadMode(false);\n setUploadNavigationGuard(false);\n fileInput.disabled = true;")
|
||||||
|
|
||||||
|
if text != original:
|
||||||
|
path.write_text(text)
|
||||||
|
changed.append(str(path))
|
||||||
|
|
||||||
|
patch_template(templates_dir / 'iso.html', 'iso-upload-button', 'iso-upload-form')
|
||||||
|
patch_template(templates_dir / 'disk_images.html', 'disk-upload-button', 'disk-upload-form')
|
||||||
|
|
||||||
|
print('upload navigation guard patch applied:')
|
||||||
|
for item in changed or ['already applied']:
|
||||||
|
print(f'- {item}')
|
||||||
Ссылка в новой задаче
Block a user