From 0d53f7e5fc1924557425b7d8449863d35f96e495 Mon Sep 17 00:00:00 2001 From: ze0s <43699394+zze0s@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:21:56 +0200 Subject: [PATCH] feat(download-clients): rtorrent support Digest Auth (#1596) * feat(download-clients): rtorrent support basic auth * feat(download-client): implement new auth logic * fix(download-client): tests store * chore(deps): update go-rtorrent to v1.11.0 --- go.mod | 3 +- go.sum | 6 ++ internal/database/download_client.go | 2 + internal/database/download_client_test.go | 6 ++ internal/domain/client.go | 65 ++++++++++++- internal/download_client/cache.go | 3 + internal/download_client/connection.go | 94 ++++++++++++------- internal/download_client/service.go | 83 ++++++++++------ web/src/components/tooltips/Tooltip.tsx | 2 +- web/src/domain/constants.ts | 15 +++ .../forms/settings/DownloadClientForms.tsx | 23 ++++- 11 files changed, 231 insertions(+), 71 deletions(-) diff --git a/go.mod b/go.mod index ab1b548..ce348e2 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef github.com/autobrr/go-deluge v1.2.0 github.com/autobrr/go-qbittorrent v1.9.0 - github.com/autobrr/go-rtorrent v1.10.0 + github.com/autobrr/go-rtorrent v1.11.0 github.com/avast/retry-go v3.0.0+incompatible github.com/avast/retry-go/v4 v4.6.0 github.com/containrrr/shoutrrr v0.8.0 @@ -72,6 +72,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hekmon/cunits/v2 v2.1.0 // indirect github.com/huandu/xstrings v1.4.0 // indirect + github.com/icholy/digest v0.1.23 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect diff --git a/go.sum b/go.sum index 853a75c..e4f45ec 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,10 @@ github.com/autobrr/go-qbittorrent v1.9.0 h1:HaLueJ99D3G1cQ2r5ADVbtfwyEhekt2eQoEZ github.com/autobrr/go-qbittorrent v1.9.0/go.mod h1:z88B3+O/1/3doQABErvIOOxE4hjpmIpulu6XzDG/q78= github.com/autobrr/go-rtorrent v1.10.0 h1:SCs7Rdi1BZ3MxNoVIdWK0qTUHQyhSj9rEU8KUTRi4Ug= github.com/autobrr/go-rtorrent v1.10.0/go.mod h1:1CyQ2tcLOGP+p9drOqFiVPb/+QvfExMPCHnEGQd0BmM= +github.com/autobrr/go-rtorrent v1.10.1-0.20240718115807-9f53e3443272 h1:qna9uyozEZbLS2HE7FP6qaXbnJzBOClQ+49mEUI51nM= +github.com/autobrr/go-rtorrent v1.10.1-0.20240718115807-9f53e3443272/go.mod h1:1CyQ2tcLOGP+p9drOqFiVPb/+QvfExMPCHnEGQd0BmM= +github.com/autobrr/go-rtorrent v1.11.0 h1:T1NRPgFLooFFMX0kfvewftLk4hFQUsMlDNB8WMynBSw= +github.com/autobrr/go-rtorrent v1.11.0/go.mod h1:1CyQ2tcLOGP+p9drOqFiVPb/+QvfExMPCHnEGQd0BmM= github.com/autobrr/sse/v2 v2.0.0-20230520125637-530e06346d7d h1:9EGCYgeugAVWLBAtjHC7AFnXSwUdYfCB98WaOgdDREE= github.com/autobrr/sse/v2 v2.0.0-20230520125637-530e06346d7d/go.mod h1:zCozZ9lp4DE340T2+wfMPL/eoQwLVIGDOCKCDEFwTQU= github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= @@ -200,6 +204,8 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/icholy/digest v0.1.23 h1:4hX2pIloP0aDx7RJW0JewhPPy3R8kU+vWKdxPsCCGtY= +github.com/icholy/digest v0.1.23/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= diff --git a/internal/database/download_client.go b/internal/database/download_client.go index 5609689..e0724b4 100644 --- a/internal/database/download_client.go +++ b/internal/database/download_client.go @@ -142,6 +142,7 @@ func (r *DownloadClientRepo) Store(ctx context.Context, client *domain.DownloadC Rules: client.Settings.Rules, ExternalDownloadClientId: client.Settings.ExternalDownloadClientId, ExternalDownloadClient: client.Settings.ExternalDownloadClient, + Auth: client.Settings.Auth, } settingsJson, err := json.Marshal(&settings) @@ -177,6 +178,7 @@ func (r *DownloadClientRepo) Update(ctx context.Context, client *domain.Download Rules: client.Settings.Rules, ExternalDownloadClientId: client.Settings.ExternalDownloadClientId, ExternalDownloadClient: client.Settings.ExternalDownloadClient, + Auth: client.Settings.Auth, } settingsJson, err := json.Marshal(&settings) diff --git a/internal/database/download_client_test.go b/internal/database/download_client_test.go index 309f50c..f9fb1d2 100644 --- a/internal/database/download_client_test.go +++ b/internal/database/download_client_test.go @@ -44,6 +44,12 @@ func getMockDownloadClient() domain.DownloadClient { }, ExternalDownloadClientId: 0, ExternalDownloadClient: "", + Auth: domain.DownloadClientAuth{ + Enabled: true, + Type: domain.DownloadClientAuthTypeBasic, + Username: "username", + Password: "password", + }, }, } } diff --git a/internal/domain/client.go b/internal/domain/client.go index f9d667e..d543f94 100644 --- a/internal/domain/client.go +++ b/internal/domain/client.go @@ -5,6 +5,7 @@ package domain import ( "context" + "encoding/json" "fmt" "net/url" @@ -38,10 +39,72 @@ type DownloadClient struct { type DownloadClientSettings struct { APIKey string `json:"apikey,omitempty"` - Basic BasicAuth `json:"basic,omitempty"` + Basic BasicAuth `json:"basic,omitempty"` // Deprecated: Use Auth instead Rules DownloadClientRules `json:"rules,omitempty"` ExternalDownloadClientId int `json:"external_download_client_id,omitempty"` ExternalDownloadClient string `json:"external_download_client,omitempty"` + Auth DownloadClientAuth `json:"auth,omitempty"` +} + +// MarshalJSON Custom method to translate Basic into Auth without including Basic in JSON output +func (dcs *DownloadClientSettings) MarshalJSON() ([]byte, error) { + // Ensuring Auth is updated with Basic info before marshaling if Basic is set + if dcs.Basic.Username != "" || dcs.Basic.Password != "" { + dcs.Auth = DownloadClientAuth{ + Enabled: dcs.Basic.Auth, + Type: DownloadClientAuthTypeBasic, + Username: dcs.Basic.Username, + Password: dcs.Basic.Password, + } + } + + type Alias DownloadClientSettings + return json.Marshal(&struct { + *Alias + }{ + Alias: (*Alias)(dcs), + }) +} + +// UnmarshalJSON Custom method to translate Basic into Auth +func (dcs *DownloadClientSettings) UnmarshalJSON(data []byte) error { + type Alias DownloadClientSettings + aux := &struct { + *Alias + }{ + Alias: (*Alias)(dcs), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // If Basic fields are not empty, populate Auth fields accordingly + if aux.Basic.Username != "" || aux.Basic.Password != "" { + dcs.Auth = DownloadClientAuth{ + Enabled: aux.Basic.Auth, + Type: DownloadClientAuthTypeBasic, + Username: aux.Basic.Username, + Password: aux.Basic.Password, + } + } + + return nil +} + +type DownloadClientAuthType string + +const ( + DownloadClientAuthTypeNone = "NONE" + DownloadClientAuthTypeBasic = "BASIC_AUTH" + DownloadClientAuthTypeDigest = "DIGEST_AUTH" +) + +type DownloadClientAuth struct { + Enabled bool `json:"enabled,omitempty"` + Type DownloadClientAuthType `json:"type,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` } type DownloadClientRules struct { diff --git a/internal/download_client/cache.go b/internal/download_client/cache.go index dd2784e..81a9ffa 100644 --- a/internal/download_client/cache.go +++ b/internal/download_client/cache.go @@ -1,3 +1,6 @@ +// Copyright (c) 2021 - 2024, Ludvig Lundgren and the autobrr contributors. +// SPDX-License-Identifier: GPL-2.0-or-later + package download_client import ( diff --git a/internal/download_client/connection.go b/internal/download_client/connection.go index b30e063..faa53f4 100644 --- a/internal/download_client/connection.go +++ b/internal/download_client/connection.go @@ -5,7 +5,9 @@ package download_client import ( "context" + "crypto/tls" "fmt" + "net/http" "net/url" "time" @@ -24,6 +26,7 @@ import ( "github.com/autobrr/go-qbittorrent" "github.com/autobrr/go-rtorrent" "github.com/dcarbone/zadapters/zstdlog" + "github.com/icholy/digest" "github.com/rs/zerolog" ) @@ -70,16 +73,16 @@ func (s *service) testConnection(ctx context.Context, client domain.DownloadClie func (s *service) testQbittorrentConnection(ctx context.Context, client domain.DownloadClient) error { qbtSettings := qbittorrent.Config{ Host: client.BuildLegacyHost(), + TLSSkipVerify: client.TLSSkipVerify, Username: client.Username, Password: client.Password, - TLSSkipVerify: client.TLSSkipVerify, Log: s.subLogger, } // only set basic auth if enabled - if client.Settings.Basic.Auth { - qbtSettings.BasicUser = client.Settings.Basic.Username - qbtSettings.BasicPass = client.Settings.Basic.Password + if client.Settings.Auth.Enabled { + qbtSettings.BasicUser = client.Settings.Auth.Username + qbtSettings.BasicPass = client.Settings.Auth.Password } qbt := qbittorrent.NewClient(qbtSettings) @@ -155,14 +158,31 @@ func (s *service) testDelugeConnection(ctx context.Context, client domain.Downlo } func (s *service) testRTorrentConnection(ctx context.Context, client domain.DownloadClient) error { - // create client - rt := rtorrent.NewClient(rtorrent.Config{ + cfg := rtorrent.Config{ Addr: client.Host, TLSSkipVerify: client.TLSSkipVerify, - BasicUser: client.Settings.Basic.Username, - BasicPass: client.Settings.Basic.Password, - Log: nil, - }) + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, + Log: s.subLogger, + } + + // create client + rt := rtorrent.NewClient(cfg) + + if client.Settings.Auth.Type == domain.DownloadClientAuthTypeDigest { + httpClient := &http.Client{ + Transport: &digest.Transport{ + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: client.TLSSkipVerify}, + }, + }, + } + + // override client + rt = rtorrent.NewClientWithOpts(cfg, rtorrent.WithCustomClient(httpClient)) + } name, err := rt.Name(ctx) if err != nil { @@ -206,7 +226,7 @@ func (s *service) testTransmissionConnection(ctx context.Context, client domain. return errors.Wrap(err, "error getting rpc info: %v", client.Host) } - s.log.Debug().Msgf("test client connection for Transmission: got version: %v", version) + s.log.Trace().Msgf("test client connection for Transmission: got version: %v", version) s.log.Debug().Msgf("test client connection for Transmission: success") @@ -217,9 +237,9 @@ func (s *service) testRadarrConnection(ctx context.Context, client domain.Downlo r := radarr.New(radarr.Config{ Hostname: client.Host, APIKey: client.Settings.APIKey, - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, Log: s.subLogger, }) @@ -236,9 +256,9 @@ func (s *service) testSonarrConnection(ctx context.Context, client domain.Downlo r := sonarr.New(sonarr.Config{ Hostname: client.Host, APIKey: client.Settings.APIKey, - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, Log: s.subLogger, }) @@ -255,9 +275,9 @@ func (s *service) testLidarrConnection(ctx context.Context, client domain.Downlo r := lidarr.New(lidarr.Config{ Hostname: client.Host, APIKey: client.Settings.APIKey, - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, Log: s.subLogger, }) @@ -274,9 +294,9 @@ func (s *service) testWhisparrConnection(ctx context.Context, client domain.Down r := whisparr.New(whisparr.Config{ Hostname: client.Host, APIKey: client.Settings.APIKey, - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, Log: s.subLogger, }) @@ -293,9 +313,9 @@ func (s *service) testReadarrConnection(ctx context.Context, client domain.Downl r := readarr.New(readarr.Config{ Hostname: client.Host, APIKey: client.Settings.APIKey, - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, Log: s.subLogger, }) @@ -310,8 +330,12 @@ func (s *service) testReadarrConnection(ctx context.Context, client domain.Downl func (s *service) testPorlaConnection(client domain.DownloadClient) error { p := porla.NewClient(porla.Config{ - Hostname: client.Host, - AuthToken: client.Settings.APIKey, + Hostname: client.Host, + TLSSkipVerify: client.TLSSkipVerify, + AuthToken: client.Settings.APIKey, + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, + Log: s.subLogger, }) version, err := p.Version() @@ -320,13 +344,13 @@ func (s *service) testPorlaConnection(client domain.DownloadClient) error { return errors.Wrap(err, "porla: failed to get version: %v", client.Host) } - commitish := version.Commitish + commitHash := version.Commitish - if len(commitish) > 8 { - commitish = commitish[:8] + if len(commitHash) > 8 { + commitHash = commitHash[:8] } - s.log.Debug().Msgf("test client connection for porla: found version %s (commit %s)", version.Version, commitish) + s.log.Debug().Msgf("test client connection for porla: found version %s (commit %s)", version.Version, commitHash) return nil } @@ -335,9 +359,9 @@ func (s *service) testSabnzbdConnection(ctx context.Context, client domain.Downl opts := sabnzbd.Options{ Addr: client.Host, ApiKey: client.Settings.APIKey, - BasicUser: client.Settings.Basic.Username, - BasicPass: client.Settings.Basic.Password, - Log: nil, + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, + Log: s.subLogger, } sab := sabnzbd.New(opts) diff --git a/internal/download_client/service.go b/internal/download_client/service.go index 9c58865..57a277d 100644 --- a/internal/download_client/service.go +++ b/internal/download_client/service.go @@ -5,8 +5,10 @@ package download_client import ( "context" + "crypto/tls" "fmt" "log" + "net/http" "net/url" "sync" "time" @@ -27,6 +29,7 @@ import ( "github.com/autobrr/go-qbittorrent" "github.com/autobrr/go-rtorrent" "github.com/dcarbone/zadapters/zstdlog" + "github.com/icholy/digest" "github.com/rs/zerolog" ) @@ -184,8 +187,8 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo Password: client.Password, TLSSkipVerify: client.TLSSkipVerify, Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "qBittorrent").Str("client", client.Name).Logger(), zerolog.TraceLevel), - BasicUser: client.Settings.Basic.Username, - BasicPass: client.Settings.Basic.Password, + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, }) case domain.DownloadClientTypePorla: @@ -193,8 +196,8 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo Hostname: client.Host, AuthToken: client.Settings.APIKey, TLSSkipVerify: client.TLSSkipVerify, - BasicUser: client.Settings.Basic.Username, - BasicPass: client.Settings.Basic.Password, + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Porla").Str("client", client.Name).Logger(), zerolog.TraceLevel), }) @@ -241,22 +244,46 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo client.Client = tbt case domain.DownloadClientTypeRTorrent: - client.Client = rtorrent.NewClient(rtorrent.Config{ - Addr: client.Host, - TLSSkipVerify: client.TLSSkipVerify, - BasicUser: client.Settings.Basic.Username, - BasicPass: client.Settings.Basic.Password, - Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "rTorrent").Str("client", client.Name).Logger(), zerolog.TraceLevel), - }) + if client.Settings.Auth.Type == domain.DownloadClientAuthTypeDigest { + cfg := rtorrent.Config{ + Addr: client.Host, + TLSSkipVerify: client.TLSSkipVerify, + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, + Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "rTorrent").Str("client", client.Name).Logger(), zerolog.TraceLevel), + } + + httpClient := &http.Client{ + Transport: &digest.Transport{ + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: client.TLSSkipVerify}, + }, + }, + } + + // override client + client.Client = rtorrent.NewClientWithOpts(cfg, rtorrent.WithCustomClient(httpClient)) + + } else { + client.Client = rtorrent.NewClient(rtorrent.Config{ + Addr: client.Host, + TLSSkipVerify: client.TLSSkipVerify, + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, + Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "rTorrent").Str("client", client.Name).Logger(), zerolog.TraceLevel), + }) + } case domain.DownloadClientTypeLidarr: client.Client = lidarr.New(lidarr.Config{ Hostname: client.Host, APIKey: client.Settings.APIKey, Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Lidarr").Str("client", client.Name).Logger(), zerolog.TraceLevel), - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, }) case domain.DownloadClientTypeRadarr: @@ -264,9 +291,9 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo Hostname: client.Host, APIKey: client.Settings.APIKey, Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Radarr").Str("client", client.Name).Logger(), zerolog.TraceLevel), - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, }) case domain.DownloadClientTypeReadarr: @@ -274,9 +301,9 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo Hostname: client.Host, APIKey: client.Settings.APIKey, Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Readarr").Str("client", client.Name).Logger(), zerolog.TraceLevel), - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, }) case domain.DownloadClientTypeSonarr: @@ -284,9 +311,9 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo Hostname: client.Host, APIKey: client.Settings.APIKey, Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Sonarr").Str("client", client.Name).Logger(), zerolog.TraceLevel), - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, }) case domain.DownloadClientTypeWhisparr: @@ -294,9 +321,9 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo Hostname: client.Host, APIKey: client.Settings.APIKey, Log: zstdlog.NewStdLoggerWithLevel(s.log.With().Str("type", "Whisparr").Str("client", client.Name).Logger(), zerolog.TraceLevel), - BasicAuth: client.Settings.Basic.Auth, - Username: client.Settings.Basic.Username, - Password: client.Settings.Basic.Password, + BasicAuth: client.Settings.Auth.Enabled, + Username: client.Settings.Auth.Username, + Password: client.Settings.Auth.Password, }) case domain.DownloadClientTypeSabnzbd: @@ -304,8 +331,8 @@ func (s *service) GetClient(ctx context.Context, clientId int32) (*domain.Downlo Addr: client.Host, ApiKey: client.Settings.APIKey, Log: nil, - BasicUser: client.Settings.Basic.Username, - BasicPass: client.Settings.Basic.Password, + BasicUser: client.Settings.Auth.Username, + BasicPass: client.Settings.Auth.Password, }) } diff --git a/web/src/components/tooltips/Tooltip.tsx b/web/src/components/tooltips/Tooltip.tsx index 8041819..5491eca 100644 --- a/web/src/components/tooltips/Tooltip.tsx +++ b/web/src/components/tooltips/Tooltip.tsx @@ -129,7 +129,7 @@ export const Tooltip = ({ {...getTooltipProps({ className: classNames( maxWidth, - "rounded-md border border-gray-300 text-black text-xs normal-case tracking-normal font-normal shadow-lg dark:text-white dark:border-gray-700 dark:shadow-2xl" + "z-10 rounded-md border border-gray-300 text-black text-xs normal-case tracking-normal font-normal shadow-lg dark:text-white dark:border-gray-700 dark:shadow-2xl" ), onClick: (e: React.MouseEvent) => e.stopPropagation() })} diff --git a/web/src/domain/constants.ts b/web/src/domain/constants.ts index 1628cb8..3e705ac 100644 --- a/web/src/domain/constants.ts +++ b/web/src/domain/constants.ts @@ -474,6 +474,21 @@ export const DownloadRuleConditionOptions: OptionBasic[] = [ } ]; +export const DownloadClientAuthType: OptionBasic[] = [ + { + label: "None", + value: "NONE" + }, + { + label: "Basic Auth", + value: "BASIC_AUTH" + }, + { + label: "Digest Auth", + value: "DIGEST_AUTH" + } +]; + const logLevel = ["DEBUG", "INFO", "WARN", "ERROR", "TRACE"] as const; export const LogLevelOptions = logLevel.map(v => ({ value: v, label: v, key: v })); diff --git a/web/src/forms/settings/DownloadClientForms.tsx b/web/src/forms/settings/DownloadClientForms.tsx index b9ff5d3..d72311d 100644 --- a/web/src/forms/settings/DownloadClientForms.tsx +++ b/web/src/forms/settings/DownloadClientForms.tsx @@ -14,7 +14,7 @@ import { classNames, sleep } from "@utils"; import { DEBUG } from "@components/debug"; import { APIClient } from "@api/APIClient"; import { DownloadClientKeys } from "@api/query_keys"; -import { DownloadClientTypeOptions, DownloadRuleConditionOptions } from "@domain/constants"; +import { DownloadClientAuthType, DownloadClientTypeOptions, DownloadRuleConditionOptions } from "@domain/constants"; import Toast from "@components/notifications/Toast"; import { useToggle } from "@hooks/hooks"; import { DeleteModal } from "@components/modals"; @@ -34,6 +34,12 @@ interface InitialValuesSettings { username: string; password: string; }; + auth?: { + enabled: boolean; + type: string; + username: string; + password: string; + }; rules?: { enabled?: boolean; ignore_slow_torrents?: boolean; @@ -267,12 +273,19 @@ function FormFieldsRTorrent() { /> )} - + - {settings.basic?.auth === true && ( + {settings.auth?.enabled && ( <> - - + This should in most cases be Basic Auth, but some providers use Digest Auth.

} + /> + + )}