fix: handle Listenarr CSRF antiforgery via cookie jar

This commit is contained in:
2026-07-01 09:44:31 -04:00
parent d6b846b922
commit b76d39a75e
2 changed files with 34 additions and 10 deletions
+2
View File
@@ -19,6 +19,7 @@
"@solana/spl-token": "^0.4.14",
"@solana/web3.js": "^1.87.6",
"axios": "^1.6.2",
"axios-cookiejar-support": "^5.0.5",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
@@ -30,6 +31,7 @@
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"socket.io": "^4.7.3",
"tough-cookie": "^5.1.2",
"tweetnacl": "^1.0.3",
"ws": "^8.15.1"
},
+32 -10
View File
@@ -1,16 +1,36 @@
import axios from "axios";
import axios, { type AxiosInstance } from "axios";
import { wrapper } from "axios-cookiejar-support";
import { CookieJar } from "tough-cookie";
const LISTENARR_URL = process.env.LISTENARR_URL || "";
const LISTENARR_API_KEY = process.env.LISTENARR_API_KEY || "";
const listenarrApi = axios.create({
baseURL: LISTENARR_URL,
timeout: 15000,
headers: {
"X-Api-Key": LISTENARR_API_KEY,
"Content-Type": "application/json",
},
});
// ponytail: single shared cookie jar so CSRF cookies persist across requests
const jar = new CookieJar();
const listenarrApi: AxiosInstance = wrapper(
axios.create({
baseURL: LISTENARR_URL,
timeout: 15000,
headers: {
"Content-Type": "application/json",
...(LISTENARR_API_KEY ? { "X-Api-Key": LISTENARR_API_KEY } : {}),
},
withCredentials: true,
jar,
}),
);
// ponytail: prime the antiforgery cookie on module load
let csrfPrimed = false;
async function ensureCsrfCookie(): Promise<void> {
if (csrfPrimed || LISTENARR_API_KEY) return;
try {
await listenarrApi.get("/api/v1/system/status");
csrfPrimed = true;
} catch {
// ignore — we just need the cookie set
}
}
export async function searchAudiobooks(
query: string
@@ -38,10 +58,12 @@ export async function addAudiobook(options: {
genres?: string[];
series?: string;
seriesNumber?: string;
isbn?: string;
isbn?: string[] | string;
explicit?: boolean;
abridged?: boolean;
}): Promise<any> {
await ensureCsrfCookie();
// Defensive: ensure series/seriesNumber are always strings, not arrays
const series = typeof options.series === "string" ? options.series : "";
const seriesNumber = typeof options.seriesNumber === "string" ? options.seriesNumber : "";