diff --git a/code/backend/Cleanuparr.Api/Controllers/ConfigurationController.cs b/code/backend/Cleanuparr.Api/Controllers/ConfigurationController.cs index 0d396898..068bfe65 100644 --- a/code/backend/Cleanuparr.Api/Controllers/ConfigurationController.cs +++ b/code/backend/Cleanuparr.Api/Controllers/ConfigurationController.cs @@ -3,7 +3,6 @@ using Cleanuparr.Application.Features.Arr.Dtos; using Cleanuparr.Application.Features.DownloadClient.Dtos; using Cleanuparr.Domain.Enums; using Cleanuparr.Domain.Exceptions; -using Cleanuparr.Infrastructure.Helpers; using Cleanuparr.Infrastructure.Http.DynamicHttpClientSystem; using Cleanuparr.Infrastructure.Logging; using Cleanuparr.Infrastructure.Models; diff --git a/code/backend/Cleanuparr.Api/DependencyInjection/MainDI.cs b/code/backend/Cleanuparr.Api/DependencyInjection/MainDI.cs index 51d8f9a4..644677d5 100644 --- a/code/backend/Cleanuparr.Api/DependencyInjection/MainDI.cs +++ b/code/backend/Cleanuparr.Api/DependencyInjection/MainDI.cs @@ -1,11 +1,11 @@ using System.Text.Json.Serialization; using Cleanuparr.Infrastructure.Features.DownloadRemover.Consumers; using Cleanuparr.Infrastructure.Features.Notifications.Consumers; +using Cleanuparr.Infrastructure.Features.Notifications.Models; using Cleanuparr.Infrastructure.Health; using Cleanuparr.Infrastructure.Http; using Cleanuparr.Infrastructure.Http.DynamicHttpClientSystem; using Data.Models.Arr; -using Infrastructure.Verticals.Notifications.Models; using MassTransit; using Microsoft.Extensions.Caching.Memory; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProvider.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProvider.cs index 8a86c4d1..246ecc63 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProvider.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProvider.cs @@ -3,13 +3,11 @@ using Cleanuparr.Infrastructure.Features.Notifications.Models; using Cleanuparr.Persistence; using Cleanuparr.Persistence.Models.Configuration.Notification; using Infrastructure.Verticals.Notifications; -using Infrastructure.Verticals.Notifications.Models; namespace Cleanuparr.Infrastructure.Features.Notifications.Apprise; public sealed class AppriseProvider : NotificationProvider { - private readonly DataContext _dataContext; private readonly IAppriseProxy _proxy; public override string Name => "Apprise"; @@ -17,7 +15,6 @@ public sealed class AppriseProvider : NotificationProvider public AppriseProvider(DataContext dataContext, IAppriseProxy proxy) : base(dataContext.AppriseConfigs) { - _dataContext = dataContext; _proxy = proxy; } diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProxy.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProxy.cs index bd9fc740..7dc578d5 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProxy.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Apprise/AppriseProxy.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Net.Http.Headers; +using System.Text; using Cleanuparr.Persistence.Models.Configuration.Notification; using Cleanuparr.Shared.Helpers; using Newtonsoft.Json; @@ -22,12 +23,18 @@ public sealed class AppriseProxy : IAppriseProxy ContractResolver = new CamelCasePropertyNamesContractResolver() }); - UriBuilder uriBuilder = new(config.Url); + UriBuilder uriBuilder = new(config.Url.ToString()); uriBuilder.Path = $"{uriBuilder.Path.TrimEnd('/')}/notify/{config.Key}"; using HttpRequestMessage request = new(HttpMethod.Post, uriBuilder.Uri); request.Method = HttpMethod.Post; request.Content = new StringContent(content, Encoding.UTF8, "application/json"); + + if (!string.IsNullOrEmpty(config.Url.UserInfo)) + { + var byteArray = Encoding.ASCII.GetBytes(config.Url.UserInfo); + request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + } using HttpResponseMessage response = await _httpClient.SendAsync(request); response.EnsureSuccessStatusCode(); diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Consumers/NotificationConsumer.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Consumers/NotificationConsumer.cs index 55c36fb3..d662c612 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Consumers/NotificationConsumer.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Consumers/NotificationConsumer.cs @@ -1,5 +1,5 @@ -using Infrastructure.Verticals.Notifications; -using Infrastructure.Verticals.Notifications.Models; +using Cleanuparr.Infrastructure.Features.Notifications.Models; +using Infrastructure.Verticals.Notifications; using MassTransit; using Microsoft.Extensions.Logging; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/INotificationProvider.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/INotificationProvider.cs index 437af5b6..58f0a75f 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/INotificationProvider.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/INotificationProvider.cs @@ -1,5 +1,5 @@ +using Cleanuparr.Infrastructure.Features.Notifications.Models; using Cleanuparr.Persistence.Models.Configuration.Notification; -using Infrastructure.Verticals.Notifications.Models; namespace Cleanuparr.Infrastructure.Features.Notifications; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/ArrNotification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/ArrNotification.cs index 0ee5ed80..a345ddf2 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/ArrNotification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/ArrNotification.cs @@ -1,5 +1,4 @@ using Cleanuparr.Domain.Enums; -using Infrastructure.Verticals.Notifications.Models; namespace Cleanuparr.Infrastructure.Features.Notifications.Models; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/CategoryChangedNotification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/CategoryChangedNotification.cs index 4da1dc8d..bd976006 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/CategoryChangedNotification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/CategoryChangedNotification.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public sealed record CategoryChangedNotification : Notification { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/DownloadCleanedNotification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/DownloadCleanedNotification.cs index 1203ce6c..d67823a2 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/DownloadCleanedNotification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/DownloadCleanedNotification.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public sealed record DownloadCleanedNotification : Notification { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/FailedImportStrikeNotification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/FailedImportStrikeNotification.cs index 0c24c85e..6be11b90 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/FailedImportStrikeNotification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/FailedImportStrikeNotification.cs @@ -1,6 +1,4 @@ -using Cleanuparr.Infrastructure.Features.Notifications.Models; - -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public sealed record FailedImportStrikeNotification : ArrNotification { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/Notification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/Notification.cs index 2a93022d..bf77c612 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/Notification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/Notification.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public abstract record Notification { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationField.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationField.cs index 1d8621f1..91492e17 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationField.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationField.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public sealed record NotificationField { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationLevel.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationLevel.cs index a2106128..2caed5ec 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationLevel.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/NotificationLevel.cs @@ -1,4 +1,4 @@ -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public enum NotificationLevel { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/QueueItemDeletedNotification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/QueueItemDeletedNotification.cs index 656ff236..db96975f 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/QueueItemDeletedNotification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/QueueItemDeletedNotification.cs @@ -1,6 +1,4 @@ -using Cleanuparr.Infrastructure.Features.Notifications.Models; - -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public sealed record QueueItemDeletedNotification : ArrNotification { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/SlowStrikeNotification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/SlowStrikeNotification.cs index 030de617..1c782929 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/SlowStrikeNotification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/SlowStrikeNotification.cs @@ -1,6 +1,4 @@ -using Cleanuparr.Infrastructure.Features.Notifications.Models; - -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public sealed record SlowStrikeNotification : ArrNotification { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/StalledStrikeNotification.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/StalledStrikeNotification.cs index 34bed2e7..6e67e3a9 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/StalledStrikeNotification.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Models/StalledStrikeNotification.cs @@ -1,6 +1,4 @@ -using Cleanuparr.Infrastructure.Features.Notifications.Models; - -namespace Infrastructure.Verticals.Notifications.Models; +namespace Cleanuparr.Infrastructure.Features.Notifications.Models; public sealed record StalledStrikeNotification : ArrNotification { diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Notifiarr/NotifiarrProvider.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Notifiarr/NotifiarrProvider.cs index 72ad02ff..35199167 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Notifiarr/NotifiarrProvider.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/Notifiarr/NotifiarrProvider.cs @@ -2,7 +2,6 @@ using Cleanuparr.Infrastructure.Features.Notifications.Models; using Cleanuparr.Persistence; using Cleanuparr.Persistence.Models.Configuration.Notification; using Infrastructure.Verticals.Notifications; -using Infrastructure.Verticals.Notifications.Models; using Mapster; namespace Cleanuparr.Infrastructure.Features.Notifications.Notifiarr; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationProvider.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationProvider.cs index e11c9806..4c644c4e 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationProvider.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationProvider.cs @@ -1,5 +1,5 @@ +using Cleanuparr.Infrastructure.Features.Notifications.Models; using Cleanuparr.Persistence.Models.Configuration.Notification; -using Infrastructure.Verticals.Notifications.Models; using Microsoft.EntityFrameworkCore; namespace Cleanuparr.Infrastructure.Features.Notifications; @@ -10,7 +10,7 @@ public abstract class NotificationProvider : INotificationProvider protected readonly DbSet _notificationConfig; protected T? _config; - public T Config => _config ??= _notificationConfig.First(); + public T Config => _config ??= _notificationConfig.AsNoTracking().First(); NotificationConfig INotificationProvider.Config => Config; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationPublisher.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationPublisher.cs index 41fcd1db..86a86448 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationPublisher.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationPublisher.cs @@ -6,7 +6,6 @@ using Cleanuparr.Infrastructure.Features.Notifications.Models; using Cleanuparr.Persistence.Models.Configuration.Arr; using Data.Models.Arr.Queue; using Infrastructure.Interceptors; -using Infrastructure.Verticals.Notifications.Models; using Mapster; using MassTransit; using Microsoft.Extensions.Logging; diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationService.cs b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationService.cs index 1c954a79..7fe398e6 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationService.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationService.cs @@ -1,5 +1,5 @@ using Cleanuparr.Infrastructure.Features.Notifications; -using Infrastructure.Verticals.Notifications.Models; +using Cleanuparr.Infrastructure.Features.Notifications.Models; using Microsoft.Extensions.Logging; namespace Infrastructure.Verticals.Notifications; diff --git a/code/backend/Cleanuparr.Persistence/Migrations/Data/20250702192200_ChangeAppriseConfig.Designer.cs b/code/backend/Cleanuparr.Persistence/Migrations/Data/20250702192200_ChangeAppriseConfig.Designer.cs new file mode 100644 index 00000000..b0970a97 --- /dev/null +++ b/code/backend/Cleanuparr.Persistence/Migrations/Data/20250702192200_ChangeAppriseConfig.Designer.cs @@ -0,0 +1,620 @@ +// +using System; +using System.Collections.Generic; +using Cleanuparr.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Cleanuparr.Persistence.Migrations.Data +{ + [DbContext(typeof(DataContext))] + [Migration("20250702192200_ChangeAppriseConfig")] + partial class ChangeAppriseConfig + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "9.0.6"); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.Arr.ArrConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("FailedImportMaxStrikes") + .HasColumnType("INTEGER") + .HasColumnName("failed_import_max_strikes"); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("type"); + + b.HasKey("Id") + .HasName("pk_arr_configs"); + + b.ToTable("arr_configs", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.Arr.ArrInstance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("api_key"); + + b.Property("ArrConfigId") + .HasColumnType("TEXT") + .HasColumnName("arr_config_id"); + + b.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("enabled"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.Property("Url") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("url"); + + b.HasKey("Id") + .HasName("pk_arr_instances"); + + b.HasIndex("ArrConfigId") + .HasDatabaseName("ix_arr_instances_arr_config_id"); + + b.ToTable("arr_instances", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.ContentBlocker.ContentBlockerConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("CronExpression") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("cron_expression"); + + b.Property("DeletePrivate") + .HasColumnType("INTEGER") + .HasColumnName("delete_private"); + + b.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("enabled"); + + b.Property("IgnorePrivate") + .HasColumnType("INTEGER") + .HasColumnName("ignore_private"); + + b.Property("UseAdvancedScheduling") + .HasColumnType("INTEGER") + .HasColumnName("use_advanced_scheduling"); + + b.ComplexProperty>("Lidarr", "Cleanuparr.Persistence.Models.Configuration.ContentBlocker.ContentBlockerConfig.Lidarr#BlocklistSettings", b1 => + { + b1.IsRequired(); + + b1.Property("BlocklistPath") + .HasColumnType("TEXT") + .HasColumnName("lidarr_blocklist_path"); + + b1.Property("BlocklistType") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("lidarr_blocklist_type"); + + b1.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("lidarr_enabled"); + }); + + b.ComplexProperty>("Radarr", "Cleanuparr.Persistence.Models.Configuration.ContentBlocker.ContentBlockerConfig.Radarr#BlocklistSettings", b1 => + { + b1.IsRequired(); + + b1.Property("BlocklistPath") + .HasColumnType("TEXT") + .HasColumnName("radarr_blocklist_path"); + + b1.Property("BlocklistType") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("radarr_blocklist_type"); + + b1.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("radarr_enabled"); + }); + + b.ComplexProperty>("Readarr", "Cleanuparr.Persistence.Models.Configuration.ContentBlocker.ContentBlockerConfig.Readarr#BlocklistSettings", b1 => + { + b1.IsRequired(); + + b1.Property("BlocklistPath") + .HasColumnType("TEXT") + .HasColumnName("readarr_blocklist_path"); + + b1.Property("BlocklistType") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("readarr_blocklist_type"); + + b1.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("readarr_enabled"); + }); + + b.ComplexProperty>("Sonarr", "Cleanuparr.Persistence.Models.Configuration.ContentBlocker.ContentBlockerConfig.Sonarr#BlocklistSettings", b1 => + { + b1.IsRequired(); + + b1.Property("BlocklistPath") + .HasColumnType("TEXT") + .HasColumnName("sonarr_blocklist_path"); + + b1.Property("BlocklistType") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("sonarr_blocklist_type"); + + b1.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("sonarr_enabled"); + }); + + b.HasKey("Id") + .HasName("pk_content_blocker_configs"); + + b.ToTable("content_blocker_configs", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.DownloadCleaner.CleanCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("DownloadCleanerConfigId") + .HasColumnType("TEXT") + .HasColumnName("download_cleaner_config_id"); + + b.Property("MaxRatio") + .HasColumnType("REAL") + .HasColumnName("max_ratio"); + + b.Property("MaxSeedTime") + .HasColumnType("REAL") + .HasColumnName("max_seed_time"); + + b.Property("MinSeedTime") + .HasColumnType("REAL") + .HasColumnName("min_seed_time"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_clean_categories"); + + b.HasIndex("DownloadCleanerConfigId") + .HasDatabaseName("ix_clean_categories_download_cleaner_config_id"); + + b.ToTable("clean_categories", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.DownloadCleaner.DownloadCleanerConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("CronExpression") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("cron_expression"); + + b.Property("DeletePrivate") + .HasColumnType("INTEGER") + .HasColumnName("delete_private"); + + b.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("enabled"); + + b.PrimitiveCollection("UnlinkedCategories") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("unlinked_categories"); + + b.Property("UnlinkedEnabled") + .HasColumnType("INTEGER") + .HasColumnName("unlinked_enabled"); + + b.Property("UnlinkedIgnoredRootDir") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("unlinked_ignored_root_dir"); + + b.Property("UnlinkedTargetCategory") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("unlinked_target_category"); + + b.Property("UnlinkedUseTag") + .HasColumnType("INTEGER") + .HasColumnName("unlinked_use_tag"); + + b.Property("UseAdvancedScheduling") + .HasColumnType("INTEGER") + .HasColumnName("use_advanced_scheduling"); + + b.HasKey("Id") + .HasName("pk_download_cleaner_configs"); + + b.ToTable("download_cleaner_configs", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.DownloadClientConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("enabled"); + + b.Property("Host") + .HasColumnType("TEXT") + .HasColumnName("host"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.Property("Password") + .HasColumnType("TEXT") + .HasColumnName("password"); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("type"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("type_name"); + + b.Property("UrlBase") + .HasColumnType("TEXT") + .HasColumnName("url_base"); + + b.Property("Username") + .HasColumnType("TEXT") + .HasColumnName("username"); + + b.HasKey("Id") + .HasName("pk_download_clients"); + + b.ToTable("download_clients", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.General.GeneralConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("DisplaySupportBanner") + .HasColumnType("INTEGER") + .HasColumnName("display_support_banner"); + + b.Property("DryRun") + .HasColumnType("INTEGER") + .HasColumnName("dry_run"); + + b.Property("EncryptionKey") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("encryption_key"); + + b.Property("HttpCertificateValidation") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("http_certificate_validation"); + + b.Property("HttpMaxRetries") + .HasColumnType("INTEGER") + .HasColumnName("http_max_retries"); + + b.Property("HttpTimeout") + .HasColumnType("INTEGER") + .HasColumnName("http_timeout"); + + b.PrimitiveCollection("IgnoredDownloads") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("ignored_downloads"); + + b.Property("LogLevel") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("log_level"); + + b.Property("SearchDelay") + .HasColumnType("INTEGER") + .HasColumnName("search_delay"); + + b.Property("SearchEnabled") + .HasColumnType("INTEGER") + .HasColumnName("search_enabled"); + + b.HasKey("Id") + .HasName("pk_general_configs"); + + b.ToTable("general_configs", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.Notification.AppriseConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("FullUrl") + .HasColumnType("TEXT") + .HasColumnName("full_url"); + + b.Property("Key") + .HasColumnType("TEXT") + .HasColumnName("key"); + + b.Property("OnCategoryChanged") + .HasColumnType("INTEGER") + .HasColumnName("on_category_changed"); + + b.Property("OnDownloadCleaned") + .HasColumnType("INTEGER") + .HasColumnName("on_download_cleaned"); + + b.Property("OnFailedImportStrike") + .HasColumnType("INTEGER") + .HasColumnName("on_failed_import_strike"); + + b.Property("OnQueueItemDeleted") + .HasColumnType("INTEGER") + .HasColumnName("on_queue_item_deleted"); + + b.Property("OnSlowStrike") + .HasColumnType("INTEGER") + .HasColumnName("on_slow_strike"); + + b.Property("OnStalledStrike") + .HasColumnType("INTEGER") + .HasColumnName("on_stalled_strike"); + + b.HasKey("Id") + .HasName("pk_apprise_configs"); + + b.ToTable("apprise_configs", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.Notification.NotifiarrConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("ApiKey") + .HasColumnType("TEXT") + .HasColumnName("api_key"); + + b.Property("ChannelId") + .HasColumnType("TEXT") + .HasColumnName("channel_id"); + + b.Property("OnCategoryChanged") + .HasColumnType("INTEGER") + .HasColumnName("on_category_changed"); + + b.Property("OnDownloadCleaned") + .HasColumnType("INTEGER") + .HasColumnName("on_download_cleaned"); + + b.Property("OnFailedImportStrike") + .HasColumnType("INTEGER") + .HasColumnName("on_failed_import_strike"); + + b.Property("OnQueueItemDeleted") + .HasColumnType("INTEGER") + .HasColumnName("on_queue_item_deleted"); + + b.Property("OnSlowStrike") + .HasColumnType("INTEGER") + .HasColumnName("on_slow_strike"); + + b.Property("OnStalledStrike") + .HasColumnType("INTEGER") + .HasColumnName("on_stalled_strike"); + + b.HasKey("Id") + .HasName("pk_notifiarr_configs"); + + b.ToTable("notifiarr_configs", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.QueueCleaner.QueueCleanerConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.Property("CronExpression") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("cron_expression"); + + b.Property("Enabled") + .HasColumnType("INTEGER") + .HasColumnName("enabled"); + + b.Property("UseAdvancedScheduling") + .HasColumnType("INTEGER") + .HasColumnName("use_advanced_scheduling"); + + b.ComplexProperty>("FailedImport", "Cleanuparr.Persistence.Models.Configuration.QueueCleaner.QueueCleanerConfig.FailedImport#FailedImportConfig", b1 => + { + b1.IsRequired(); + + b1.Property("DeletePrivate") + .HasColumnType("INTEGER") + .HasColumnName("failed_import_delete_private"); + + b1.Property("IgnorePrivate") + .HasColumnType("INTEGER") + .HasColumnName("failed_import_ignore_private"); + + b1.PrimitiveCollection("IgnoredPatterns") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("failed_import_ignored_patterns"); + + b1.Property("MaxStrikes") + .HasColumnType("INTEGER") + .HasColumnName("failed_import_max_strikes"); + }); + + b.ComplexProperty>("Slow", "Cleanuparr.Persistence.Models.Configuration.QueueCleaner.QueueCleanerConfig.Slow#SlowConfig", b1 => + { + b1.IsRequired(); + + b1.Property("DeletePrivate") + .HasColumnType("INTEGER") + .HasColumnName("slow_delete_private"); + + b1.Property("IgnoreAboveSize") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("slow_ignore_above_size"); + + b1.Property("IgnorePrivate") + .HasColumnType("INTEGER") + .HasColumnName("slow_ignore_private"); + + b1.Property("MaxStrikes") + .HasColumnType("INTEGER") + .HasColumnName("slow_max_strikes"); + + b1.Property("MaxTime") + .HasColumnType("REAL") + .HasColumnName("slow_max_time"); + + b1.Property("MinSpeed") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("slow_min_speed"); + + b1.Property("ResetStrikesOnProgress") + .HasColumnType("INTEGER") + .HasColumnName("slow_reset_strikes_on_progress"); + }); + + b.ComplexProperty>("Stalled", "Cleanuparr.Persistence.Models.Configuration.QueueCleaner.QueueCleanerConfig.Stalled#StalledConfig", b1 => + { + b1.IsRequired(); + + b1.Property("DeletePrivate") + .HasColumnType("INTEGER") + .HasColumnName("stalled_delete_private"); + + b1.Property("DownloadingMetadataMaxStrikes") + .HasColumnType("INTEGER") + .HasColumnName("stalled_downloading_metadata_max_strikes"); + + b1.Property("IgnorePrivate") + .HasColumnType("INTEGER") + .HasColumnName("stalled_ignore_private"); + + b1.Property("MaxStrikes") + .HasColumnType("INTEGER") + .HasColumnName("stalled_max_strikes"); + + b1.Property("ResetStrikesOnProgress") + .HasColumnType("INTEGER") + .HasColumnName("stalled_reset_strikes_on_progress"); + }); + + b.HasKey("Id") + .HasName("pk_queue_cleaner_configs"); + + b.ToTable("queue_cleaner_configs", (string)null); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.Arr.ArrInstance", b => + { + b.HasOne("Cleanuparr.Persistence.Models.Configuration.Arr.ArrConfig", "ArrConfig") + .WithMany("Instances") + .HasForeignKey("ArrConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_arr_instances_arr_configs_arr_config_id"); + + b.Navigation("ArrConfig"); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.DownloadCleaner.CleanCategory", b => + { + b.HasOne("Cleanuparr.Persistence.Models.Configuration.DownloadCleaner.DownloadCleanerConfig", "DownloadCleanerConfig") + .WithMany("Categories") + .HasForeignKey("DownloadCleanerConfigId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_clean_categories_download_cleaner_configs_download_cleaner_config_id"); + + b.Navigation("DownloadCleanerConfig"); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.Arr.ArrConfig", b => + { + b.Navigation("Instances"); + }); + + modelBuilder.Entity("Cleanuparr.Persistence.Models.Configuration.DownloadCleaner.DownloadCleanerConfig", b => + { + b.Navigation("Categories"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/code/backend/Cleanuparr.Persistence/Migrations/Data/20250702192200_ChangeAppriseConfig.cs b/code/backend/Cleanuparr.Persistence/Migrations/Data/20250702192200_ChangeAppriseConfig.cs new file mode 100644 index 00000000..262228ec --- /dev/null +++ b/code/backend/Cleanuparr.Persistence/Migrations/Data/20250702192200_ChangeAppriseConfig.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Cleanuparr.Persistence.Migrations.Data +{ + /// + public partial class ChangeAppriseConfig : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "url", + table: "apprise_configs", + newName: "full_url"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "full_url", + table: "apprise_configs", + newName: "url"); + } + } +} diff --git a/code/backend/Cleanuparr.Persistence/Migrations/Data/DataContextModelSnapshot.cs b/code/backend/Cleanuparr.Persistence/Migrations/Data/DataContextModelSnapshot.cs index 7f9ecd9b..2d4e2747 100644 --- a/code/backend/Cleanuparr.Persistence/Migrations/Data/DataContextModelSnapshot.cs +++ b/code/backend/Cleanuparr.Persistence/Migrations/Data/DataContextModelSnapshot.cs @@ -387,6 +387,10 @@ namespace Cleanuparr.Persistence.Migrations.Data .HasColumnType("TEXT") .HasColumnName("id"); + b.Property("FullUrl") + .HasColumnType("TEXT") + .HasColumnName("full_url"); + b.Property("Key") .HasColumnType("TEXT") .HasColumnName("key"); @@ -415,10 +419,6 @@ namespace Cleanuparr.Persistence.Migrations.Data .HasColumnType("INTEGER") .HasColumnName("on_stalled_strike"); - b.Property("Url") - .HasColumnType("TEXT") - .HasColumnName("url"); - b.HasKey("Id") .HasName("pk_apprise_configs"); diff --git a/code/backend/Cleanuparr.Persistence/Models/Configuration/Notification/AppriseConfig.cs b/code/backend/Cleanuparr.Persistence/Models/Configuration/Notification/AppriseConfig.cs index fbbd7de0..a3e6ad87 100644 --- a/code/backend/Cleanuparr.Persistence/Models/Configuration/Notification/AppriseConfig.cs +++ b/code/backend/Cleanuparr.Persistence/Models/Configuration/Notification/AppriseConfig.cs @@ -1,10 +1,15 @@ -namespace Cleanuparr.Persistence.Models.Configuration.Notification; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Cleanuparr.Persistence.Models.Configuration.Notification; public sealed record AppriseConfig : NotificationConfig { - public Uri? Url { get; init; } + public string? FullUrl { get; set; } - public string? Key { get; init; } + [NotMapped] + public Uri? Url => string.IsNullOrEmpty(FullUrl) ? null : new Uri(FullUrl, UriKind.Absolute); + + public string? Key { get; set; } public override bool IsValid() { diff --git a/code/frontend/src/app/core/services/documentation.service.ts b/code/frontend/src/app/core/services/documentation.service.ts index 81706361..e8c541b4 100644 --- a/code/frontend/src/app/core/services/documentation.service.ts +++ b/code/frontend/src/app/core/services/documentation.service.ts @@ -93,7 +93,7 @@ export class DocumentationService { 'notifications': { 'notifiarr.apiKey': 'notifiarr-api-key', 'notifiarr.channelId': 'notifiarr-channel-id', - 'apprise.url': 'apprise-url', + 'apprise.fullUrl': 'apprise-url', 'apprise.key': 'apprise-key', 'eventTriggers': 'event-triggers' } diff --git a/code/frontend/src/app/settings/notification-settings/notification-settings.component.html b/code/frontend/src/app/settings/notification-settings/notification-settings.component.html index 495d973b..72feb79a 100644 --- a/code/frontend/src/app/settings/notification-settings/notification-settings.component.html +++ b/code/frontend/src/app/settings/notification-settings/notification-settings.component.html @@ -114,13 +114,13 @@
- + The Apprise server URL
diff --git a/code/frontend/src/app/settings/notification-settings/notification-settings.component.ts b/code/frontend/src/app/settings/notification-settings/notification-settings.component.ts index ba538458..d6611219 100644 --- a/code/frontend/src/app/settings/notification-settings/notification-settings.component.ts +++ b/code/frontend/src/app/settings/notification-settings/notification-settings.component.ts @@ -84,7 +84,7 @@ export class NotificationSettingsComponent implements OnDestroy, CanComponentDea }), // Apprise configuration apprise: this.formBuilder.group({ - url: [''], + fullUrl: [''], key: [''], onFailedImportStrike: [false], onStalledStrike: [false], @@ -112,7 +112,7 @@ export class NotificationSettingsComponent implements OnDestroy, CanComponentDea onCategoryChanged: false, }, apprise: config.apprise || { - url: '', + fullUrl: '', key: '', onFailedImportStrike: false, onStalledStrike: false, @@ -268,7 +268,7 @@ export class NotificationSettingsComponent implements OnDestroy, CanComponentDea onCategoryChanged: false, }, apprise: { - url: '', + fullUrl: '', key: '', onFailedImportStrike: false, onStalledStrike: false, diff --git a/code/frontend/src/app/shared/models/apprise-config.model.ts b/code/frontend/src/app/shared/models/apprise-config.model.ts index 20ff57db..ddb7f449 100644 --- a/code/frontend/src/app/shared/models/apprise-config.model.ts +++ b/code/frontend/src/app/shared/models/apprise-config.model.ts @@ -1,6 +1,6 @@ import { NotificationConfig } from './notification-config.model'; export interface AppriseConfig extends NotificationConfig { - url?: string; + fullUrl?: string; key?: string; }