Commit Graph
19 Commits
Author SHA1 Message Date
splitsec2 e575ff0fcb feat(activity): clearer cards, a Rejected view and Reopen for failed deliveries (#1618)
A batch of activity list changes from running it as an admin on my own
instance. They are separate commits that build on each other, so they
can be reviewed in order.

The cards read better. Every card gets a format pill (violet for audio,
orange for ebooks), the requester is named once, and finished cards say
how long a download took and how long ago it ended ("took 5m · 35m
ago"). A download that picked up work already under way shows a
Reattached pill.

Admins can tell what is automatic and what needs a person. A declined
request shows who declined it and when, anything with an `[auto]` note
reads "Declined automatically", and "Needs Review" becomes "No Match
Yet" with a line saying the search repeats on every sync.

There is a Rejected view next to History for admins, with Reopen on each
card and a way to hide the dead ones.

The part to read most carefully is the backend in `requests_service.py`
and `user_db.py`. An admin can now reopen a fulfilled request whose
download failed or was cancelled, and the reopen a failed download
triggers remembers which release failed, so the next search doesn't pick
it again.

I ran it on a scratch instance (when pulling them together to move from
my fork to upstream) as an admin with seeded requests and looked at the
pills, the durations, the wording and the Rejected view in a browser.
Reopening a fulfilled request I only exercised in the tests. The
frontend and Python suites, type check, ruff and basedpyright pass.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **New Features**
* Admins can review rejected requests in a dedicated view, hide or
restore cards, and reopen requests.
* Fulfilled requests with failed or cancelled deliveries can be
reopened; the failed release is skipped in the next automatic search.
* Activity cards now show format badges, download timing, and when a
download resumes existing work.
* **Improvements**
* Activity views clarify automatic retries and actions, and show
reviewer names for admins.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-09 12:02:21 -06:00
splitsec2 8c06862ec7 feat(requests): let a book be requested alongside downloads (#1579)
In Download mode there is no way to ask for a book that has nothing to
download yet. The release modal has an "Add to requests" chip for
admins, which sends a book-level request, and when the default mode is
Download the server answers it with "Download policy requires a concrete
release". That is #995, where @expostarv2 used to bookmark unreleased
books this way. #1019 is the other side of it: @punkincognito wants a
less technical user to be able to download from one source and, when
that fails, ask for the book without choosing a release.

A book-level request in Download mode now becomes a normal pending book
request, stored with the mode `request_book`, when the actor is an admin
or when a new setting allows it: **Allow book requests alongside
downloads** (`REQUESTS_ALLOW_BOOK_REQUEST_WITH_DOWNLOAD`, off by
default, per-user overridable). A request that names a release still
goes straight to the download queue as before, and with the setting off
a non-admin gets the same error as today, so nothing changes for an
existing install unless it turns the setting on.

The policy endpoint reports the setting, and the release modal shows
"Add to requests" to a non-admin when requests are on and the setting
allows it (admins keep it as before, and it stays off while fulfilling a
request). The small `canRequestBookFromReleaseModal` helper holds that
rule so it can be tested.

I chose a setting over making this the default for everyone because it
changes what Download mode means, which is yours to decide. The other
way to read #1019 is a per-source rule, "Request Book" for Prowlarr and
Download for the direct sources. That is not here: the rules matrix
excludes Request Book today, the release modal shows such a source as
unavailable, and it needs more UI work than I can check without trying
it in a browser. This PR covers the half that works with the existing
flow, and the rule version can follow if you want it.

Tests: four route tests (a user with the setting on, an admin with it
off, a release request that must still queue a download, and the policy
endpoint reporting the setting), and a frontend test for the helper. The
route tests that depend on the change fail on current main. Full backend
and frontend suites pass, with ruff, oxlint and tsc clean. I have not
tried it in a browser, so this is a draft until someone has looked at
the modal.


Closes #995. #1019 stays open for the per-source rule.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **New Features**
* Added an off-by-default, user-overridable setting that lets users with
download access request a book without selecting a release. These
requests remain pending for admin action, and admins can always make
book-level requests.
* Eligible users can make book-level requests under the download policy
when the setting is enabled. This option is unavailable in browse-fulfil
mode.
* Requests that include release details continue to queue downloads
rather than create book-level requests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-08 17:56:10 -07:00
splitsec2 a1ac017487 feat(requests): let an admin reopen a rejected request (#1491)
Closes #1445, where you said you were open to it.

A rejected request was final. `update_request` won't move a terminal
status and the admin routes only fulfil or reject, so an admin who
changed their mind had to ask the user to request the book again, and
the user kept seeing the old rejected one.

`POST /api/admin/requests/<id>/reopen` moves a rejected request back to
pending and clears the review fields. What the user asked for (request
level, release, note) is kept. It refuses anything that isn't rejected,
and refuses when that user already has a pending request for the same
book.

Admins get a Reopen action on rejected cards in the activity sidebar and
in History. A rejected request has usually been cleared from the list by
the time anyone wants it back, so reopening also clears the dismissal,
the way the failed-download reopen does. Without that the pending
request stays hidden.

Tests cover the service (state change, what is kept, the refusals, a
non-string admin note) and the route, and the card model for the new
action. Ruff, tsc, oxlint, vitest and the backend suite pass. It has
been running on my instance since Oct 8.
2026-10-20 23:20:26 -04:00
splitsec2 ab3aa9a8b0 fix(requests): reject non-object items in the batch endpoint (#1369)
`POST /api/requests/batch` checks that `requests` is a non-empty list
and then hands each element to the shared preparation helper, which
calls `.get()` on it. A bare string, number or null in the list raises
`AttributeError` and the caller gets a 500, while `POST /api/requests`
answers 400 with a message for the same mistake.

This validates the element type beside the existing list check. One bad
item rejects the whole batch rather than being reported per item, which
matches the endpoint's current contract: every other failure path
already aborts the batch with a single error body, as
`test_batch_create_requests_is_atomic` asserts. Responses for valid
payloads are unchanged.

## Verification

- `tests/core/test_request_routes_api.py`: a case per bad shape (int,
str, null, list), plus a mixed valid and invalid batch that asserts
nothing was created. All fail on current main with a 500 and pass here.
- Full suite (3168), ruff, ruff format, basedpyright, vulture green.
2026-09-20 23:03:31 -04:00
splitsec2 545480c557 fix(download): default is_admin to False in the request policy guard (#1358)
`_resolve_policy_mode_for_current_user` reads `session.get("is_admin",
True)`, so a session carrying `user_id` but no `is_admin` key skips the
request policy entirely, while every other admin check in the codebase
defaults the key to `False`.

This uses the same default here. Every authenticated login path
(builtin, CWA, proxy, OIDC) writes `is_admin` into the session, and
`AUTH_METHOD=none` is already short-circuited one line earlier, so
sessions from those flows behave exactly as before.

## Verification

- `tests/core/test_request_routes_api.py::TestDownloadPolicyGuards`: a
session without `is_admin` now gets `policy_requires_request` and
nothing is queued. Fails on current main, passes here.
- Full suite (3147), ruff, ruff format, basedpyright, vulture green.
2026-09-20 12:50:53 -04:00
Alex 0e120abfaf Validate request policy release source (#975) 2026-05-10 09:46:31 +01:00
Alex e615797e69 Enforce release source availability before queueing (#971) 2026-05-10 08:52:17 +01:00
Alex d7b9f2e67f Backend test hardening + quality enforcement (#872)
- Reworked many tests
- Enforcing lint + type checking for test suite
- Fixed various issues surfaced by the new tests
- CI tweaks
2026-04-12 12:01:52 +01:00
Alex 678c54cba2 Fixes: Entrypoint, seedtime, request policy flow (#805)
- Added a path for rootless permissions in the entrypoint script
- Routed prowlarr searches through torznab for seedtime info
- Added additional request flow for download permissions
2026-03-25 18:34:42 +00:00
Alex cb093f61c6 Feature: Combined book+audiobook downloads (#773)
- Adds a combined search option in the search bar selector
- Choose both a book and audiobook file in a two-step release modal, and
download both simultaneously from a single search result.
- Works for requests. Request both a book+audiobook at once, or works
seamlessly with request policies that differ between book + audiobook
(E.g. automatically download the ebook portion, while the audiobook gets
sent as a request)
- Hidden for users who have book or audiobooks blocked. 

Closes #611
2026-03-16 18:34:46 +00:00
Alex 8bb188c903 Refactor direct source to use universal API (#711) 2026-03-06 12:59:37 +00:00
Alex cce2c10704 Download history refactor pt.2 (#703)
Two-phase download history: downloads are now recorded in the DB at
queue time (not just at terminal time), eliminating the need to
reconstruct metadata in the terminal hook and removing the
`_is_graduated_request_download()` request-scan mess
2026-03-05 13:06:34 +00:00
Alex fbe25725d3 Download history refactor (#700)
- Much simpler handling of downloads in the activity sidebar, and
improved storage, persistence and UI behavior.
- Replace `ActivityService` with direct storage on
`DownloadHistoryService` and `download_requests` and removes the
activity_log/activity_dismissals tables
- Simplify no-auth mode by removing the fake user row pattern, handled
internally
- Add local download fallback so history entries can still serve files
after tasks leave the queue
- Downloads, requests and history are now entirely persistent between
updates / restarts, and correctly tied to each user.
2026-03-04 19:10:06 +00:00
Alex 7d992c3918 User DB cleanup and refactor (#686)
- Refactored user and request code to avoid any database conflicts
- Fix threading behavior with custom script execution
- Harden the no_auth activity user filtering
- Add a hint to add local admin if none is created
- Added secret key to persist login states across updates / restarts
2026-03-02 15:41:15 +00:00
Alex ac36d539c8 Patch: Various fixes (#660)
Various fixes from the last couple days: 

- Add manual approval option for book/audiobook requests (#651)
- Add flagged HTTP headers 
- Add filesystem fallback - copy + delete when hardlink/move fails
across filesystems (#647)
- Dependency updates
- Tweak frontend test config (simplified tsconfig for tests)
- Fix overlapping sort scoring in release modal - duplicate scoring keys
caused incorrect release ordering (#654 )
- Fix stale search session after download - search state was not
refreshed when returning from a download (#659)
- Fix multi-format release filtering - releases with multiple formats
were incorrectly excluded by the format filter (#658)
- Fix config persistence when action button is used - clicking "Test
connection" reset unsaved settings (#657)
- Fix request grid text positioning in admin request policy panel 
- Fix rTorrent path discovery (#653)
- Fix `/login` API check (#650)
2026-02-25 18:44:46 +00:00
Alex c80c88676c Fix direct search request flow (#644)
Fixes #643
2026-02-23 09:27:55 +00:00
Alex 1931eb96a5 Feature: Notification support + Enhanced request management (#618)
- Added notification support via Apprise dependency
- Notifications can be configured globally or per user, with full
customization of events and notification type.
- Added expanded ActivityCard for increased detail of each request, file
info, and managing the attached file.
- Enhanced tests
2026-02-15 17:59:53 +00:00
Alex b7bee132a1 Requests: Various fixes and improvements (#617)
- Refactored activity backend for full user-level management, using the
db file
- Revamped the activity sidebar UX and categorisation
- Added download history and user filtering
- Added User Preferences modal, giving limited configuration for
non-admins - replaces the "restrict settings" config option.
- Many many bug fixes
- Many many new tests
2026-02-14 18:24:28 +00:00
Alex 68608b6162 Feature: Multi-user request system (#615)
- Adds a comprehensive multi-user request system to the existing
download flow
- Request configuration is policy based. Configure global settings for
content type, or narrow down policy for specific sources (E.g. allow
direct downloads, set prowlarr to request only, block IRC completely,
etc).
- Global policy configuration and per-user overrides for tailored
configs
- Replaced downloads sidebar with ActivitySidebar, combining active
downloads with requests. Admin management of user requests is done here,
and admins have view of downloads from all users. Sidebar can now be
pinned.
- Request either a standard book or a specific release. Release-requests
are used if you permit one source differently than the other. On
book-level requests, admins pick the specific file to be attached to the
fulfilled request.
- Users can request books with a note

This is WIP so some features are still not complete (notifications, more
automatic release selection, among others).
2026-02-14 11:08:20 +00:00