Add openapi compatible xmldoc for TorrentsController

This commit is contained in:
Cucumberrbob 2025-03-06 21:10:53 +00:00
parent 95d2024097
commit 0862c7130f
No known key found for this signature in database
GPG key ID: 2B935C47401C3614

View file

@ -1,4 +1,5 @@
using System.Text.RegularExpressions; using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MonoTorrent; using MonoTorrent;
@ -6,15 +7,26 @@ using RdtClient.Data.Models.TorrentClient;
using RdtClient.Service.Helpers; using RdtClient.Service.Helpers;
using RdtClient.Service.Services; using RdtClient.Service.Services;
using Torrent = RdtClient.Data.Models.Data.Torrent; using Torrent = RdtClient.Data.Models.Data.Torrent;
using System.Text.Json.Serialization;
using NSwag.Annotations;
namespace RdtClient.Web.Controllers; namespace RdtClient.Web.Controllers;
/// <summary>
/// Controller for managing torrents and their downloads
/// </summary>
[Authorize(Policy = "AuthSetting")] [Authorize(Policy = "AuthSetting")]
[Route("Api/Torrents")] [Route("Api/Torrents")]
public class TorrentsController(ILogger<TorrentsController> logger, Torrents torrents, TorrentRunner torrentRunner) : Controller public class TorrentsController(ILogger<TorrentsController> logger, Torrents torrents, TorrentRunner torrentRunner) : Controller
{ {
/// <summary>
/// Retrieves all torrents and their associated downloads
/// </summary>
/// <returns>List of all torrents with their download status</returns>
/// <response code="200">Returns the list of torrents</response>
[HttpGet] [HttpGet]
[Route("")] [Route("")]
[ProducesResponseType(typeof(IList<Torrent>), StatusCodes.Status200OK)]
public async Task<ActionResult<IList<Torrent>>> GetAll() public async Task<ActionResult<IList<Torrent>>> GetAll()
{ {
var results = await torrents.Get(); var results = await torrents.Get();
@ -28,8 +40,17 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(results); return Ok(results);
} }
/// <summary>
/// Retrieves a specific torrent by its ID
/// </summary>
/// <param name="torrentId">The unique identifier of the torrent</param>
/// <returns>The requested torrent details</returns>
/// <response code="200">Returns the requested torrent</response>
/// <response code="404">Torrent not found</response>
[HttpGet] [HttpGet]
[Route("Get/{torrentId:guid}")] [Route("Get/{torrentId:guid}")]
[ProducesResponseType(typeof(Torrent), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Torrent>> GetById(Guid torrentId) public async Task<ActionResult<Torrent>> GetById(Guid torrentId)
{ {
var torrent = await torrents.GetById(torrentId); var torrent = await torrents.GetById(torrentId);
@ -46,11 +67,13 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
} }
/// <summary> /// <summary>
/// Used for debugging only. Force a tick. /// Forces an immediate processing cycle for debugging purposes
/// </summary> /// </summary>
/// <returns></returns> /// <returns>Success status</returns>
/// <response code="200">Processing cycle completed successfully</response>
[HttpGet] [HttpGet]
[Route("Tick")] [Route("Tick")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> Tick() public async Task<ActionResult> Tick()
{ {
await torrentRunner.Tick(); await torrentRunner.Tick();
@ -58,11 +81,21 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(); return Ok();
} }
/// <summary>
/// Adds a new torrent file with configuration
/// </summary>
/// <param name="file">The .torrent file to add</param>
/// <param name="formData">Configuration for the torrent download</param>
/// <returns>Success status</returns>
/// <response code="200">Torrent added successfully</response>
/// <response code="400">Invalid file or configuration provided</response>
[HttpPost] [HttpPost]
[Route("UploadFile")] [Route("UploadFile")]
public async Task<ActionResult> UploadFile([FromForm] IFormFile? file, [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(String), StatusCodes.Status400BadRequest)]
public async Task<ActionResult> UploadFile([OpenApiFile] IFormFile? file,
[ModelBinder(BinderType = typeof(JsonModelBinder))] [ModelBinder(BinderType = typeof(JsonModelBinder))]
TorrentControllerUploadFileRequest? formData) [FromForm] TorrentControllerUploadFileRequest? formData)
{ {
if (file == null || file.Length <= 0) if (file == null || file.Length <= 0)
{ {
@ -89,8 +122,17 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(); return Ok();
} }
/// <summary>
/// Adds a new torrent using a magnet link
/// </summary>
/// <param name="request">The magnet link and torrent configuration</param>
/// <returns>Success status</returns>
/// <response code="200">Magnet link processed successfully</response>
/// <response code="400">Invalid magnet link or configuration</response>
[HttpPost] [HttpPost]
[Route("UploadMagnet")] [Route("UploadMagnet")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(String), StatusCodes.Status400BadRequest)]
public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest? request) public async Task<ActionResult> UploadMagnet([FromBody] TorrentControllerUploadMagnetRequest? request)
{ {
if (request == null) if (request == null)
@ -115,9 +157,18 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(); return Ok();
} }
/// <summary>
/// Checks available files in a torrent file
/// </summary>
/// <param name="file">The .torrent file to analyze</param>
/// <returns>List of available files in the torrent</returns>
/// <response code="200">Returns the list of available files</response>
/// <response code="400">Invalid torrent file provided</response>
[HttpPost] [HttpPost]
[Route("CheckFiles")] [Route("CheckFiles")]
public async Task<ActionResult> CheckFiles([FromForm] IFormFile? file) [ProducesResponseType(typeof(IList<TorrentClientAvailableFile>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(String), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<IList<TorrentClientAvailableFile>>> CheckFiles([FromForm] IFormFile? file)
{ {
if (file == null || file.Length <= 0) if (file == null || file.Length <= 0)
{ {
@ -139,9 +190,18 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(result); return Ok(result);
} }
/// <summary>
/// Checks available files from a magnet link
/// </summary>
/// <param name="request">The magnet link to analyze</param>
/// <returns>List of available files in the torrent</returns>
/// <response code="200">Returns the list of available files</response>
/// <response code="400">Invalid magnet link provided</response>
[HttpPost] [HttpPost]
[Route("CheckFilesMagnet")] [Route("CheckFilesMagnet")]
public async Task<ActionResult> CheckFilesMagnet([FromBody] TorrentControllerCheckFilesRequest? request) [ProducesResponseType(typeof(IList<TorrentClientAvailableFile>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(String), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<IList<TorrentClientAvailableFile>>> CheckFilesMagnet([FromBody] TorrentControllerCheckFilesRequest? request)
{ {
if (request == null) if (request == null)
{ {
@ -160,8 +220,18 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(result); return Ok(result);
} }
/// <summary>
/// Deletes a torrent and optionally its associated data
/// </summary>
/// <param name="torrentId">The unique identifier of the torrent to delete</param>
/// <param name="request">Delete options specifying what should be removed</param>
/// <returns>Success status</returns>
/// <response code="200">Torrent deleted successfully</response>
/// <response code="400">Invalid request parameters</response>
[HttpPost] [HttpPost]
[Route("Delete/{torrentId:guid}")] [Route("Delete/{torrentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult> Delete(Guid torrentId, [FromBody] TorrentControllerDeleteRequest? request) public async Task<ActionResult> Delete(Guid torrentId, [FromBody] TorrentControllerDeleteRequest? request)
{ {
if (request == null) if (request == null)
@ -176,8 +246,15 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(); return Ok();
} }
/// <summary>
/// Retries a failed torrent download
/// </summary>
/// <param name="torrentId">The unique identifier of the torrent to retry</param>
/// <returns>Success status</returns>
/// <response code="200">Retry initiated successfully</response>
[HttpPost] [HttpPost]
[Route("Retry/{torrentId:guid}")] [Route("Retry/{torrentId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> Retry(Guid torrentId) public async Task<ActionResult> Retry(Guid torrentId)
{ {
logger.LogDebug("Retry {torrentId}", torrentId); logger.LogDebug("Retry {torrentId}", torrentId);
@ -188,8 +265,15 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(); return Ok();
} }
/// <summary>
/// Retries a failed download within a torrent
/// </summary>
/// <param name="downloadId">The unique identifier of the download to retry</param>
/// <returns>Success status</returns>
/// <response code="200">Retry initiated successfully</response>
[HttpPost] [HttpPost]
[Route("RetryDownload/{downloadId:guid}")] [Route("RetryDownload/{downloadId:guid}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult> RetryDownload(Guid downloadId) public async Task<ActionResult> RetryDownload(Guid downloadId)
{ {
logger.LogDebug("Retry download {downloadId}", downloadId); logger.LogDebug("Retry download {downloadId}", downloadId);
@ -199,8 +283,17 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(); return Ok();
} }
/// <summary>
/// Updates torrent configuration
/// </summary>
/// <param name="torrent">The updated torrent configuration</param>
/// <returns>Success status</returns>
/// <response code="200">Torrent updated successfully</response>
/// <response code="400">Invalid torrent configuration</response>
[HttpPut] [HttpPut]
[Route("Update")] [Route("Update")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult> Update([FromBody] Torrent? torrent) public async Task<ActionResult> Update([FromBody] Torrent? torrent)
{ {
if (torrent == null) if (torrent == null)
@ -213,9 +306,17 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
return Ok(); return Ok();
} }
/// <summary>
/// Tests regex patterns against torrent files
/// </summary>
/// <param name="request">The regex patterns and magnet link to test</param>
/// <returns>Matching files and any regex errors</returns>
/// <response code="200">Returns the regex test results</response>
/// <response code="400">Invalid request parameters</response>
[HttpPost] [HttpPost]
[Route("VerifyRegex")] [Route("VerifyRegex")]
public async Task<ActionResult> VerifyRegex([FromForm] IFormFile? file, [FromBody] TorrentControllerVerifyRegexRequest? request) [ProducesResponseType(typeof(RegexVerificationResult), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<RegexVerificationResult>> VerifyRegex([FromBody] TorrentControllerVerifyRegexRequest? request) public async Task<ActionResult<RegexVerificationResult>> VerifyRegex([FromBody] TorrentControllerVerifyRegexRequest? request)
{ {
if (request == null) if (request == null)
@ -280,41 +381,119 @@ public class TorrentsController(ILogger<TorrentsController> logger, Torrents tor
selectedFiles = [.. availableFiles]; selectedFiles = [.. availableFiles];
} }
return Ok(new return Ok(new RegexVerificationResult
{ {
includeError, IncludeError = includeError,
excludeError, ExcludeError = excludeError,
selectedFiles SelectedFiles = selectedFiles
}); });
} }
} }
/// <summary>
/// Request model for uploading a torrent file
/// </summary>
public class TorrentControllerUploadFileRequest public class TorrentControllerUploadFileRequest
{ {
/// <summary>
/// Configuration for the torrent download
/// </summary>
[Required]
public Torrent? Torrent { get; set; } public Torrent? Torrent { get; set; }
} }
/// <summary>
/// Request model for adding a magnet link
/// </summary>
public class TorrentControllerUploadMagnetRequest public class TorrentControllerUploadMagnetRequest
{ {
/// <summary>
/// The magnet URI to process
/// </summary>
[Required]
public String? MagnetLink { get; set; } public String? MagnetLink { get; set; }
/// <summary>
/// Configuration for the torrent download
/// </summary>
[Required]
public Torrent? Torrent { get; set; } public Torrent? Torrent { get; set; }
} }
/// <summary>
/// Request model for deleting a torrent
/// </summary>
public class TorrentControllerDeleteRequest public class TorrentControllerDeleteRequest
{ {
/// <summary>
/// Whether to delete the downloaded data
/// </summary>
public Boolean DeleteData { get; set; } public Boolean DeleteData { get; set; }
/// <summary>
/// Whether to remove the torrent from the Debrid service
/// </summary>
public Boolean DeleteRdTorrent { get; set; } public Boolean DeleteRdTorrent { get; set; }
/// <summary>
/// Whether to delete local torrent files
/// </summary>
public Boolean DeleteLocalFiles { get; set; } public Boolean DeleteLocalFiles { get; set; }
} }
/// <summary>
/// Request model for checking files in a magnet link
/// </summary>
public class TorrentControllerCheckFilesRequest public class TorrentControllerCheckFilesRequest
{ {
/// <summary>
/// The magnet URI to analyze
/// </summary>
[Required]
public String? MagnetLink { get; set; } public String? MagnetLink { get; set; }
} }
/// <summary>
/// Request model for verifying regex patterns
/// </summary>
public class TorrentControllerVerifyRegexRequest public class TorrentControllerVerifyRegexRequest
{ {
/// <summary>
/// Pattern for including files
/// </summary>
public String? IncludeRegex { get; set; } public String? IncludeRegex { get; set; }
/// <summary>
/// Pattern for excluding files
/// </summary>
public String? ExcludeRegex { get; set; } public String? ExcludeRegex { get; set; }
public String? MagnetLink { get; set;}
/// <summary>
/// Magnet link to test patterns against
/// </summary>
public String? MagnetLink { get; set; }
}
/// <summary>
/// Response model for regex verification results
/// </summary>
public class RegexVerificationResult
{
/// <summary>
/// Error message for the include regex pattern, if any
/// </summary>
[JsonPropertyName("includeError")]
public String IncludeError { get; set; } = String.Empty;
/// <summary>
/// Error message for the exclude regex pattern, if any
/// </summary>
[JsonPropertyName("excludeError")]
public String ExcludeError { get; set; } = String.Empty;
/// <summary>
/// Files that match the specified patterns
/// </summary>
[JsonPropertyName("selectedFiles")]
public IList<TorrentClientAvailableFile> SelectedFiles { get; set; } = new List<TorrentClientAvailableFile>();
} }