mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-03-04 14:27:52 -05:00
This commit is contained in:
304
code/Executable/Controllers/DownloadClientsController.cs
Normal file
304
code/Executable/Controllers/DownloadClientsController.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
using Common.Configuration.DownloadClient;
|
||||
using Common.Enums;
|
||||
using Infrastructure.Configuration;
|
||||
using Infrastructure.Verticals.DownloadClient;
|
||||
using Infrastructure.Verticals.DownloadClient.Factory;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Executable.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller for managing individual download clients
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/clients")]
|
||||
public class DownloadClientsController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<DownloadClientsController> _logger;
|
||||
private readonly IConfigManager _configManager;
|
||||
private readonly IDownloadClientFactory _clientFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DownloadClientsController"/> class
|
||||
/// </summary>
|
||||
public DownloadClientsController(
|
||||
ILogger<DownloadClientsController> logger,
|
||||
IConfigManager configManager,
|
||||
IDownloadClientFactory clientFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_configManager = configManager;
|
||||
_clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all download clients
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAllClients()
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = await _configManager.GetDownloadClientConfigAsync();
|
||||
if (config == null)
|
||||
{
|
||||
return NotFound(new { Message = "No download client configuration found" });
|
||||
}
|
||||
|
||||
return Ok(config.Clients);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error retrieving download clients");
|
||||
return StatusCode(500, new { Error = "An error occurred while retrieving download clients" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific download client by ID
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetClient(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = await _configManager.GetDownloadClientConfigAsync();
|
||||
if (config == null)
|
||||
{
|
||||
return NotFound(new { Message = "No download client configuration found" });
|
||||
}
|
||||
|
||||
var client = config.GetClientConfig(id);
|
||||
if (client == null)
|
||||
{
|
||||
return NotFound(new { Message = $"Client with ID '{id}' not found" });
|
||||
}
|
||||
|
||||
return Ok(client);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error retrieving download client {id}", id);
|
||||
return StatusCode(500, new { Error = "An error occurred while retrieving the download client" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new download client
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddClient([FromBody] ClientConfig clientConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Validate the new client configuration
|
||||
clientConfig.Validate();
|
||||
|
||||
// Get the current configuration
|
||||
var config = await _configManager.GetDownloadClientConfigAsync();
|
||||
if (config == null)
|
||||
{
|
||||
config = new DownloadClientConfig
|
||||
{
|
||||
Clients = new List<ClientConfig>()
|
||||
};
|
||||
}
|
||||
|
||||
// Check if a client with the same ID already exists
|
||||
if (config.GetClientConfig(clientConfig.Id) != null)
|
||||
{
|
||||
return BadRequest(new { Error = $"A client with ID '{clientConfig.Id}' already exists" });
|
||||
}
|
||||
|
||||
// Add the new client
|
||||
config.Clients.Add(clientConfig);
|
||||
|
||||
// Persist the updated configuration
|
||||
var result = await _configManager.UpdateDownloadClientConfigAsync(config);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, new { Error = "Failed to save download client configuration" });
|
||||
}
|
||||
|
||||
// Refresh the client factory to recognize the new client
|
||||
await _clientFactory.RefreshClients();
|
||||
|
||||
_logger.LogInformation("Added new download client: {name} ({id})", clientConfig.Name, clientConfig.Id);
|
||||
return CreatedAtAction(nameof(GetClient), new { id = clientConfig.Id }, clientConfig);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error adding download client");
|
||||
return BadRequest(new { Error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing download client
|
||||
/// </summary>
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> UpdateClient(string id, [FromBody] ClientConfig clientConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ensure the ID in the route matches the ID in the body
|
||||
if (id != clientConfig.Id)
|
||||
{
|
||||
return BadRequest(new { Error = "Client ID in the URL does not match the ID in the request body" });
|
||||
}
|
||||
|
||||
// Validate the updated client configuration
|
||||
clientConfig.Validate();
|
||||
|
||||
// Get the current configuration
|
||||
var config = await _configManager.GetDownloadClientConfigAsync();
|
||||
if (config == null)
|
||||
{
|
||||
return NotFound(new { Message = "No download client configuration found" });
|
||||
}
|
||||
|
||||
// Find the client to update
|
||||
var existingClientIndex = config.Clients.FindIndex(c => c.Id == id);
|
||||
if (existingClientIndex == -1)
|
||||
{
|
||||
return NotFound(new { Message = $"Client with ID '{id}' not found" });
|
||||
}
|
||||
|
||||
// Update the client
|
||||
config.Clients[existingClientIndex] = clientConfig;
|
||||
|
||||
// Persist the updated configuration
|
||||
var result = await _configManager.UpdateDownloadClientConfigAsync(config);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, new { Error = "Failed to save download client configuration" });
|
||||
}
|
||||
|
||||
// Refresh the client factory to recognize the updated client
|
||||
await _clientFactory.RefreshClients();
|
||||
|
||||
_logger.LogInformation("Updated download client: {name} ({id})", clientConfig.Name, clientConfig.Id);
|
||||
return Ok(clientConfig);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error updating download client {id}", id);
|
||||
return BadRequest(new { Error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a download client
|
||||
/// </summary>
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteClient(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the current configuration
|
||||
var config = await _configManager.GetDownloadClientConfigAsync();
|
||||
if (config == null)
|
||||
{
|
||||
return NotFound(new { Message = "No download client configuration found" });
|
||||
}
|
||||
|
||||
// Find the client to delete
|
||||
var existingClientIndex = config.Clients.FindIndex(c => c.Id == id);
|
||||
if (existingClientIndex == -1)
|
||||
{
|
||||
return NotFound(new { Message = $"Client with ID '{id}' not found" });
|
||||
}
|
||||
|
||||
// Remove the client
|
||||
config.Clients.RemoveAt(existingClientIndex);
|
||||
|
||||
// Persist the updated configuration
|
||||
var result = await _configManager.UpdateDownloadClientConfigAsync(config);
|
||||
if (!result)
|
||||
{
|
||||
return StatusCode(500, new { Error = "Failed to save download client configuration" });
|
||||
}
|
||||
|
||||
// Refresh the client factory to recognize the deleted client
|
||||
await _clientFactory.RefreshClients();
|
||||
|
||||
_logger.LogInformation("Deleted download client with ID: {id}", id);
|
||||
return NoContent();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error deleting download client {id}", id);
|
||||
return StatusCode(500, new { Error = "An error occurred while deleting the download client" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests connection to a download client
|
||||
/// </summary>
|
||||
[HttpPost("{id}/test")]
|
||||
public async Task<IActionResult> TestConnection(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the client configuration
|
||||
var config = await _configManager.GetDownloadClientConfigAsync();
|
||||
if (config == null)
|
||||
{
|
||||
return NotFound(new { Message = "No download client configuration found" });
|
||||
}
|
||||
|
||||
var clientConfig = config.GetClientConfig(id);
|
||||
if (clientConfig == null)
|
||||
{
|
||||
return NotFound(new { Message = $"Client with ID '{id}' not found" });
|
||||
}
|
||||
|
||||
// Ensure the client is initialized
|
||||
try
|
||||
{
|
||||
// Get the client instance
|
||||
var client = _clientFactory.GetClient(id);
|
||||
|
||||
// Try to login
|
||||
await client.LoginAsync();
|
||||
|
||||
_logger.LogInformation("Successfully connected to download client: {name} ({id})", clientConfig.Name, id);
|
||||
return Ok(new { Success = true, Message = "Connection successful" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to connect to download client: {name} ({id})", clientConfig.Name, id);
|
||||
return Ok(new { Success = false, Message = $"Connection failed: {ex.Message}" });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error testing connection to download client {id}", id);
|
||||
return StatusCode(500, new { Error = "An error occurred while testing the connection" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all clients of a specific type
|
||||
/// </summary>
|
||||
[HttpGet("type/{type}")]
|
||||
public async Task<IActionResult> GetClientsByType(DownloadClient type)
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = await _configManager.GetDownloadClientConfigAsync();
|
||||
if (config == null)
|
||||
{
|
||||
return NotFound(new { Message = "No download client configuration found" });
|
||||
}
|
||||
|
||||
var clients = config.Clients.Where(c => c.Type == type).ToList();
|
||||
return Ok(clients);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error retrieving download clients of type {type}", type);
|
||||
return StatusCode(500, new { Error = "An error occurred while retrieving download clients" });
|
||||
}
|
||||
}
|
||||
}
|
||||
103
code/Executable/Controllers/HealthCheckController.cs
Normal file
103
code/Executable/Controllers/HealthCheckController.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Infrastructure.Health;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Executable.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controller for checking the health of download clients
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/health")]
|
||||
public class HealthCheckController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<HealthCheckController> _logger;
|
||||
private readonly IHealthCheckService _healthCheckService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HealthCheckController"/> class
|
||||
/// </summary>
|
||||
public HealthCheckController(
|
||||
ILogger<HealthCheckController> logger,
|
||||
IHealthCheckService healthCheckService)
|
||||
{
|
||||
_logger = logger;
|
||||
_healthCheckService = healthCheckService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the health status of all download clients
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public IActionResult GetAllHealth()
|
||||
{
|
||||
try
|
||||
{
|
||||
var healthStatuses = _healthCheckService.GetAllClientHealth();
|
||||
return Ok(healthStatuses);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error retrieving client health statuses");
|
||||
return StatusCode(500, new { Error = "An error occurred while retrieving client health statuses" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the health status of a specific download client
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult GetClientHealth(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var healthStatus = _healthCheckService.GetClientHealth(id);
|
||||
if (healthStatus == null)
|
||||
{
|
||||
return NotFound(new { Message = $"Health status for client with ID '{id}' not found" });
|
||||
}
|
||||
|
||||
return Ok(healthStatus);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error retrieving health status for client {id}", id);
|
||||
return StatusCode(500, new { Error = "An error occurred while retrieving the client health status" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a health check for all download clients
|
||||
/// </summary>
|
||||
[HttpPost("check")]
|
||||
public async Task<IActionResult> CheckAllHealth()
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = await _healthCheckService.CheckAllClientsHealthAsync();
|
||||
return Ok(results);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error checking health for all clients");
|
||||
return StatusCode(500, new { Error = "An error occurred while checking client health" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a health check for a specific download client
|
||||
/// </summary>
|
||||
[HttpPost("check/{id}")]
|
||||
public async Task<IActionResult> CheckClientHealth(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _healthCheckService.CheckClientHealthAsync(id);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error checking health for client {id}", id);
|
||||
return StatusCode(500, new { Error = "An error occurred while checking client health" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Infrastructure.Health;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
@@ -11,6 +12,13 @@ public static class ApiDI
|
||||
// Add API-specific services
|
||||
services.AddControllers();
|
||||
services.AddEndpointsApiExplorer();
|
||||
|
||||
// Add SignalR for real-time updates
|
||||
services.AddSignalR();
|
||||
|
||||
// Add health status broadcaster
|
||||
services.AddHostedService<HealthStatusBroadcaster>();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new OpenApiInfo
|
||||
@@ -45,6 +53,9 @@ public static class ApiDI
|
||||
app.UseHttpsRedirection();
|
||||
app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
|
||||
// Map SignalR hubs
|
||||
app.MapHub<HealthStatusHub>("/hubs/health");
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Common.Configuration.General;
|
||||
using Common.Helpers;
|
||||
using Domain.Models.Arr;
|
||||
using Infrastructure.Configuration;
|
||||
using Infrastructure.Health;
|
||||
using Infrastructure.Http;
|
||||
using Infrastructure.Services;
|
||||
using Infrastructure.Verticals.DownloadClient.Factory;
|
||||
@@ -29,6 +30,7 @@ public static class MainDI
|
||||
})
|
||||
.AddServices()
|
||||
.AddDownloadClientServices()
|
||||
.AddHealthServices()
|
||||
.AddQuartzServices(configuration)
|
||||
.AddNotifications(configuration)
|
||||
.AddMassTransit(config =>
|
||||
@@ -121,4 +123,15 @@ public static class MainDI
|
||||
.AddTransient<Infrastructure.Verticals.DownloadClient.QBittorrent.QBitService>()
|
||||
.AddTransient<Infrastructure.Verticals.DownloadClient.Transmission.TransmissionService>()
|
||||
.AddTransient<Infrastructure.Verticals.DownloadClient.Deluge.DelugeService>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds health check services to the service collection
|
||||
/// </summary>
|
||||
private static IServiceCollection AddHealthServices(this IServiceCollection services) =>
|
||||
services
|
||||
// Register the health check service
|
||||
.AddSingleton<IHealthCheckService, HealthCheckService>()
|
||||
|
||||
// Register the background service for periodic health checks
|
||||
.AddHostedService<HealthCheckBackgroundService>();
|
||||
}
|
||||
Reference in New Issue
Block a user