fix: handle Listenarr CSRF antiforgery via cookie jar
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
"@solana/spl-token": "^0.4.14",
|
"@solana/spl-token": "^0.4.14",
|
||||||
"@solana/web3.js": "^1.87.6",
|
"@solana/web3.js": "^1.87.6",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
|
"axios-cookiejar-support": "^5.0.5",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"socket.io": "^4.7.3",
|
"socket.io": "^4.7.3",
|
||||||
|
"tough-cookie": "^5.1.2",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
"ws": "^8.15.1"
|
"ws": "^8.15.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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_URL = process.env.LISTENARR_URL || "";
|
||||||
const LISTENARR_API_KEY = process.env.LISTENARR_API_KEY || "";
|
const LISTENARR_API_KEY = process.env.LISTENARR_API_KEY || "";
|
||||||
|
|
||||||
const listenarrApi = axios.create({
|
// ponytail: single shared cookie jar so CSRF cookies persist across requests
|
||||||
baseURL: LISTENARR_URL,
|
const jar = new CookieJar();
|
||||||
timeout: 15000,
|
const listenarrApi: AxiosInstance = wrapper(
|
||||||
headers: {
|
axios.create({
|
||||||
"X-Api-Key": LISTENARR_API_KEY,
|
baseURL: LISTENARR_URL,
|
||||||
"Content-Type": "application/json",
|
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(
|
export async function searchAudiobooks(
|
||||||
query: string
|
query: string
|
||||||
@@ -38,10 +58,12 @@ export async function addAudiobook(options: {
|
|||||||
genres?: string[];
|
genres?: string[];
|
||||||
series?: string;
|
series?: string;
|
||||||
seriesNumber?: string;
|
seriesNumber?: string;
|
||||||
isbn?: string;
|
isbn?: string[] | string;
|
||||||
explicit?: boolean;
|
explicit?: boolean;
|
||||||
abridged?: boolean;
|
abridged?: boolean;
|
||||||
}): Promise<any> {
|
}): Promise<any> {
|
||||||
|
await ensureCsrfCookie();
|
||||||
|
|
||||||
// Defensive: ensure series/seriesNumber are always strings, not arrays
|
// Defensive: ensure series/seriesNumber are always strings, not arrays
|
||||||
const series = typeof options.series === "string" ? options.series : "";
|
const series = typeof options.series === "string" ? options.series : "";
|
||||||
const seriesNumber = typeof options.seriesNumber === "string" ? options.seriesNumber : "";
|
const seriesNumber = typeof options.seriesNumber === "string" ? options.seriesNumber : "";
|
||||||
|
|||||||
Reference in New Issue
Block a user