Files
jellybit/tasks/items/bulk-delete-page.md
T
av 969926fae3 tasks: заведены баг устаревшей карточки и цель группового удаления
- fix card-stale-after-download-finish: карточка списка не обновляется после выхода задачи из downloading
- goal bulk-download-management + feature bulk-delete-page: удаление раздач с файлами пачкой на отдельной странице
2026-08-10 12:32:17 +03:00

65 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ✨ Удалять выбранные загрузки с файлами на отдельной странице
- **Тип:** 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`) — не эта
задача.