- Проверка «уже скачивается» идёт по всем операциям каталога (glob), а не по последним 50; скачивания без живого потока закрываются при старте (reap_stale_downloads), их .part удаляются, а отмена такой операции завершает её сразу, не дожидаясь потока. - Два запуска одного файла больше не пишут в один .part: проверки и создание операции под замком, файл создаётся монопольно (O_EXCL), вторая копия завершается ошибкой, не трогая чужой файл; осиротевший .part без владельца убирается перед новым запуском. - Отмена во время проверки контрольной суммы и распаковки .img.xz останавливает работу: операция завершается как отменённая, недокачанный файл, архив и недораспакованный образ удаляются. - Битый .img.xz после неудачной распаковки удаляется с понятным сообщением, повторное скачивание разрешено; проверка «файл уже есть» учитывает распакованный .img и qcow2 после конвертации. - Ссылки с кириллицей (домен IDNA, путь и параметры в процентах) открываются, некорректный порт и непонятные символы отклоняются сразу; ошибки сети описаны словами, исходный текст — в журнале. - Прогресс раз в секунду перечитывает JSON операции, чтобы не затирать cancel_requested и замечать отмену из другого процесса; операции читаются один раз на страницу; отмена в карточке — не ошибка. - Формулировки карточек и сообщения о недоступной архитектуре упрощены; добавлены тесты на каждое поведение. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019LEUXMcdZo7b87mjkUsPgg
18 строки
1.5 KiB
HTML
18 строки
1.5 KiB
HTML
{#- Карточка «Скачать по ссылке». Ожидает переменную kind ('iso' | 'disk'). -#}
|
|
{% from "_ui.html" import icon %}
|
|
<article class="card">
|
|
<div class="card-header"><div><h2>Скачать по ссылке</h2><p>Сервер скачает файл сам — не нужно сначала загружать его на компьютер</p></div></div>
|
|
<div class="card-body">
|
|
<form method="post" action="/downloads/url" class="stack-sm" data-busy="Проверяем ссылку…">
|
|
<input type="hidden" name="kind" value="{{ kind }}">
|
|
<div class="field">
|
|
<label for="download-url-{{ kind }}">Адрес файла</label>
|
|
<input class="input" id="download-url-{{ kind }}" type="url" name="url" inputmode="url" spellcheck="false" autocomplete="off" required
|
|
placeholder="{{ 'https://…/ubuntu-26.04-server.iso' if kind == 'iso' else 'https://…/debian-13-cloud.qcow2' }}">
|
|
<span class="hint">{{ 'Ссылка должна вести на файл .iso — например, со страницы загрузки Ubuntu или Debian.' if kind == 'iso' else 'Подойдут файлы .qcow2, .img, .raw и сжатые .img.xz — распакуем и подготовим сами.' }}</span>
|
|
</div>
|
|
<div class="row"><button type="submit" class="btn btn-primary">{{ icon('download') }}Скачать на сервер</button></div>
|
|
</form>
|
|
</div>
|
|
</article>
|