From 0cdbb233cd25a13e306b872aee27cffd09950de8 Mon Sep 17 00:00:00 2001 From: Michael Goodnow Date: Fri, 6 Oct 2023 01:30:10 -0400 Subject: [PATCH] Handle URL paths better (#514) added a `basePath` argument to `extractCredentialsFromURL` that should hopefully prevent us from making the same mistake I made again. ```js > extractCredentialsFromUrl('http://admin:adminadmin@localhost:8080/qbit') OkResult { contents: { username: 'admin', password: 'adminadmin', href: 'http://localhost:8080/qbit' } } > extractCredentialsFromUrl('http://admin:adminadmin@localhost:8080') OkResult { contents: { username: 'admin', password: 'adminadmin', href: 'http://localhost:8080' } } > extractCredentialsFromUrl('http://admin:adminadmin@localhost:8080/qbit', '/api/v2') OkResult { contents: { username: 'admin', password: 'adminadmin', href: 'http://localhost:8080/qbit/api/v2' } } > extractCredentialsFromUrl('http://admin:adminadmin@localhost:8080', '/api/v2') OkResult { contents: { username: 'admin', password: 'adminadmin', href: 'http://localhost:8080/api/v2' } } ``` --------- Co-authored-by: zakary <123845855+zakkarry@users.noreply.github.com> --- src/clients/QBittorrent.ts | 19 +++++++++++-------- src/utils.ts | 11 ++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/clients/QBittorrent.ts b/src/clients/QBittorrent.ts index 46730110c..3556b4add 100644 --- a/src/clients/QBittorrent.ts +++ b/src/clients/QBittorrent.ts @@ -87,18 +87,23 @@ interface Category { export default class QBittorrent implements TorrentClient { cookie: string; + url: { username: string; password: string; href: string }; - async login(): Promise { + constructor() { const { qbittorrentUrl } = getRuntimeConfig(); - const { username, password, href } = extractCredentialsFromUrl( - qbittorrentUrl + this.url = extractCredentialsFromUrl( + qbittorrentUrl, + "/api/v2" ).unwrapOrThrow( new CrossSeedError("qBittorrent url must be percent-encoded") ); + } + async login(): Promise { let response: Response; + const { href, username, password } = this.url; try { - response = await fetch(`${href}api/v2/auth/login`, { + response = await fetch(`${href}/auth/login`, { method: "POST", body: new URLSearchParams({ username, password }), }); @@ -137,10 +142,8 @@ export default class QBittorrent implements TorrentClient { label: Label.QBITTORRENT, message: `Making request to ${path} with body ${body.toString()}`, }); - const { qbittorrentUrl } = getRuntimeConfig(); - const { href } = - extractCredentialsFromUrl(qbittorrentUrl).unwrapOrThrow(); - const response = await fetch(`${href}api/v2${path}`, { + + const response = await fetch(`${this.url.href}${path}`, { method: "post", headers: { Cookie: this.cookie, ...headers }, body, diff --git a/src/utils.ts b/src/utils.ts index 1cb408e8a..f7a9efbee 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { basename } from "path"; +import path, { basename, join } from "path"; import { EP_REGEX, MOVIE_REGEX, @@ -96,14 +96,19 @@ export function fallback(...args: T[]): T { } export function extractCredentialsFromUrl( - url: string + url: string, + basePath?: string ): Result<{ username: string; password: string; href: string }, "invalid URL"> { try { const { origin, pathname, username, password } = new URL(url); return resultOf({ username: decodeURIComponent(username), password: decodeURIComponent(password), - href: origin + pathname, + href: basePath + ? origin + path.posix.join(pathname, basePath) + : pathname === "/" + ? origin + : origin + pathname, }); } catch (e) { return resultOfErr("invalid URL");