- fix card-stale-after-download-finish: карточка списка не обновляется после выхода задачи из downloading - goal bulk-download-management + feature bulk-delete-page: удаление раздач с файлами пачкой на отдельной странице
5.3 KiB
✨ Удалять выбранные загрузки с файлами на отдельной странице
- Тип: feature
- Категория: Ядро продукта
- Зачем: удаление с файлами живёт только в danger-секции страницы одной загрузки: чтобы снести десять раздач, надо десять раз пройти путь список → карточка → подтверждение
- Теги: goal:bulk-download-management
Отдельная страница веб-UI, доступная из шапки: список загрузок, у которых
удаление разрешено (done, orphaned, target_missing —
internal/worker/review.go:631), чекбокс у каждой строки и одна кнопка
«Удалить выбранные». Кнопка ведёт на подтверждение, где выбранные раздачи
названы поимённо, и только оттуда уходит удаление.
Страница отдельная, чтобы не вешать режим выбора на основной список: там карточки живые, самополлятся и перерисовываются, а выбор пользователя такое перерисовывание переживать не обязан.
Речь только о полном удалении (снять хардлинки + снести раздачу с файлами из
qBittorrent). Групповой Dismiss (смена статуса без файлов) в эту задачу не
входит.
Затрагивает
- новая страница веб-UI и ссылка на неё в
web/templates/partials/header.html; новый шаблон страницы и её партиалы; - новый POST-эндпоинт группового удаления в
internal/httpapi(рядом сPOST /ui/downloads/{id}/delete,internal/httpapi/httpapi.go:143); Reviewer.Deleteворкера (internal/worker/review.go:619) — зовётся по каждой выбранной загрузке;- qBittorrent
torrents/deleteсdeleteFiles=true— внешний сервис, необратимая операция; - спека
web-ui, требования «Страницы веб-UI» и «Действия соответствуют состоянию»; - спека
state-reconciliationв частиDelete— групповой вызов не меняет условий поштучного, но подтверждение человека теперь одно на пачку.
Критерии приёмки
- Страница открывается из шапки и показывает только те загрузки, для которых
удаление разрешено поштучно (оракул: тест
internal/httpapi— подставной читатель отдаёт задачи во всех состояниях, в разметке строки есть уdone/orphaned/target_missingи нет у остальных). - Удаление уходит только после явного подтверждения, и подтверждение называет
каждую выбранную раздачу поимённо (оракул: тест — POST без признака
подтверждения отвечает отказом и не делает ни одного вызова
Deleteу подставного воркера; ответ подтверждения содержит заголовки всех выбранных). - Отказ на одной загрузке не отменяет остальных, а результат называет
удалённые и отказавшие поимённо с причиной (оракул: тест, где второй
Deleteвозвращает ошибку — первая и третья удалены, страница результата называет вторую и её причину). - Групповой путь не расширяет прав поштучного: попытка удалить задачу в
состоянии, где кнопка недоступна, отклоняется с тем же отказом
(оракул: тест —
DeleteдляdownloadingвозвращаетErrConflict, страница показывает отказ, остальные выбранные не затронуты). - Поведение страницы записано дельта-спекой и проходит валидацию (оракул:
openspec validate --strictиtask gate).
Рамки
Необратимое действие: deleteFiles=true сносит файлы раздачи, гард последней
копии в Delete выключен сознательно. Ослаблять подтверждение ради удобства
пачки нельзя — оно остаётся обязательным и поимённым. Групповой Dismiss и
автоматическая чистка по сроку хранения (db-retention-cleanup) — не эта
задача.