From 34cf89b6be1dac1984e15f79a48e6ae93dc48d5a Mon Sep 17 00:00:00 2001 From: Castle <7586648+MythodeaLoL@users.noreply.github.com> Date: Mon, 5 Jan 2026 05:20:38 -0300 Subject: [PATCH 1/4] fix(category): add correct category name - fix category path's - added override to api key - added secure select for real category --- config.sample.yaml | 9 ++- .../config/SABnzbdConfigSection.tsx | 57 ++++++++++++++---- internal/api/auth_handlers.go | 21 +++++++ internal/api/parsers.go | 10 ++++ internal/api/sabnzbd_handlers.go | 43 +++++++++++--- internal/config/manager.go | 58 +++++++++++++++++-- internal/importer/service.go | 17 +++++- 7 files changed, 188 insertions(+), 27 deletions(-) diff --git a/config.sample.yaml b/config.sample.yaml index f90330956..09260cf49 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -10,6 +10,7 @@ webdav: # REST API configuration api: prefix: '/api' # API endpoint prefix + key_override: '' # 33-char API key override (takes precedence over database key, leave empty to use database) # Authentication configuration auth: @@ -168,8 +169,12 @@ mount_path: '' # WebDAV mount path, Example: '/mnt/altmount' or '/mnt/unionfs'. # SABnzbd-compatible API configuration sabnzbd: enabled: false # Enable SABnzbd-compatible API - complete_dir: '/complete' # The complete directory where the files will be "downloaded" - categories: # Download categories (optional) + complete_dir: '/' # Base virtual directory (relative to mount point, defaults to root) + categories: # Download categories + - name: 'Default' # System default category (name cannot be changed) + order: 0 + priority: 0 + dir: 'complete' # Default directory for uncategorized downloads - name: 'movies' order: 1 priority: 0 diff --git a/frontend/src/components/config/SABnzbdConfigSection.tsx b/frontend/src/components/config/SABnzbdConfigSection.tsx index ffd0bf849..8c02d8043 100644 --- a/frontend/src/components/config/SABnzbdConfigSection.tsx +++ b/frontend/src/components/config/SABnzbdConfigSection.tsx @@ -24,6 +24,9 @@ const DEFAULT_NEW_CATEGORY: NewCategoryForm = { dir: "", }; +const DEFAULT_CATEGORY_NAME = "Default"; +const isDefaultCategory = (categoryName: string) => categoryName === DEFAULT_CATEGORY_NAME; + export function SABnzbdConfigSection({ config, onUpdate, @@ -82,11 +85,11 @@ export function SABnzbdConfigSection({ const errors: string[] = []; if (data.enabled) { - // Validate complete_dir is required and absolute + // Validate complete_dir is required and starts with / if (!data.complete_dir?.trim()) { errors.push("Complete directory is required when SABnzbd API is enabled"); } else if (!data.complete_dir.startsWith("/")) { - errors.push("Complete directory must be an absolute path (starting with /)"); + errors.push("Complete directory must start with /"); } // Validate category names are unique @@ -134,12 +137,27 @@ export function SABnzbdConfigSection({ }; const handleCategoryUpdate = (index: number, updates: Partial) => { + const category = formData.categories[index]; + // For Default category, only prevent renaming (allow order, priority, dir changes) + if (isDefaultCategory(category.name) && updates.name !== undefined) { + // Prevent renaming Default category + delete updates.name; + if (Object.keys(updates).length === 0) { + return; + } + } + const categories = [...formData.categories]; categories[index] = { ...categories[index], ...updates }; updateFormData({ categories }); }; const handleRemoveCategory = (index: number) => { + const category = formData.categories[index]; + // Prevent removing Default category + if (isDefaultCategory(category.name)) { + return; + } const categories = formData.categories.filter((_, i) => i !== index); updateFormData({ categories }); }; @@ -149,6 +167,12 @@ export function SABnzbdConfigSection({ return; } + // Prevent creating a category with the reserved name + if (newCategory.name.trim() === DEFAULT_CATEGORY_NAME) { + setValidationErrors([`"${DEFAULT_CATEGORY_NAME}" is a reserved category name`]); + return; + } + const category: SABnzbdCategory = { name: newCategory.name.trim(), order: newCategory.order, @@ -231,12 +255,11 @@ export function SABnzbdConfigSection({ className="input" value={formData.complete_dir} readOnly={isReadOnly} - placeholder="/mnt/altmount/complete" + placeholder="/" onChange={(e) => handleCompleteDirChange(e.target.value)} />

- Absolute path to the directory where the full imports will be stored, relative to the - mounted folder. + Base virtual directory (relative to mount point, defaults to root).

@@ -346,9 +369,16 @@ export function SABnzbdConfigSection({
{formData.categories .sort((a, b) => a.order - b.order) - .map((category, index) => ( -
+ .map((category, index) => { + const isDefault = isDefaultCategory(category.name); + return ( +
+ {isDefault && ( +
+ System Default (name cannot be changed) +
+ )}
Name @@ -356,11 +386,15 @@ export function SABnzbdConfigSection({ type="text" className="input input-sm" value={category.name} - readOnly={isReadOnly} + readOnly={isReadOnly || isDefault} + disabled={isDefault} onChange={(e) => handleCategoryUpdate(index, { name: e.target.value }) } /> + {isDefault && ( +

Name is immutable

+ )}
Order @@ -400,12 +434,15 @@ export function SABnzbdConfigSection({ className="input input-sm" value={category.dir} readOnly={isReadOnly} - placeholder="Optional subdirectory" + placeholder={isDefault ? "complete" : "Optional subdirectory"} onChange={(e) => handleCategoryUpdate(index, { dir: e.target.value })} /> + {isDefault && ( +

Default: complete

+ )}
- {!isReadOnly && ( + {!isReadOnly && !isDefault && (
- ))} + )})}
)}