Repository navigation
Release 0.36.0
28 commits since 0.35.0 — the torrent detail page and the catalogue rebuilt
around the questions their readers arrive with, and the review passes that
followed. As with 0.35.0, the findings that mattered came from probing the
running stack, not from static analysis: semgrep and gitleaks were clean or
already arbitrated, while calling the changed routes without a session, then
with another member's session, is what turned up the leaks below.
The torrent detail page
The page used to answer "what is this release?" some 700px below the fold,
repeat every audio and subtitle track twice, and offer no way to say "wrong
film". It now opens on a hero band that names the work, with two columns where
the decision — download, cost, seed obligation — is pinned, swarm health read
from the collector, and a report affordance on the metadata itself. The work's
own colour, extracted from its poster, tints the band; the remaining motion is
limited to what carries meaning (the click, the fold, the numbers).
- Hit & Run thresholds had no interface. The tracker and the API both read
them; the 24 h shown on every torrent page was a code default. They are now
in the admin panel, bounded, and read from one place. - A timed-out metadata lookup was cached as "no such record" for an hour.
One network hiccup cost thirty posters until the entry expired. Timeouts,
5xx, 429 and malformed JSON now share a 120 s error cache; only a real 404
is remembered as an absence. - The poster relay accepted any client. No
Sec-Fetchheaders, any port,
any image type — an SVG served from our own origin would have executed
there. Now: same-origin image requests only, three CDN hosts, raster types,
a 400 KB cap enforced during the stream, no redirects, 60/min per address. - A held-down
Dkey downloaded on repeat; the shortcut now ignores key
repeat, composition, open modals and editable targets.
The catalogue
A bar that understands what you type — s02e04, @member, a TMDb or IMDb
link, a resolution, a year — turning each into a removable chip; a rail of
facets that carry their counts; works before releases; and an empty state that
proposes what to drop rather than apologising.
A work card opens on its own header, with a chevron: the scope badge used to be
the only way in. Inside, seasons and episodes fold down to the releases, each
carrying its age with the exact date on hover.
- Relevance ranking. A text search could only be sorted by age. Work titles
are now indexed and searched, so a file namedSousou.no.Frieren.S01E10
answers to "frieren", and a near miss gets a did-you-mean. - Saved searches match what the page filters — season, episode, year,
uploader and tag families included — and their links replay exactly what was
stored. - A hover menu on Torrents: shortcuts (everything, today, freeleech,
favourites), this browser's recent searches, and the category tree. Opens on
hover with an intention, on the chevron by click or keyboard, never on hover
alone for a touch screen. - Per-instance catalogue defaults (view, sort, page size, visible facets), a
record of searches that found nothing, and category merge — all in the admin
panel.
Security and correctness
- The abuse counter is per address, and the web container has one for the
whole site. Four API calls per page view meant 25 page views in 10 seconds
blacklisted the instance for everyone, for five minutes doubling to twenty-four
hours. Server-side rendering is now recognised by a discriminant an Internet
client cannot forge (private socket peer and no forwarding header), and
only the coarse counter is relaxed — never the agent filter, the path
validation, the address bans, the per-route limits or authentication. anonymous_uploadswas honoured by the detail page and the feeds but by
none of the new catalogue paths. The listing returned the name,
?uploader=<name>listed an anonymous member's whole history, and a saved
search on that name notified every one of their uploads.- "frieren" found one release out of nine. A dotted release name was a
single token for the text-search parser, and the fuzzy fallback hid the hole
whenever the exact search returned zero. backdrop-filterwritten before its-webkittwin was dropped by the
minifier. Five glass surfaces across the site had no blur in Chrome — a
correct source and a stripped stylesheet.- Unbounded work, now bounded:
tagGroupsfanned out to one query per group on
a pool of ten connections,search_missesgrew a row per distinct empty
search, and neither the listing nor the facets carried a rate limit. - Visibility: the did-you-mean read work titles with no veil, and two per-hash
routes answered for a torrent the member cannot see. - Category merge failed with a 500 on its nominal case (both categories
carrying an upload pattern), and could make an adult category's torrents
visible to members who opted out. - Determinism: every sort now ends on a unique key, so a "load more" cannot
repeat or drop a row; the year facet counts every year in a name with the
same pattern the filter uses; and the metadata warmer no longer marks a work
done for a week because the upstream was briefly unavailable.
Verified
nuxt typecheck and the API tsc clean; 558 API and 948 web unit tests; schema
parity between schema.ts and the 77 migrations; the end-to-end suite (13
scenarios, 543 checks) against images built from this branch; and a browser
pass at 1280 and 390, dark and light, with no horizontal scroll, no truncated
text, no dangling aria-controls and no failed asset.
🤖 Generated with Claude Code
What's Changed
- Rebuild the torrent detail page and the catalogue around the reader's questions by @Dim145 in #30
- Release 0.36.0 by @Dim145 in #31
Full Changelog: 0.35.0...0.36.0