Add openapi compatible xmldoc to SettingsController
also narrows types for `Update`, we onlu used the `Key` and `Value` properties, so that's all clients need to pass
This commit is contained in:
parent
0862c7130f
commit
f47d4631df
5 changed files with 129 additions and 18 deletions
|
|
@ -16,7 +16,7 @@ public class SettingData(DataContext dataContext, ILogger<SettingData> logger)
|
||||||
return GetSettings(Get, null);
|
return GetSettings(Get, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Update(IList<SettingProperty> settings)
|
public async Task Update(IList<SettingKeyValuePair> settings)
|
||||||
{
|
{
|
||||||
var dbSettings = await dataContext.Settings.ToListAsync();
|
var dbSettings = await dataContext.Settings.ToListAsync();
|
||||||
|
|
||||||
|
|
|
||||||
15
server/RdtClient.Data/Models/Internal/SettingKeyValuePair.cs
Normal file
15
server/RdtClient.Data/Models/Internal/SettingKeyValuePair.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace RdtClient.Data.Models.Internal;
|
||||||
|
|
||||||
|
public class SettingKeyValuePair
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The unique human-readable key identifying the setting
|
||||||
|
/// </summary>
|
||||||
|
/// <example>General:LogLevel</example>
|
||||||
|
/// <example>"Provider:Default:ExcludeRegex</example>
|
||||||
|
public String Key { get; set; } = default!;
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the setting
|
||||||
|
/// </summary>
|
||||||
|
public Object? Value { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
namespace RdtClient.Data.Models.Internal;
|
namespace RdtClient.Data.Models.Internal;
|
||||||
|
|
||||||
public class SettingProperty
|
public class SettingProperty: SettingKeyValuePair
|
||||||
{
|
{
|
||||||
public String Key { get; set; } = default!;
|
|
||||||
public Object? Value { get; set; }
|
|
||||||
public String? DisplayName { get; set; }
|
public String? DisplayName { get; set; }
|
||||||
public String? Description { get; set; }
|
public String? Description { get; set; }
|
||||||
public String Type { get; set; } = default!;
|
public String Type { get; set; } = default!;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ public class Settings(SettingData settingData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Update(IList<SettingProperty> settings)
|
public async Task Update(IList<SettingKeyValuePair> settings)
|
||||||
{
|
{
|
||||||
await settingData.Update(settings);
|
await settingData.Update(settings);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Diagnostics;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Diagnostics;
|
||||||
using Aria2NET;
|
using Aria2NET;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
@ -11,21 +12,39 @@ using RdtClient.Service.Services.Downloaders;
|
||||||
|
|
||||||
namespace RdtClient.Web.Controllers;
|
namespace RdtClient.Web.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controller for managing application settings and performing system tests
|
||||||
|
/// </summary>
|
||||||
[Authorize(Policy = "AuthSetting")]
|
[Authorize(Policy = "AuthSetting")]
|
||||||
[Route("Api/Settings")]
|
[Route("Api/Settings")]
|
||||||
public class SettingsController(Settings settings, Torrents torrents) : Controller
|
public class SettingsController(Settings settings, Torrents torrents) : Controller
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves all application settings
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A collection of all configured settings</returns>
|
||||||
|
/// <response code="200">Returns the list of settings</response>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("")]
|
[Route("")]
|
||||||
public ActionResult Get()
|
[ProducesResponseType(typeof(IEnumerable<SettingProperty>), StatusCodes.Status200OK)]
|
||||||
|
public ActionResult<IEnumerable<SettingProperty>> Get()
|
||||||
{
|
{
|
||||||
var result = SettingData.GetAll();
|
var result = SettingData.GetAll();
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates multiple application settings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="settings1">List of setting properties to update</param>
|
||||||
|
/// <returns>Success status</returns>
|
||||||
|
/// <response code="200">Settings were successfully updated</response>
|
||||||
|
/// <response code="400">Invalid settings data provided</response>
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[Route("")]
|
[Route("")]
|
||||||
public async Task<ActionResult> Update([FromBody] IList<SettingProperty>? settings1)
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
public async Task<ActionResult> Update([FromBody] IList<SettingKeyValuePair>? settings1)
|
||||||
{
|
{
|
||||||
if (settings1 == null)
|
if (settings1 == null)
|
||||||
{
|
{
|
||||||
|
|
@ -33,20 +52,41 @@ public class SettingsController(Settings settings, Torrents torrents) : Controll
|
||||||
}
|
}
|
||||||
|
|
||||||
await settings.Update(settings1);
|
await settings.Update(settings1);
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the profile information from the currently configured debrid service
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The profile information</returns>
|
||||||
|
/// <response code="200">The profile information</response>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("Profile")]
|
[Route("Profile")]
|
||||||
|
[ProducesResponseType(typeof(Profile), StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult<Profile>> Profile()
|
public async Task<ActionResult<Profile>> Profile()
|
||||||
{
|
{
|
||||||
var profile = await torrents.GetProfile();
|
var profile = await torrents.GetProfile();
|
||||||
return Ok(profile);
|
return Ok(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests if a specified path is writable by attempting to create and delete a test file
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Creates a test file in the specified directory to verify write permissions.
|
||||||
|
/// The test file is automatically deleted after the test completes.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="request">The path testing request containing the directory to test</param>
|
||||||
|
/// <returns>Success status if the path is writable</returns>
|
||||||
|
/// <response code="200">The path is valid and writable</response>
|
||||||
|
/// <response code="400">Invalid or empty path provided</response>
|
||||||
|
/// <response code="500">Path does not exist or is not accessible</response>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("TestPath")]
|
[Route("TestPath")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(typeof(String), StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(typeof(String), StatusCodes.Status500InternalServerError)]
|
||||||
public async Task<ActionResult> TestPath([FromBody] SettingsControllerTestPathRequest? request)
|
public async Task<ActionResult> TestPath([FromBody] SettingsControllerTestPathRequest? request)
|
||||||
{
|
{
|
||||||
if (request == null)
|
if (request == null)
|
||||||
|
|
@ -69,14 +109,24 @@ public class SettingsController(Settings settings, Torrents torrents) : Controll
|
||||||
var testFile = $"{path}/test.txt";
|
var testFile = $"{path}/test.txt";
|
||||||
|
|
||||||
await System.IO.File.WriteAllTextAsync(testFile, "RealDebridClient Test File, you can remove this file.");
|
await System.IO.File.WriteAllTextAsync(testFile, "RealDebridClient Test File, you can remove this file.");
|
||||||
|
|
||||||
await FileHelper.Delete(testFile);
|
await FileHelper.Delete(testFile);
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests download speed by downloading a sample file and measuring throughput
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">Token to cancel the operation</param>
|
||||||
|
/// <returns>The measured download speed in bytes per second</returns>
|
||||||
|
/// <response code="200">Returns the measured download speed</response>
|
||||||
|
/// <remarks>
|
||||||
|
/// The test downloads a file up to 50MB and measures the download speed.
|
||||||
|
/// </remarks>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("TestDownloadSpeed")]
|
[Route("TestDownloadSpeed")]
|
||||||
|
[ProducesResponseType(typeof(Int64), StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult> TestDownloadSpeed(CancellationToken cancellationToken)
|
public async Task<ActionResult> TestDownloadSpeed(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var downloadPath = Settings.Get.DownloadClient.DownloadPath;
|
var downloadPath = Settings.Get.DownloadClient.DownloadPath;
|
||||||
|
|
@ -121,7 +171,7 @@ public class SettingsController(Settings settings, Torrents torrents) : Controll
|
||||||
|
|
||||||
await aria2Downloader.Update(allDownloads);
|
await aria2Downloader.Update(allDownloads);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downloadClient.BytesDone > 1024 * 1024 * 50)
|
if (downloadClient.BytesDone > 1024 * 1024 * 50)
|
||||||
{
|
{
|
||||||
await downloadClient.Cancel();
|
await downloadClient.Cancel();
|
||||||
|
|
@ -131,12 +181,22 @@ public class SettingsController(Settings settings, Torrents torrents) : Controll
|
||||||
}
|
}
|
||||||
|
|
||||||
await FileHelper.Delete(testFilePath);
|
await FileHelper.Delete(testFilePath);
|
||||||
|
// ReSharper disable once SuggestVarOrType_BuiltInTypes
|
||||||
return Ok(downloadClient.Speed);
|
return Ok(downloadClient.Speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests write speed to the configured download directory
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The measured write speed in bytes per second</returns>
|
||||||
|
/// <response code="200">Returns the measured write speed</response>
|
||||||
|
/// <remarks>
|
||||||
|
/// Creates a 64MB test file with random data to measure disk write performance.
|
||||||
|
/// The test file is automatically deleted after the test completes.
|
||||||
|
/// </remarks>
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("TestWriteSpeed")]
|
[Route("TestWriteSpeed")]
|
||||||
|
[ProducesResponseType(typeof(Double), StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult> TestWriteSpeed()
|
public async Task<ActionResult> TestWriteSpeed()
|
||||||
{
|
{
|
||||||
var downloadPath = Settings.Get.DownloadClient.DownloadPath;
|
var downloadPath = Settings.Get.DownloadClient.DownloadPath;
|
||||||
|
|
@ -163,20 +223,35 @@ public class SettingsController(Settings settings, Torrents torrents) : Controll
|
||||||
|
|
||||||
await fileStream.WriteAsync(buffer.AsMemory(0, buffer.Length));
|
await fileStream.WriteAsync(buffer.AsMemory(0, buffer.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
|
|
||||||
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
|
var writeSpeed = fileStream.Length / watch.Elapsed.TotalSeconds;
|
||||||
|
|
||||||
fileStream.Close();
|
fileStream.Close();
|
||||||
|
|
||||||
await FileHelper.Delete(testFilePath);
|
await FileHelper.Delete(testFilePath);
|
||||||
|
|
||||||
return Ok(writeSpeed);
|
return Ok(writeSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests the connection to an Aria2c instance
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Attempts to connect to an Aria2c RPC endpoint and retrieve its version information.
|
||||||
|
/// This verifies both connectivity and authentication with the Aria2c server.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="request">The connection details for the Aria2c instance</param>
|
||||||
|
/// <returns>The version information of the Aria2c server if connection is successful</returns>
|
||||||
|
/// <response code="200">Returns the Aria2c version information</response>
|
||||||
|
/// <response code="400">Invalid or missing connection details</response>
|
||||||
|
/// <response code="500">Connection to Aria2c failed</response>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("TestAria2cConnection")]
|
[Route("TestAria2cConnection")]
|
||||||
|
[ProducesResponseType(typeof(String), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
public async Task<ActionResult<String>> TestAria2cConnection([FromBody] SettingsControllerTestAria2cConnectionRequest? request)
|
public async Task<ActionResult<String>> TestAria2cConnection([FromBody] SettingsControllerTestAria2cConnectionRequest? request)
|
||||||
{
|
{
|
||||||
if (request == null)
|
if (request == null)
|
||||||
|
|
@ -197,13 +272,36 @@ public class SettingsController(Settings settings, Torrents torrents) : Controll
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request model for testing path accessibility
|
||||||
|
/// </summary>
|
||||||
public class SettingsControllerTestPathRequest
|
public class SettingsControllerTestPathRequest
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The directory path to test for write access
|
||||||
|
/// </summary>
|
||||||
|
/// <example>/path/to/downloads</example>
|
||||||
|
[Required]
|
||||||
public String? Path { get; set; }
|
public String? Path { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request model for testing Aria2c connection
|
||||||
|
/// </summary>
|
||||||
public class SettingsControllerTestAria2cConnectionRequest
|
public class SettingsControllerTestAria2cConnectionRequest
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The URL of the Aria2c RPC endpoint
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
/// <example>http://localhost:6800/jsonrpc</example>
|
||||||
|
[Required]
|
||||||
public String? Url { get; set; }
|
public String? Url { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The secret token for authenticating with the Aria2c server
|
||||||
|
/// </summary>
|
||||||
|
/// <example>your-secret-token</example>
|
||||||
|
[Required]
|
||||||
public String? Secret { get; set; }
|
public String? Secret { get; set; }
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue