Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ba9d15954 | |||
| 83c9d4d164 |
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
||||
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.7.16] - 2026-05-24
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Blocklist-Search Queue ID Type Mismatch** — Resolved a bug where the "Blocklist and search" action consistently returned `403 Download not found or permission denied` for all users. The server-side download lookup in `server/routes/dashboard.js` used strict equality (`===`) to compare `arrQueueId` values, but the value sent from the SPA client (read from a DOM `data-*` attribute) is always a `string`, while the value populated from the Radarr/Sonarr queue API is a `number`. Both sides are now cast to `String` before comparison, resolving the false-negative match failure. Resolves Gitea Issue [#48](https://git.i3omb.com/Gandalf/sofarr/issues/48).
|
||||
|
||||
---
|
||||
|
||||
## [1.7.15] - 2026-05-24
|
||||
|
||||
### Fixed
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sofarr",
|
||||
"version": "1.7.15",
|
||||
"version": "1.7.16",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sofarr",
|
||||
"version": "1.7.15",
|
||||
"version": "1.7.16",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sofarr",
|
||||
"version": "1.7.15",
|
||||
"version": "1.7.16",
|
||||
"description": "A personal media download dashboard that shows your downloads 'so far' while you relax on the sofa waiting for your *arr services to finish",
|
||||
"main": "server/index.js",
|
||||
"scripts": {
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ function createApp({ skipRateLimits = false } = {}) {
|
||||
* version:
|
||||
* type: string
|
||||
* description: sofarr version
|
||||
* example: "1.7.14"
|
||||
* example: "1.7.16"
|
||||
* x-code-samples:
|
||||
* - lang: curl
|
||||
* label: cURL
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ app.use(express.json({ limit: '64kb' })); // prevent oversized JSON payloads
|
||||
* version:
|
||||
* type: string
|
||||
* description: sofarr version
|
||||
* example: "1.7.14"
|
||||
* example: "1.7.16"
|
||||
*/
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', uptime: process.uptime(), version });
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ info:
|
||||
|
||||
## SSE Streaming
|
||||
Real-time updates are available via Server-Sent Events at GET /api/dashboard/stream.
|
||||
version: 1.7.15
|
||||
version: 1.7.16
|
||||
contact:
|
||||
name: sofarr
|
||||
license:
|
||||
|
||||
@@ -686,9 +686,12 @@ router.post('/blocklist-search', requireAuth, async (req, res) => {
|
||||
return res.status(400).json({ error: 'arrType must be sonarr or radarr' });
|
||||
}
|
||||
|
||||
// Look up the download to verify permission
|
||||
// Look up the download to verify permission.
|
||||
// Note: arrQueueId from req.body is always a string (DOM dataset), while
|
||||
// d.arrQueueId from the Radarr/Sonarr API is a number. Cast both to String
|
||||
// to avoid a type mismatch causing a false-negative lookup.
|
||||
const allDownloads = await downloadClientRegistry.getAllDownloads();
|
||||
const download = allDownloads.find(d => d.arrQueueId === arrQueueId && d.arrType === arrType);
|
||||
const download = allDownloads.find(d => d.arrQueueId != null && String(d.arrQueueId) === String(arrQueueId) && d.arrType === arrType);
|
||||
|
||||
if (!download) {
|
||||
console.error('[Blocklist] Download not found:', { arrQueueId, arrType });
|
||||
|
||||
@@ -972,6 +972,38 @@ describe('POST /api/dashboard/blocklist-search', () => {
|
||||
expect(res.body.ok).toBe(true);
|
||||
mockGetAllDownloads.mockRestore();
|
||||
});
|
||||
|
||||
it('matches download correctly when arrQueueId is sent as a string but stored as a number (type mismatch regression)', async () => {
|
||||
// Regression test for GitHub #48: arrQueueId from the SPA DOM dataset is always
|
||||
// a string, but the value stored in allDownloads from the Radarr/Sonarr API is a number.
|
||||
// Without String() casting the === comparison fails and returns 403.
|
||||
const app = createApp({ skipRateLimits: true });
|
||||
const { cookies, csrf, csrfCookie } = await getAuthHeaders(app);
|
||||
|
||||
const downloadClientRegistry = require('../../server/utils/downloadClients');
|
||||
const mockGetAllDownloads = vi.spyOn(downloadClientRegistry, 'getAllDownloads').mockResolvedValue([
|
||||
// arrQueueId stored as a number (as Radarr API returns it)
|
||||
{ arrQueueId: 9050001, arrType: 'radarr', importIssues: [], qbittorrent: null }
|
||||
]);
|
||||
|
||||
nock(RADARR_BASE)
|
||||
.delete('/api/v3/queue/9050001')
|
||||
.query({ removeFromClient: 'true', blocklist: 'true' })
|
||||
.reply(200, {});
|
||||
nock(RADARR_BASE)
|
||||
.post('/api/v3/command')
|
||||
.reply(200, {});
|
||||
|
||||
const res = await request(app)
|
||||
.post('/api/dashboard/blocklist-search')
|
||||
.set('Cookie', [...cookies, csrfCookie].join('; '))
|
||||
.set('X-CSRF-Token', csrf)
|
||||
// arrQueueId sent as a STRING from the client (as the SPA DOM dataset does)
|
||||
.send({ arrQueueId: '9050001', arrType: 'radarr', arrInstanceUrl: RADARR_BASE, arrInstanceKey: 'rk', arrContentId: 77, arrContentType: 'movie' });
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.ok).toBe(true);
|
||||
mockGetAllDownloads.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user