mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-06-12 07:45:49 -04:00
try fix config update
This commit is contained in:
@@ -8,7 +8,7 @@ public class ArrInstanceDto
|
||||
/// <summary>
|
||||
/// Unique identifier for this instance
|
||||
/// </summary>
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public Guid? Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Friendly name for this instance
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Common.Configuration.DTOs.DownloadClient;
|
||||
/// <summary>
|
||||
/// DTO for retrieving DownloadClient configuration (excludes sensitive data)
|
||||
/// </summary>
|
||||
public class DownloadClientConfigDto
|
||||
public class DownloadClientConfigDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of download clients configured for the application
|
||||
@@ -26,7 +26,7 @@ public class ClientConfigDto
|
||||
/// <summary>
|
||||
/// Unique identifier for this client
|
||||
/// </summary>
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public Guid? Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Friendly name for this client
|
||||
@@ -36,12 +36,12 @@ public class ClientConfigDto
|
||||
/// <summary>
|
||||
/// Type of download client
|
||||
/// </summary>
|
||||
public DownloadClientType Type { get; set; } = DownloadClientType.None;
|
||||
public required DownloadClientType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Host address for the download client
|
||||
/// </summary>
|
||||
public string Host { get; set; } = string.Empty;
|
||||
public Uri? Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Username for authentication (included without password)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Common.Attributes;
|
||||
using Common.Configuration;
|
||||
using Common.Enums;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Common.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Common.Configuration.DownloadClient;
|
||||
@@ -29,12 +28,12 @@ public sealed record ClientConfig
|
||||
/// <summary>
|
||||
/// Type of download client
|
||||
/// </summary>
|
||||
public DownloadClientType Type { get; init; } = DownloadClientType.None;
|
||||
public required DownloadClientType Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Host address for the download client
|
||||
/// </summary>
|
||||
public string Host { get; init; } = string.Empty;
|
||||
public Uri? Host { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Username for authentication
|
||||
@@ -57,7 +56,7 @@ public sealed record ClientConfig
|
||||
/// <summary>
|
||||
/// The computed full URL for the client
|
||||
/// </summary>
|
||||
public Uri Url => new($"{Host.TrimEnd('/')}/{UrlBase.TrimStart('/').TrimEnd('/')}");
|
||||
public Uri Url => new($"{Host?.ToString().TrimEnd('/')}/{UrlBase.TrimStart('/').TrimEnd('/')}");
|
||||
|
||||
/// <summary>
|
||||
/// Validates the configuration
|
||||
@@ -66,22 +65,17 @@ public sealed record ClientConfig
|
||||
{
|
||||
if (Id == Guid.Empty)
|
||||
{
|
||||
throw new InvalidOperationException("Client ID cannot be empty");
|
||||
throw new ValidationException("Client ID cannot be empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Name))
|
||||
{
|
||||
throw new InvalidOperationException($"Client name cannot be empty for client ID: {Id}");
|
||||
throw new ValidationException($"Client name cannot be empty for client ID: {Id}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Host))
|
||||
if (Host is null && Type is not DownloadClientType.Usenet)
|
||||
{
|
||||
throw new InvalidOperationException($"Host cannot be empty for client ID: {Id}");
|
||||
}
|
||||
|
||||
if (Type == DownloadClientType.None)
|
||||
{
|
||||
throw new InvalidOperationException($"Client type must be specified for client ID: {Id}");
|
||||
throw new ValidationException($"Host cannot be empty for client ID: {Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Common.Configuration.DownloadClient;
|
||||
using Common.Exceptions;
|
||||
|
||||
namespace Common.Configuration.DownloadClient;
|
||||
|
||||
public sealed record DownloadClientConfig : IConfig
|
||||
{
|
||||
@@ -32,34 +34,21 @@ public sealed record DownloadClientConfig : IConfig
|
||||
public void Validate()
|
||||
{
|
||||
// Validate clients have unique IDs
|
||||
var duplicateIds = Clients
|
||||
.GroupBy(c => c.Id)
|
||||
var duplicateNames = Clients
|
||||
.GroupBy(c => c.Name)
|
||||
.Where(g => g.Count() > 1)
|
||||
.Select(g => g.Key)
|
||||
.ToList();
|
||||
|
||||
if (duplicateIds.Any())
|
||||
if (duplicateNames.Any())
|
||||
{
|
||||
throw new InvalidOperationException($"Duplicate client IDs found: {string.Join(", ", duplicateIds)}");
|
||||
throw new ValidationException($"Duplicate client names found: {string.Join(", ", duplicateNames)}");
|
||||
}
|
||||
|
||||
// Validate each client configuration
|
||||
foreach (var client in Clients)
|
||||
{
|
||||
if (client.Id == Guid.Empty)
|
||||
{
|
||||
throw new InvalidOperationException("Client ID cannot be empty");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(client.Name))
|
||||
{
|
||||
throw new InvalidOperationException($"Client name cannot be empty for client ID: {client.Id}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(client.Host))
|
||||
{
|
||||
throw new InvalidOperationException($"Host cannot be empty for client ID: {client.Id}");
|
||||
}
|
||||
client.Validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,5 @@ public enum DownloadClientType
|
||||
QBittorrent,
|
||||
Deluge,
|
||||
Transmission,
|
||||
None,
|
||||
Disabled
|
||||
Usenet,
|
||||
}
|
||||
@@ -107,23 +107,24 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateQueueCleanerConfig([FromBody] QueueCleanerConfig dto)
|
||||
{
|
||||
// Get existing config
|
||||
var config = await _configManager.GetConfigurationAsync<QueueCleanerConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<QueueCleanerConfig>();
|
||||
|
||||
// Apply updates from DTO
|
||||
dto.Adapt(config);
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
var newConfig = oldConfig.Adapt<QueueCleanerConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save QueueCleaner configuration");
|
||||
}
|
||||
|
||||
// Update the scheduler based on configuration changes
|
||||
await UpdateJobSchedule(config, JobType.QueueCleaner);
|
||||
await UpdateJobSchedule(oldConfig, JobType.QueueCleaner);
|
||||
|
||||
return Ok(new { Message = "QueueCleaner configuration updated successfully" });
|
||||
}
|
||||
@@ -166,16 +167,17 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateContentBlockerConfig([FromBody] ContentBlockerConfigUpdateDto dto)
|
||||
{
|
||||
// Get existing config
|
||||
var config = await _configManager.GetConfigurationAsync<ContentBlockerConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<ContentBlockerConfig>();
|
||||
|
||||
// Apply updates from DTO
|
||||
dto.Adapt(config);
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
var newConfig = oldConfig.Adapt<ContentBlockerConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save ContentBlocker configuration");
|
||||
@@ -188,41 +190,43 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateDownloadCleanerConfig([FromBody] DownloadCleanerConfig dto)
|
||||
{
|
||||
// Get existing config
|
||||
var config = await _configManager.GetConfigurationAsync<DownloadCleanerConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<DownloadCleanerConfig>();
|
||||
|
||||
// Apply updates from DTO
|
||||
dto.Adapt(config);
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
var newConfig = oldConfig.Adapt<DownloadCleanerConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save DownloadCleaner configuration");
|
||||
}
|
||||
|
||||
// Update the scheduler based on configuration changes
|
||||
await UpdateJobSchedule(config, JobType.DownloadCleaner);
|
||||
await UpdateJobSchedule(oldConfig, JobType.DownloadCleaner);
|
||||
|
||||
return Ok(new { Message = "DownloadCleaner configuration updated successfully" });
|
||||
}
|
||||
|
||||
[HttpPut("download_client")]
|
||||
public async Task<IActionResult> UpdateDownloadClientConfig([FromBody] DownloadClientConfigUpdateDto dto)
|
||||
public async Task<IActionResult> UpdateDownloadClientConfig(DownloadClientConfigUpdateDto dto)
|
||||
{
|
||||
// Get existing config to preserve sensitive data
|
||||
var config = await _configManager.GetConfigurationAsync<DownloadClientConfig>();
|
||||
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<DownloadClientConfig>();
|
||||
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
dto.Adapt(config);
|
||||
var newConfig = oldConfig.Adapt<DownloadClientConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save DownloadClient configuration");
|
||||
@@ -235,24 +239,24 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateGeneralConfig([FromBody] GeneralConfig dto)
|
||||
{
|
||||
// Get existing config to preserve sensitive data
|
||||
var config = await _configManager.GetConfigurationAsync<GeneralConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<GeneralConfig>();
|
||||
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
dto.Adapt(config);
|
||||
var newConfig = oldConfig.Adapt<GeneralConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
|
||||
_loggingConfigManager.SetLogLevel(config.LogLevel);
|
||||
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save General configuration");
|
||||
}
|
||||
|
||||
_loggingConfigManager.SetLogLevel(oldConfig.LogLevel);
|
||||
|
||||
return Ok(new { Message = "General configuration updated successfully" });
|
||||
}
|
||||
|
||||
@@ -260,16 +264,17 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateSonarrConfig([FromBody] SonarrConfigUpdateDto dto)
|
||||
{
|
||||
// Get existing config to preserve sensitive data
|
||||
var config = await _configManager.GetConfigurationAsync<SonarrConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<SonarrConfig>();
|
||||
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
dto.Adapt(config);
|
||||
var newConfig = oldConfig.Adapt<SonarrConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save Sonarr configuration");
|
||||
@@ -282,16 +287,17 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateRadarrConfig([FromBody] RadarrConfigUpdateDto dto)
|
||||
{
|
||||
// Get existing config to preserve sensitive data
|
||||
var config = await _configManager.GetConfigurationAsync<RadarrConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<RadarrConfig>();
|
||||
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
dto.Adapt(config);
|
||||
var newConfig = oldConfig.Adapt<RadarrConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save Radarr configuration");
|
||||
@@ -304,16 +310,17 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateLidarrConfig([FromBody] LidarrConfigUpdateDto dto)
|
||||
{
|
||||
// Get existing config to preserve sensitive data
|
||||
var config = await _configManager.GetConfigurationAsync<LidarrConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<LidarrConfig>();
|
||||
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
dto.Adapt(config);
|
||||
var newConfig = oldConfig.Adapt<LidarrConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
config.Validate();
|
||||
newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save Lidarr configuration");
|
||||
@@ -326,13 +333,17 @@ public class ConfigurationController : ControllerBase
|
||||
public async Task<IActionResult> UpdateNotificationsConfig([FromBody] NotificationsConfigUpdateDto dto)
|
||||
{
|
||||
// Get existing config to preserve sensitive data
|
||||
var config = await _configManager.GetConfigurationAsync<NotificationsConfig>();
|
||||
var oldConfig = await _configManager.GetConfigurationAsync<NotificationsConfig>();
|
||||
|
||||
// Apply updates from DTO, preserving sensitive data if not provided
|
||||
dto.Adapt(config);
|
||||
var newConfig = oldConfig.Adapt<NotificationsConfig>();
|
||||
newConfig = dto.Adapt(newConfig);
|
||||
|
||||
// Validate the configuration
|
||||
// newConfig.Validate();
|
||||
|
||||
// Persist the configuration
|
||||
var result = await _configManager.SaveConfigurationAsync(config);
|
||||
var result = await _configManager.SaveConfigurationAsync(newConfig);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, "Failed to save Notifications configuration");
|
||||
|
||||
@@ -38,7 +38,7 @@ public class DynamicHttpClientProviderFixture : IDisposable
|
||||
Name = "QBit Test",
|
||||
Type = DownloadClientType.QBittorrent,
|
||||
Enabled = true,
|
||||
Host = "http://localhost:8080",
|
||||
Host = new("http://localhost:8080"),
|
||||
Username = "admin",
|
||||
Password = "adminadmin"
|
||||
};
|
||||
@@ -52,7 +52,7 @@ public class DynamicHttpClientProviderFixture : IDisposable
|
||||
Name = "Transmission Test",
|
||||
Type = DownloadClientType.Transmission,
|
||||
Enabled = true,
|
||||
Host = "http://localhost:9091",
|
||||
Host = new("http://localhost:9091"),
|
||||
Username = "admin",
|
||||
Password = "adminadmin",
|
||||
UrlBase = "transmission"
|
||||
@@ -67,7 +67,7 @@ public class DynamicHttpClientProviderFixture : IDisposable
|
||||
Name = "Deluge Test",
|
||||
Type = DownloadClientType.Deluge,
|
||||
Enabled = true,
|
||||
Host = "http://localhost:8112",
|
||||
Host = new("http://localhost:8112"),
|
||||
Username = "admin",
|
||||
Password = "deluge"
|
||||
};
|
||||
|
||||
@@ -71,9 +71,6 @@ public abstract class DownloadService : IDownloadService
|
||||
_cacheOptions = new MemoryCacheEntryOptions()
|
||||
.SetSlidingExpiration(StaticConfiguration.TriggerValue + Constants.CacheLimitBuffer);
|
||||
|
||||
// Initialize with default empty configuration
|
||||
_clientConfig = new ClientConfig();
|
||||
|
||||
_queueCleanerConfig = _configManager.GetConfiguration<QueueCleanerConfig>();
|
||||
_downloadCleanerConfig = _configManager.GetConfiguration<DownloadCleanerConfig>();
|
||||
}
|
||||
|
||||
@@ -69,35 +69,43 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instance-field">
|
||||
<label>Host</label>
|
||||
<div class="field-input">
|
||||
<input type="text" pInputText formControlName="host" placeholder="http://localhost:8080" required />
|
||||
<small *ngIf="hasClientFieldError(i, 'host', 'required')" class="p-error block">Host is required</small>
|
||||
<small *ngIf="hasClientFieldError(i, 'host', 'invalidUri')" class="p-error block">Host must be a valid URL</small>
|
||||
<small *ngIf="hasClientFieldError(i, 'host', 'invalidProtocol')" class="p-error block">Host must use http or https protocol</small>
|
||||
<!-- Connection fields - only shown when client type is not Usenet -->
|
||||
<ng-container *ngIf="!isUsenetClient(client.get('type')?.value)">
|
||||
<div class="instance-field">
|
||||
<label>Host</label>
|
||||
<div class="field-input">
|
||||
<input type="text" pInputText formControlName="host" placeholder="http://localhost:8080" required />
|
||||
<small *ngIf="hasClientFieldError(i, 'host', 'required')" class="p-error block">Host is required</small>
|
||||
<small *ngIf="hasClientFieldError(i, 'host', 'invalidUri')" class="p-error block">Host must be a valid URL</small>
|
||||
<small *ngIf="hasClientFieldError(i, 'host', 'invalidProtocol')" class="p-error block">Host must use http or https protocol</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instance-field">
|
||||
<label>URL Base</label>
|
||||
<div class="field-input">
|
||||
<input type="text" pInputText formControlName="urlBase" placeholder="(Optional) Path prefix" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instance-field">
|
||||
<label>Username</label>
|
||||
<div class="field-input">
|
||||
<input type="text" pInputText formControlName="username" placeholder="Username" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instance-field">
|
||||
<label>Password</label>
|
||||
<div class="field-input">
|
||||
<input type="password" pInputText formControlName="password" placeholder="Password" />
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="instance-field">
|
||||
<label>URL Base</label>
|
||||
<div class="field-input">
|
||||
<input type="text" pInputText formControlName="urlBase" placeholder="(Optional) Path prefix" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instance-field">
|
||||
<label>Username</label>
|
||||
<div class="field-input">
|
||||
<input type="text" pInputText formControlName="username" placeholder="Username" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instance-field">
|
||||
<label>Password</label>
|
||||
<div class="field-input">
|
||||
<input type="password" pInputText formControlName="password" placeholder="Password" />
|
||||
</div>
|
||||
<!-- Message shown when Usenet is selected -->
|
||||
<div *ngIf="isUsenetClient(client.get('type')?.value)" class="p-3 mt-2 surface-overlay border-1 border-round-sm">
|
||||
<small class="text-secondary">Usenet client type is for categorization only. No connection details needed.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,8 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
|
||||
clientTypeOptions = [
|
||||
{ label: "QBittorrent", value: DownloadClientType.QBittorrent },
|
||||
{ label: "Deluge", value: DownloadClientType.Deluge },
|
||||
{ label: "Transmission", value: DownloadClientType.Transmission }
|
||||
{ label: "Transmission", value: DownloadClientType.Transmission },
|
||||
{ label: "Usenet", value: DownloadClientType.Usenet }
|
||||
];
|
||||
|
||||
// Clean up subscriptions
|
||||
@@ -255,20 +256,35 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
|
||||
*/
|
||||
addClient(client: ClientConfig | null = null): void {
|
||||
const clientsArray = this.downloadClientForm.get('clients') as FormArray;
|
||||
const clientType = client?.type ?? DownloadClientType.QBittorrent;
|
||||
const isUsenet = clientType === DownloadClientType.Usenet;
|
||||
|
||||
clientsArray.push(
|
||||
this.formBuilder.group({
|
||||
enabled: [client?.enabled ?? true],
|
||||
id: [client?.id ?? ''],
|
||||
name: [client?.name ?? '', Validators.required],
|
||||
type: [client?.type ?? DownloadClientType.QBittorrent, Validators.required],
|
||||
host: [client?.host ?? '', [Validators.required, this.uriValidator]],
|
||||
username: [client?.username ?? ''],
|
||||
password: [client?.password ?? ''],
|
||||
urlBase: [client?.urlBase ?? '']
|
||||
})
|
||||
);
|
||||
// Create the client form group with conditional validators based on client type
|
||||
const clientFormGroup = this.formBuilder.group({
|
||||
enabled: [client?.enabled ?? true],
|
||||
id: [client?.id ?? ''],
|
||||
name: [client?.name ?? '', Validators.required],
|
||||
type: [clientType, Validators.required],
|
||||
host: [client?.host ?? '', isUsenet ? [] : [Validators.required, this.uriValidator]],
|
||||
username: [client?.username ?? ''],
|
||||
password: [client?.password ?? ''],
|
||||
urlBase: [client?.urlBase ?? '']
|
||||
});
|
||||
|
||||
// Set up subscription to type changes to update validators
|
||||
const typeControl = clientFormGroup.get('type');
|
||||
if (typeControl) {
|
||||
typeControl.valueChanges.pipe(
|
||||
takeUntil(this.destroy$)
|
||||
).subscribe(newType => {
|
||||
// Only update validators if newType is not null
|
||||
if (newType !== null) {
|
||||
this.updateValidatorsForClientType(clientFormGroup, newType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clientsArray.push(clientFormGroup);
|
||||
this.downloadClientForm.markAsDirty();
|
||||
// Recalculate if actual changes exist by comparing with original values
|
||||
this.hasActualChanges = this.formValuesChanged();
|
||||
@@ -361,4 +377,30 @@ export class DownloadClientSettingsComponent implements OnDestroy, CanComponentD
|
||||
return { invalidUri: true }; // Invalid URI
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a client type is Usenet
|
||||
*/
|
||||
isUsenetClient(clientType: DownloadClientType | null | undefined): boolean {
|
||||
return clientType === DownloadClientType.Usenet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update validators for a client form group based on the client type
|
||||
*/
|
||||
private updateValidatorsForClientType(clientFormGroup: FormGroup, clientType: DownloadClientType): void {
|
||||
const hostControl = clientFormGroup.get('host');
|
||||
if (!hostControl) return;
|
||||
|
||||
if (clientType === DownloadClientType.Usenet) {
|
||||
// For Usenet, remove all validators
|
||||
hostControl.clearValidators();
|
||||
} else {
|
||||
// For other client types, add required and URI validators
|
||||
hostControl.setValidators([Validators.required, this.uriValidator]);
|
||||
}
|
||||
|
||||
// Update validation state
|
||||
hostControl.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,5 @@ export enum DownloadClientType {
|
||||
QBittorrent = 0,
|
||||
Deluge = 1,
|
||||
Transmission = 2,
|
||||
None = 3,
|
||||
Disabled = 4
|
||||
Usenet = 3,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user