using System; namespace StellaOps.Concelier.Connector.Distro.Suse.Configuration; public sealed class SuseOptions { public const string HttpClientName = "concelier.suse"; /// /// CSV index enumerating CSAF advisories with their last modification timestamps. /// public Uri ChangesEndpoint { get; set; } = new("https://ftp.suse.com/pub/projects/security/csaf/changes.csv"); /// /// Base URI where individual CSAF advisories reside (filename appended verbatim). /// public Uri AdvisoryBaseUri { get; set; } = new("https://ftp.suse.com/pub/projects/security/csaf/"); /// /// Maximum advisories to fetch per run to bound backfill effort. /// public int MaxAdvisoriesPerFetch { get; set; } = 40; /// /// Initial history window for first-time execution. /// public TimeSpan InitialBackfill { get; set; } = TimeSpan.FromDays(30); /// /// Overlap window applied when resuming to capture late edits. /// public TimeSpan ResumeOverlap { get; set; } = TimeSpan.FromDays(3); /// /// Optional delay between advisory detail fetches. /// public TimeSpan RequestDelay { get; set; } = TimeSpan.Zero; /// /// Custom user agent presented to SUSE endpoints. /// public string UserAgent { get; set; } = "StellaOps.Concelier.Suse/0.1 (+https://stella-ops.org)"; /// /// Timeout override applied to HTTP requests (defaults to 60 seconds when unset). /// public TimeSpan FetchTimeout { get; set; } = TimeSpan.FromSeconds(45); public void Validate() { if (ChangesEndpoint is null || !ChangesEndpoint.IsAbsoluteUri) { throw new InvalidOperationException("SuseOptions.ChangesEndpoint must be an absolute URI."); } if (AdvisoryBaseUri is null || !AdvisoryBaseUri.IsAbsoluteUri) { throw new InvalidOperationException("SuseOptions.AdvisoryBaseUri must be an absolute URI."); } if (MaxAdvisoriesPerFetch <= 0 || MaxAdvisoriesPerFetch > 250) { throw new InvalidOperationException("MaxAdvisoriesPerFetch must be between 1 and 250."); } if (InitialBackfill < TimeSpan.Zero || InitialBackfill > TimeSpan.FromDays(365)) { throw new InvalidOperationException("InitialBackfill must be between 0 and 365 days."); } if (ResumeOverlap < TimeSpan.Zero || ResumeOverlap > TimeSpan.FromDays(14)) { throw new InvalidOperationException("ResumeOverlap must be between 0 and 14 days."); } if (FetchTimeout <= TimeSpan.Zero || FetchTimeout > TimeSpan.FromMinutes(5)) { throw new InvalidOperationException("FetchTimeout must be positive and less than five minutes."); } if (RequestDelay < TimeSpan.Zero || RequestDelay > TimeSpan.FromSeconds(10)) { throw new InvalidOperationException("RequestDelay must be between 0 and 10 seconds."); } } }