mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
refactor: implement unified startup validator architecture with IStartupValidator interface
This commit is contained in:
@@ -3,6 +3,7 @@ using octo_fiesta.Services;
|
|||||||
using octo_fiesta.Services.Deezer;
|
using octo_fiesta.Services.Deezer;
|
||||||
using octo_fiesta.Services.Qobuz;
|
using octo_fiesta.Services.Qobuz;
|
||||||
using octo_fiesta.Services.Local;
|
using octo_fiesta.Services.Local;
|
||||||
|
using octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -43,8 +44,13 @@ else
|
|||||||
builder.Services.AddSingleton<IDownloadService, DeezerDownloadService>();
|
builder.Services.AddSingleton<IDownloadService, DeezerDownloadService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Startup validation - runs at application startup to validate configuration
|
// Startup validation - register validators
|
||||||
builder.Services.AddHostedService<StartupValidationService>();
|
builder.Services.AddSingleton<IStartupValidator, SubsonicStartupValidator>();
|
||||||
|
builder.Services.AddSingleton<IStartupValidator, DeezerStartupValidator>();
|
||||||
|
builder.Services.AddSingleton<IStartupValidator, QobuzStartupValidator>();
|
||||||
|
|
||||||
|
// Register orchestrator as hosted service
|
||||||
|
builder.Services.AddHostedService<StartupValidationOrchestrator>();
|
||||||
|
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,24 +2,26 @@ using System.Text;
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using octo_fiesta.Models;
|
using octo_fiesta.Models;
|
||||||
|
using octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
namespace octo_fiesta.Services.Deezer;
|
namespace octo_fiesta.Services.Deezer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates Deezer ARL credentials at startup
|
/// Validates Deezer ARL credentials at startup
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DeezerStartupValidator
|
public class DeezerStartupValidator : BaseStartupValidator
|
||||||
{
|
{
|
||||||
private readonly DeezerSettings _settings;
|
private readonly DeezerSettings _settings;
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
|
public override string ServiceName => "Deezer";
|
||||||
|
|
||||||
public DeezerStartupValidator(IOptions<DeezerSettings> settings, HttpClient httpClient)
|
public DeezerStartupValidator(IOptions<DeezerSettings> settings, HttpClient httpClient)
|
||||||
|
: base(httpClient)
|
||||||
{
|
{
|
||||||
_settings = settings.Value;
|
_settings = settings.Value;
|
||||||
_httpClient = httpClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ValidateAsync(CancellationToken cancellationToken)
|
public override async Task<ValidationResult> ValidateAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var arl = _settings.Arl;
|
var arl = _settings.Arl;
|
||||||
var arlFallback = _settings.ArlFallback;
|
var arlFallback = _settings.ArlFallback;
|
||||||
@@ -31,7 +33,7 @@ public class DeezerStartupValidator
|
|||||||
{
|
{
|
||||||
WriteStatus("Deezer ARL", "NOT CONFIGURED", ConsoleColor.Red);
|
WriteStatus("Deezer ARL", "NOT CONFIGURED", ConsoleColor.Red);
|
||||||
WriteDetail("Set the Deezer__Arl environment variable");
|
WriteDetail("Set the Deezer__Arl environment variable");
|
||||||
return;
|
return ValidationResult.NotConfigured("Deezer ARL not configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteStatus("Deezer ARL", MaskSecret(arl), ConsoleColor.Cyan);
|
WriteStatus("Deezer ARL", MaskSecret(arl), ConsoleColor.Cyan);
|
||||||
@@ -50,6 +52,8 @@ public class DeezerStartupValidator
|
|||||||
{
|
{
|
||||||
await ValidateArlTokenAsync(arlFallback, "fallback", cancellationToken);
|
await ValidateArlTokenAsync(arlFallback, "fallback", cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ValidationResult.Success("Deezer validation completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ValidateArlTokenAsync(string arl, string label, CancellationToken cancellationToken)
|
private async Task ValidateArlTokenAsync(string arl, string label, CancellationToken cancellationToken)
|
||||||
@@ -150,37 +154,4 @@ public class DeezerStartupValidator
|
|||||||
|
|
||||||
return "Free";
|
return "Free";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteStatus(string label, string value, ConsoleColor valueColor)
|
|
||||||
{
|
|
||||||
Console.Write($" {label}: ");
|
|
||||||
var originalColor = Console.ForegroundColor;
|
|
||||||
Console.ForegroundColor = valueColor;
|
|
||||||
Console.WriteLine(value);
|
|
||||||
Console.ForegroundColor = originalColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteDetail(string message)
|
|
||||||
{
|
|
||||||
var originalColor = Console.ForegroundColor;
|
|
||||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
|
||||||
Console.WriteLine($" -> {message}");
|
|
||||||
Console.ForegroundColor = originalColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string MaskSecret(string secret)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(secret))
|
|
||||||
{
|
|
||||||
return "(empty)";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int visibleChars = 4;
|
|
||||||
if (secret.Length <= visibleChars)
|
|
||||||
{
|
|
||||||
return new string('*', secret.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return secret[..visibleChars] + new string('*', Math.Min(secret.Length - visibleChars, 8));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using octo_fiesta.Models;
|
using octo_fiesta.Models;
|
||||||
|
using octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
namespace octo_fiesta.Services.Qobuz;
|
namespace octo_fiesta.Services.Qobuz;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates Qobuz credentials at startup
|
/// Validates Qobuz credentials at startup
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class QobuzStartupValidator
|
public class QobuzStartupValidator : BaseStartupValidator
|
||||||
{
|
{
|
||||||
private readonly IOptions<QobuzSettings> _qobuzSettings;
|
private readonly IOptions<QobuzSettings> _qobuzSettings;
|
||||||
private readonly HttpClient _httpClient;
|
|
||||||
|
public override string ServiceName => "Qobuz";
|
||||||
|
|
||||||
public QobuzStartupValidator(IOptions<QobuzSettings> qobuzSettings, HttpClient httpClient)
|
public QobuzStartupValidator(IOptions<QobuzSettings> qobuzSettings, HttpClient httpClient)
|
||||||
|
: base(httpClient)
|
||||||
{
|
{
|
||||||
_qobuzSettings = qobuzSettings;
|
_qobuzSettings = qobuzSettings;
|
||||||
_httpClient = httpClient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ValidateAsync(CancellationToken cancellationToken)
|
public override async Task<ValidationResult> ValidateAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var userAuthToken = _qobuzSettings.Value.UserAuthToken;
|
var userAuthToken = _qobuzSettings.Value.UserAuthToken;
|
||||||
var userId = _qobuzSettings.Value.UserId;
|
var userId = _qobuzSettings.Value.UserId;
|
||||||
@@ -29,14 +31,14 @@ public class QobuzStartupValidator
|
|||||||
{
|
{
|
||||||
WriteStatus("Qobuz UserAuthToken", "NOT CONFIGURED", ConsoleColor.Red);
|
WriteStatus("Qobuz UserAuthToken", "NOT CONFIGURED", ConsoleColor.Red);
|
||||||
WriteDetail("Set the Qobuz__UserAuthToken environment variable");
|
WriteDetail("Set the Qobuz__UserAuthToken environment variable");
|
||||||
return;
|
return ValidationResult.NotConfigured("Qobuz UserAuthToken not configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(userId))
|
if (string.IsNullOrWhiteSpace(userId))
|
||||||
{
|
{
|
||||||
WriteStatus("Qobuz UserId", "NOT CONFIGURED", ConsoleColor.Red);
|
WriteStatus("Qobuz UserId", "NOT CONFIGURED", ConsoleColor.Red);
|
||||||
WriteDetail("Set the Qobuz__UserId environment variable");
|
WriteDetail("Set the Qobuz__UserId environment variable");
|
||||||
return;
|
return ValidationResult.NotConfigured("Qobuz UserId not configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteStatus("Qobuz UserAuthToken", MaskSecret(userAuthToken), ConsoleColor.Cyan);
|
WriteStatus("Qobuz UserAuthToken", MaskSecret(userAuthToken), ConsoleColor.Cyan);
|
||||||
@@ -45,6 +47,8 @@ public class QobuzStartupValidator
|
|||||||
|
|
||||||
// Validate token by calling Qobuz API
|
// Validate token by calling Qobuz API
|
||||||
await ValidateQobuzTokenAsync(userAuthToken, userId, cancellationToken);
|
await ValidateQobuzTokenAsync(userAuthToken, userId, cancellationToken);
|
||||||
|
|
||||||
|
return ValidationResult.Success("Qobuz validation completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ValidateQobuzTokenAsync(string userAuthToken, string userId, CancellationToken cancellationToken)
|
private async Task ValidateQobuzTokenAsync(string userAuthToken, string userId, CancellationToken cancellationToken)
|
||||||
@@ -122,37 +126,4 @@ public class QobuzStartupValidator
|
|||||||
WriteDetail(ex.Message);
|
WriteDetail(ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteStatus(string label, string value, ConsoleColor valueColor)
|
|
||||||
{
|
|
||||||
Console.Write($" {label}: ");
|
|
||||||
var originalColor = Console.ForegroundColor;
|
|
||||||
Console.ForegroundColor = valueColor;
|
|
||||||
Console.WriteLine(value);
|
|
||||||
Console.ForegroundColor = originalColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteDetail(string message)
|
|
||||||
{
|
|
||||||
var originalColor = Console.ForegroundColor;
|
|
||||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
|
||||||
Console.WriteLine($" -> {message}");
|
|
||||||
Console.ForegroundColor = originalColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string MaskSecret(string secret)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(secret))
|
|
||||||
{
|
|
||||||
return "(empty)";
|
|
||||||
}
|
|
||||||
|
|
||||||
const int visibleChars = 4;
|
|
||||||
if (secret.Length <= visibleChars)
|
|
||||||
{
|
|
||||||
return new string('*', secret.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return secret[..visibleChars] + new string('*', Math.Min(secret.Length - visibleChars, 8));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
95
octo-fiesta/Services/Validation/BaseStartupValidator.cs
Normal file
95
octo-fiesta/Services/Validation/BaseStartupValidator.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
namespace octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for startup validators providing common functionality
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BaseStartupValidator : IStartupValidator
|
||||||
|
{
|
||||||
|
protected readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
protected BaseStartupValidator(HttpClient httpClient)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the service being validated
|
||||||
|
/// </summary>
|
||||||
|
public abstract string ServiceName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the service configuration and connectivity
|
||||||
|
/// </summary>
|
||||||
|
public abstract Task<ValidationResult> ValidateAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a status line to the console with colored output
|
||||||
|
/// </summary>
|
||||||
|
protected static void WriteStatus(string label, string value, ConsoleColor valueColor)
|
||||||
|
{
|
||||||
|
Console.Write($" {label}: ");
|
||||||
|
var originalColor = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = valueColor;
|
||||||
|
Console.WriteLine(value);
|
||||||
|
Console.ForegroundColor = originalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a detail line to the console in dark gray
|
||||||
|
/// </summary>
|
||||||
|
protected static void WriteDetail(string message)
|
||||||
|
{
|
||||||
|
var originalColor = Console.ForegroundColor;
|
||||||
|
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||||
|
Console.WriteLine($" -> {message}");
|
||||||
|
Console.ForegroundColor = originalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Masks a secret string for display, showing only the first few characters
|
||||||
|
/// </summary>
|
||||||
|
protected static string MaskSecret(string secret)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(secret))
|
||||||
|
{
|
||||||
|
return "(empty)";
|
||||||
|
}
|
||||||
|
|
||||||
|
const int visibleChars = 4;
|
||||||
|
if (secret.Length <= visibleChars)
|
||||||
|
{
|
||||||
|
return new string('*', secret.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return secret[..visibleChars] + new string('*', Math.Min(secret.Length - visibleChars, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles common HTTP exceptions and returns appropriate validation result
|
||||||
|
/// </summary>
|
||||||
|
protected static ValidationResult HandleException(Exception ex, string fieldName)
|
||||||
|
{
|
||||||
|
return ex switch
|
||||||
|
{
|
||||||
|
TaskCanceledException => ValidationResult.Failure("TIMEOUT",
|
||||||
|
"Could not reach service within timeout period", ConsoleColor.Yellow),
|
||||||
|
|
||||||
|
HttpRequestException httpEx => ValidationResult.Failure("UNREACHABLE",
|
||||||
|
httpEx.Message, ConsoleColor.Yellow),
|
||||||
|
|
||||||
|
_ => ValidationResult.Failure("ERROR", ex.Message, ConsoleColor.Red)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes validation result to console
|
||||||
|
/// </summary>
|
||||||
|
protected void WriteValidationResult(string fieldName, ValidationResult result)
|
||||||
|
{
|
||||||
|
WriteStatus(fieldName, result.Status, result.StatusColor);
|
||||||
|
if (!string.IsNullOrEmpty(result.Details))
|
||||||
|
{
|
||||||
|
WriteDetail(result.Details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
octo-fiesta/Services/Validation/IStartupValidator.cs
Normal file
19
octo-fiesta/Services/Validation/IStartupValidator.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for service startup validators
|
||||||
|
/// </summary>
|
||||||
|
public interface IStartupValidator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the service being validated
|
||||||
|
/// </summary>
|
||||||
|
string ServiceName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the service configuration and connectivity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">Cancellation token</param>
|
||||||
|
/// <returns>Validation result containing status and details</returns>
|
||||||
|
Task<ValidationResult> ValidateAsync(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using octo_fiesta.Models;
|
||||||
|
|
||||||
|
namespace octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Orchestrates startup validation for all configured services.
|
||||||
|
/// This replaces the old StartupValidationService with a more extensible architecture.
|
||||||
|
/// </summary>
|
||||||
|
public class StartupValidationOrchestrator : IHostedService
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<IStartupValidator> _validators;
|
||||||
|
private readonly IOptions<SubsonicSettings> _subsonicSettings;
|
||||||
|
|
||||||
|
public StartupValidationOrchestrator(
|
||||||
|
IEnumerable<IStartupValidator> validators,
|
||||||
|
IOptions<SubsonicSettings> subsonicSettings)
|
||||||
|
{
|
||||||
|
_validators = validators;
|
||||||
|
_subsonicSettings = subsonicSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("========================================");
|
||||||
|
Console.WriteLine(" octo-fiesta starting up... ");
|
||||||
|
Console.WriteLine("========================================");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
// Run all validators
|
||||||
|
foreach (var validator in _validators)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await validator.ValidateAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error validating {validator.ServiceName}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("========================================");
|
||||||
|
Console.WriteLine(" Startup validation complete ");
|
||||||
|
Console.WriteLine("========================================");
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
87
octo-fiesta/Services/Validation/SubsonicStartupValidator.cs
Normal file
87
octo-fiesta/Services/Validation/SubsonicStartupValidator.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using octo_fiesta.Models;
|
||||||
|
|
||||||
|
namespace octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates Subsonic server connectivity at startup
|
||||||
|
/// </summary>
|
||||||
|
public class SubsonicStartupValidator : BaseStartupValidator
|
||||||
|
{
|
||||||
|
private readonly IOptions<SubsonicSettings> _subsonicSettings;
|
||||||
|
|
||||||
|
public override string ServiceName => "Subsonic";
|
||||||
|
|
||||||
|
public SubsonicStartupValidator(IOptions<SubsonicSettings> subsonicSettings, HttpClient httpClient)
|
||||||
|
: base(httpClient)
|
||||||
|
{
|
||||||
|
_subsonicSettings = subsonicSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<ValidationResult> ValidateAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var subsonicUrl = _subsonicSettings.Value.Url;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(subsonicUrl))
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic URL", "NOT CONFIGURED", ConsoleColor.Red);
|
||||||
|
WriteDetail("Set the Subsonic__Url environment variable");
|
||||||
|
return ValidationResult.NotConfigured("Subsonic URL not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteStatus("Subsonic URL", subsonicUrl, ConsoleColor.Cyan);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pingUrl = $"{subsonicUrl.TrimEnd('/')}/rest/ping.view?v=1.16.1&c=octo-fiesta&f=json";
|
||||||
|
var response = await _httpClient.GetAsync(pingUrl, cancellationToken);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var content = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (content.Contains("\"status\":\"ok\"") || content.Contains("status=\"ok\""))
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic server", "OK", ConsoleColor.Green);
|
||||||
|
return ValidationResult.Success("Subsonic server is accessible");
|
||||||
|
}
|
||||||
|
else if (content.Contains("\"status\":\"failed\"") || content.Contains("status=\"failed\""))
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic server", "REACHABLE", ConsoleColor.Yellow);
|
||||||
|
WriteDetail("Authentication may be required for some operations");
|
||||||
|
return ValidationResult.Success("Subsonic server is reachable");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic server", "REACHABLE", ConsoleColor.Yellow);
|
||||||
|
WriteDetail("Unexpected response format");
|
||||||
|
return ValidationResult.Success("Subsonic server is reachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic server", $"HTTP {(int)response.StatusCode}", ConsoleColor.Red);
|
||||||
|
return ValidationResult.Failure($"HTTP {(int)response.StatusCode}",
|
||||||
|
"Subsonic server returned an error", ConsoleColor.Red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic server", "TIMEOUT", ConsoleColor.Red);
|
||||||
|
WriteDetail("Could not reach server within 10 seconds");
|
||||||
|
return ValidationResult.Failure("TIMEOUT", "Could not reach server within timeout period", ConsoleColor.Red);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic server", "UNREACHABLE", ConsoleColor.Red);
|
||||||
|
WriteDetail(ex.Message);
|
||||||
|
return ValidationResult.Failure("UNREACHABLE", ex.Message, ConsoleColor.Red);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
WriteStatus("Subsonic server", "ERROR", ConsoleColor.Red);
|
||||||
|
WriteDetail(ex.Message);
|
||||||
|
return ValidationResult.Failure("ERROR", ex.Message, ConsoleColor.Red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
octo-fiesta/Services/Validation/ValidationResult.cs
Normal file
69
octo-fiesta/Services/Validation/ValidationResult.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
namespace octo_fiesta.Services.Validation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of a startup validation operation
|
||||||
|
/// </summary>
|
||||||
|
public class ValidationResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the validation was successful
|
||||||
|
/// </summary>
|
||||||
|
public bool IsValid { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Short status message (e.g., "VALID", "INVALID", "TIMEOUT", "NOT CONFIGURED")
|
||||||
|
/// </summary>
|
||||||
|
public string Status { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detailed information about the validation result
|
||||||
|
/// </summary>
|
||||||
|
public string? Details { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color to use when displaying the status in console
|
||||||
|
/// </summary>
|
||||||
|
public ConsoleColor StatusColor { get; set; } = ConsoleColor.White;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional metadata about the validation
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, object> Metadata { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a successful validation result
|
||||||
|
/// </summary>
|
||||||
|
public static ValidationResult Success(string details, Dictionary<string, object>? metadata = null)
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
{
|
||||||
|
IsValid = true,
|
||||||
|
Status = "VALID",
|
||||||
|
StatusColor = ConsoleColor.Green,
|
||||||
|
Details = details,
|
||||||
|
Metadata = metadata ?? new()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a failed validation result
|
||||||
|
/// </summary>
|
||||||
|
public static ValidationResult Failure(string status, string details, ConsoleColor color = ConsoleColor.Red)
|
||||||
|
{
|
||||||
|
return new ValidationResult
|
||||||
|
{
|
||||||
|
IsValid = false,
|
||||||
|
Status = status,
|
||||||
|
StatusColor = color,
|
||||||
|
Details = details
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a not configured validation result
|
||||||
|
/// </summary>
|
||||||
|
public static ValidationResult NotConfigured(string details)
|
||||||
|
{
|
||||||
|
return Failure("NOT CONFIGURED", details, ConsoleColor.Red);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user