Improve Arr integerations

This commit is contained in:
Mukhtar Akere
2025-06-19 14:40:12 +01:00
parent c15e9d8f70
commit 086aa3b1ff
7 changed files with 472 additions and 145 deletions
+53 -3
View File
@@ -3,10 +3,12 @@ package qbit
import (
"context"
"encoding/base64"
"fmt"
"github.com/go-chi/chi/v5"
"github.com/sirrobot01/decypharr/pkg/arr"
"github.com/sirrobot01/decypharr/pkg/store"
"net/http"
"net/url"
"strings"
)
@@ -18,6 +20,45 @@ const (
arrKey contextKey = "arr"
)
func validateServiceURL(urlStr string) error {
if urlStr == "" {
return fmt.Errorf("URL cannot be empty")
}
// Try parsing as full URL first
u, err := url.Parse(urlStr)
if err == nil && u.Scheme != "" && u.Host != "" {
// It's a full URL, validate scheme
if u.Scheme != "http" && u.Scheme != "https" {
return fmt.Errorf("URL scheme must be http or https")
}
return nil
}
// Check if it's a host:port format (no scheme)
if strings.Contains(urlStr, ":") && !strings.Contains(urlStr, "://") {
// Try parsing with http:// prefix
testURL := "http://" + urlStr
u, err := url.Parse(testURL)
if err != nil {
return fmt.Errorf("invalid host:port format: %w", err)
}
if u.Host == "" {
return fmt.Errorf("host is required in host:port format")
}
// Validate port number
if u.Port() == "" {
return fmt.Errorf("port is required in host:port format")
}
return nil
}
return fmt.Errorf("invalid URL format: %s", urlStr)
}
func getCategory(ctx context.Context) string {
if category, ok := ctx.Value(categoryKey).(string); ok {
return category
@@ -32,7 +73,7 @@ func getHashes(ctx context.Context) []string {
return nil
}
func getArr(ctx context.Context) *arr.Arr {
func getArrFromContext(ctx context.Context) *arr.Arr {
if a, ok := ctx.Value(arrKey).(*arr.Arr); ok {
return a
}
@@ -78,6 +119,10 @@ func (q *QBit) categoryContext(next http.Handler) http.Handler {
})
}
// authContext creates a middleware that extracts the Arr host and token from the Authorization header
// and adds it to the request context.
// This is used to identify the Arr instance for the request.
// Only a valid host and token will be added to the context/config. The rest are manual
func (q *QBit) authContext(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
host, token, err := decodeAuthHeader(r.Header.Get("Authorization"))
@@ -86,8 +131,9 @@ func (q *QBit) authContext(next http.Handler) http.Handler {
// Check if arr exists
a := arrs.Get(category)
if a == nil {
// Arr is not configured, create a new one
downloadUncached := false
a = arr.New(category, "", "", false, false, &downloadUncached, "")
a = arr.New(category, "", "", false, false, &downloadUncached, "", "auto")
}
if err == nil {
host = strings.TrimSpace(host)
@@ -99,7 +145,11 @@ func (q *QBit) authContext(next http.Handler) http.Handler {
a.Token = token
}
}
a.Source = "auto"
if err := validateServiceURL(a.Host); err != nil {
// Return silently, no need to raise a problem. Just do not add the Arr to the context/config.json
return
}
arrs.AddOrUpdate(a)
ctx := context.WithValue(r.Context(), arrKey, a)
next.ServeHTTP(w, r.WithContext(ctx))
+6 -4
View File
@@ -10,14 +10,15 @@ import (
func (q *QBit) handleLogin(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
_arr := getArr(ctx)
_arr := getArrFromContext(ctx)
if _arr == nil {
// No arr
// Arr not in context, return OK
_, _ = w.Write([]byte("Ok."))
return
}
if err := _arr.Validate(); err != nil {
q.logger.Error().Err(err).Msgf("Error validating arr")
http.Error(w, "Invalid arr configuration", http.StatusBadRequest)
}
_, _ = w.Write([]byte("Ok."))
}
@@ -94,9 +95,10 @@ func (q *QBit) handleTorrentsAdd(w http.ResponseWriter, r *http.Request) {
}
debridName := r.FormValue("debrid")
category := r.FormValue("category")
_arr := getArr(ctx)
_arr := getArrFromContext(ctx)
if _arr == nil {
_arr = arr.New(category, "", "", false, false, nil, "")
// Arr is not in context
_arr = arr.New(category, "", "", false, false, nil, "", "")
}
atleastOne := false