mirror of
https://github.com/C9Glax/tranga.git
synced 2025-07-03 17:34:17 +02:00
Refactor Controllers
SettingsController.cs SearchController.cs QueryController.cs NotificationConnectorController.cs MetadataFetcherController.cs MangaConnectorController.cs FileLibraryController LibraryConnectors WorkerController
This commit is contained in:
141
API/Controllers/FileLibraryController.cs
Normal file
141
API/Controllers/FileLibraryController.cs
Normal file
@ -0,0 +1,141 @@
|
||||
using API.Schema.MangaContext;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class FileLibraryController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns all <see cref="FileLibrary"/>
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<FileLibrary[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetFileLibraries()
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
|
||||
return Ok(context.FileLibraries.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="FileLibrary"/> with <paramref name="FileLibraryId"/>
|
||||
/// </summary>
|
||||
/// <param name="FileLibraryId"><see cref="FileLibrary"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404"><see cref="FileLibrary"/> with <paramref name="FileLibraryId"/> not found.</response>
|
||||
[HttpGet("{FileLibraryId}")]
|
||||
[ProducesResponseType<FileLibrary>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetFileLibrary(string FileLibraryId)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.FileLibraries.Find(FileLibraryId) is not { } library)
|
||||
return NotFound();
|
||||
|
||||
return Ok(library);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="FileLibraryId"/>.BasePath with <paramref name="FileLibraryId"/>
|
||||
/// </summary>
|
||||
/// <param name="FileLibraryId"><see cref="FileLibrary"/>.Key</param>
|
||||
/// <param name="newBasePath">New <see cref="FileLibraryId"/>.BasePath</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404"><see cref="FileLibrary"/> with <paramref name="FileLibraryId"/> not found.</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPatch("{FileLibraryId}/ChangeBasePath")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult ChangeLibraryBasePath(string FileLibraryId, [FromBody]string newBasePath)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.FileLibraries.Find(FileLibraryId) is not { } library)
|
||||
return NotFound();
|
||||
|
||||
//TODO Path check
|
||||
library.BasePath = newBasePath;
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the <see cref="FileLibraryId"/>.LibraryName with <paramref name="FileLibraryId"/>
|
||||
/// </summary>
|
||||
/// <param name="FileLibraryId"><see cref="FileLibrary"/>.Key</param>
|
||||
/// <param name="newName">New <see cref="FileLibraryId"/>.LibraryName</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404"><see cref="FileLibrary"/> with <paramref name="FileLibraryId"/> not found.</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPatch("{FileLibraryId}/ChangeName")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult ChangeLibraryName(string FileLibraryId, [FromBody] string newName)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.FileLibraries.Find(FileLibraryId) is not { } library)
|
||||
return NotFound();
|
||||
|
||||
//TODO Name check
|
||||
library.LibraryName = newName;
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new <see cref="FileLibraryId"/>
|
||||
/// </summary>
|
||||
/// <param name="library">New <see cref="FileLibrary"/> to add</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateNewLibrary([FromBody]FileLibrary library)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
|
||||
//TODO Parameter check
|
||||
context.FileLibraries.Add(library);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Created();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the <see cref="FileLibraryId"/>.LibraryName with <paramref name="FileLibraryId"/>
|
||||
/// </summary>
|
||||
/// <param name="FileLibraryId"><see cref="FileLibrary"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpDelete("{FileLibraryId}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult DeleteLocalLibrary(string FileLibraryId)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.FileLibraries.Find(FileLibraryId) is not { } library)
|
||||
return NotFound();
|
||||
|
||||
context.FileLibraries.Remove(library);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Ok();
|
||||
}
|
||||
}
|
@ -1,52 +1,54 @@
|
||||
using API.Schema.LibraryContext;
|
||||
using API.Schema.LibraryContext.LibraryConnectors;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class LibraryConnectorController(LibraryContext context, ILog Log) : Controller
|
||||
public class LibraryConnectorController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all configured ToFileLibrary-Connectors
|
||||
/// Gets all configured <see cref="LibraryConnector"/>
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<LibraryConnector[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetAllConnectors()
|
||||
{
|
||||
LibraryContext context = scope.ServiceProvider.GetRequiredService<LibraryContext>();
|
||||
|
||||
LibraryConnector[] connectors = context.LibraryConnectors.ToArray();
|
||||
|
||||
return Ok(connectors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns ToFileLibrary-Connector with requested ID
|
||||
/// Returns <see cref="LibraryConnector"/> with <paramref name="LibraryConnectorId"/>
|
||||
/// </summary>
|
||||
/// <param name="LibraryControllerId">ToFileLibrary-Connector-ID</param>
|
||||
/// <param name="LibraryConnectorId"><see cref="LibraryConnector"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Connector with ID not found.</response>
|
||||
[HttpGet("{LibraryControllerId}")]
|
||||
/// <response code="404"><see cref="LibraryConnector"/> with <paramref name="LibraryConnectorId"/> not found.</response>
|
||||
[HttpGet("{LibraryConnectorId}")]
|
||||
[ProducesResponseType<LibraryConnector>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetConnector(string LibraryControllerId)
|
||||
public IActionResult GetConnector(string LibraryConnectorId)
|
||||
{
|
||||
LibraryConnector? ret = context.LibraryConnectors.Find(LibraryControllerId);
|
||||
return (ret is not null) switch
|
||||
{
|
||||
true => Ok(ret),
|
||||
false => NotFound()
|
||||
};
|
||||
LibraryContext context = scope.ServiceProvider.GetRequiredService<LibraryContext>();
|
||||
if (context.LibraryConnectors.Find(LibraryConnectorId) is not { } connector)
|
||||
return NotFound();
|
||||
|
||||
return Ok(connector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ToFileLibrary-Connector
|
||||
/// Creates a new <see cref="LibraryConnector"/>
|
||||
/// </summary>
|
||||
/// <param name="libraryConnector">ToFileLibrary-Connector</param>
|
||||
/// <param name="libraryConnector"></param>
|
||||
/// <response code="201"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut]
|
||||
@ -54,46 +56,36 @@ public class LibraryConnectorController(LibraryContext context, ILog Log) : Cont
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateConnector([FromBody]LibraryConnector libraryConnector)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.LibraryConnectors.Add(libraryConnector);
|
||||
context.SaveChanges();
|
||||
return Created();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
LibraryContext context = scope.ServiceProvider.GetRequiredService<LibraryContext>();
|
||||
|
||||
context.LibraryConnectors.Add(libraryConnector);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Created();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the ToFileLibrary-Connector with the requested ID
|
||||
/// Deletes <see cref="LibraryConnector"/> with <paramref name="LibraryConnectorId"/>
|
||||
/// </summary>
|
||||
/// <param name="LibraryControllerId">ToFileLibrary-Connector-ID</param>
|
||||
/// <param name="LibraryConnectorId">ToFileLibrary-Connector-ID</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Connector with ID not found.</response>
|
||||
/// <response code="404"><see cref="LibraryConnector"/> with <<paramref name="LibraryConnectorId"/> not found.</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpDelete("{LibraryControllerId}")]
|
||||
[HttpDelete("{LibraryConnectorId}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult DeleteConnector(string LibraryControllerId)
|
||||
public IActionResult DeleteConnector(string LibraryConnectorId)
|
||||
{
|
||||
try
|
||||
{
|
||||
LibraryConnector? ret = context.LibraryConnectors.Find(LibraryControllerId);
|
||||
if (ret is null)
|
||||
return NotFound();
|
||||
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
LibraryContext context = scope.ServiceProvider.GetRequiredService<LibraryContext>();
|
||||
if (context.LibraryConnectors.Find(LibraryConnectorId) is not { } connector)
|
||||
return NotFound();
|
||||
|
||||
context.LibraryConnectors.Remove(connector);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Ok();
|
||||
}
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
using API.APIEndpointRecords;
|
||||
using API.Schema.MangaContext;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class LocalLibrariesController(MangaContext context, ILog Log) : Controller
|
||||
{
|
||||
[HttpGet]
|
||||
[ProducesResponseType<FileLibrary[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetLocalLibraries()
|
||||
{
|
||||
return Ok(context.LocalLibraries);
|
||||
}
|
||||
|
||||
[HttpGet("{LibraryId}")]
|
||||
[ProducesResponseType<FileLibrary>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetLocalLibrary(string LibraryId)
|
||||
{
|
||||
FileLibrary? library = context.LocalLibraries.Find(LibraryId);
|
||||
if (library is null)
|
||||
return NotFound();
|
||||
return Ok(library);
|
||||
}
|
||||
|
||||
[HttpPatch("{LibraryId}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult UpdateLocalLibrary(string LibraryId, [FromBody]NewLibraryRecord record)
|
||||
{
|
||||
FileLibrary? library = context.LocalLibraries.Find(LibraryId);
|
||||
if (library is null)
|
||||
return NotFound();
|
||||
if (record.Validate() == false)
|
||||
return BadRequest();
|
||||
|
||||
try
|
||||
{
|
||||
library.LibraryName = record.name;
|
||||
library.BasePath = record.path;
|
||||
context.SaveChanges();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPatch("{LibraryId}/ChangeBasePath")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult ChangeLibraryBasePath(string LibraryId, [FromBody] string newBasePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileLibrary? library = context.LocalLibraries.Find(LibraryId);
|
||||
if (library is null)
|
||||
return NotFound();
|
||||
|
||||
if (false) //TODO implement path check
|
||||
return BadRequest();
|
||||
|
||||
library.BasePath = newBasePath;
|
||||
context.SaveChanges();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPatch("{LibraryId}/ChangeName")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult ChangeLibraryName(string LibraryId, [FromBody] string newName)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileLibrary? library = context.LocalLibraries.Find(LibraryId);
|
||||
if (library is null)
|
||||
return NotFound();
|
||||
|
||||
if(newName.Length < 1)
|
||||
return BadRequest();
|
||||
|
||||
library.LibraryName = newName;
|
||||
context.SaveChanges();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
[ProducesResponseType<FileLibrary>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateNewLibrary([FromBody]NewLibraryRecord library)
|
||||
{
|
||||
if (library.Validate() == false)
|
||||
return BadRequest();
|
||||
try
|
||||
{
|
||||
FileLibrary newFileLibrary = new (library.path, library.name);
|
||||
context.LocalLibraries.Add(newFileLibrary);
|
||||
context.SaveChanges();
|
||||
|
||||
return Ok(newFileLibrary);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{LibraryId}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult DeleteLocalLibrary(string LibraryId)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
FileLibrary? library = context.LocalLibraries.Find(LibraryId);
|
||||
if (library is null)
|
||||
return NotFound();
|
||||
context.Remove(library);
|
||||
context.SaveChanges();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,107 +1,95 @@
|
||||
using API.Schema.MangaContext;
|
||||
using API.Schema.MangaContext.MangaConnectors;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class MangaConnectorController(MangaContext context, ILog Log) : Controller
|
||||
public class MangaConnectorController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all available Connectors (Scanlation-Sites)
|
||||
/// Get all <see cref="MangaConnector"/> (Scanlation-Sites)
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="200">Names of <see cref="MangaConnector"/> (Scanlation-Sites)</response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<MangaConnector[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetConnectors()
|
||||
{
|
||||
MangaConnector[] connectors = context.MangaConnectors.ToArray();
|
||||
return Ok(connectors);
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
return Ok(context.MangaConnectors.Select(c => c.Name).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the MangaConnector with the requested Name
|
||||
/// Returns the <see cref="MangaConnector"/> (Scanlation-Sites) with the requested Name
|
||||
/// </summary>
|
||||
/// <param name="MangaConnectorName"></param>
|
||||
/// <param name="MangaConnectorName"><see cref="MangaConnector"/>.Name</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Connector with ID not found.</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
/// <response code="404"><see cref="MangaConnector"/> (Scanlation-Sites) with Name not found.</response>
|
||||
[HttpGet("{MangaConnectorName}")]
|
||||
[ProducesResponseType<MangaConnector>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetConnector(string MangaConnectorName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(context.MangaConnectors.Find(MangaConnectorName) is not { } connector)
|
||||
return NotFound();
|
||||
|
||||
return Ok(connector);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if(context.MangaConnectors.Find(MangaConnectorName) is not { } connector)
|
||||
return NotFound();
|
||||
|
||||
return Ok(connector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all enabled Connectors (Scanlation-Sites)
|
||||
/// Get all enabled <see cref="MangaConnector"/> (Scanlation-Sites)
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("enabled")]
|
||||
[HttpGet("Enabled")]
|
||||
[ProducesResponseType<MangaConnector[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetEnabledConnectors()
|
||||
{
|
||||
MangaConnector[] connectors = context.MangaConnectors.Where(c => c.Enabled == true).ToArray();
|
||||
return Ok(connectors);
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
|
||||
return Ok(context.MangaConnectors.Where(c => c.Enabled).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all disabled Connectors (Scanlation-Sites)
|
||||
/// Get all disabled <see cref="MangaConnector"/> (Scanlation-Sites)
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("disabled")]
|
||||
[HttpGet("Disabled")]
|
||||
[ProducesResponseType<MangaConnector[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetDisabledConnectors()
|
||||
{
|
||||
MangaConnector[] connectors = context.MangaConnectors.Where(c => c.Enabled == false).ToArray();
|
||||
return Ok(connectors);
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
|
||||
return Ok(context.MangaConnectors.Where(c => c.Enabled == false).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enabled or disables a Connector
|
||||
/// Enabled or disables <see cref="MangaConnector"/> (Scanlation-Sites) with Name
|
||||
/// </summary>
|
||||
/// <param name="MangaConnectorName">ID of the connector</param>
|
||||
/// <param name="enabled">Set true to enable</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Connector with ID not found.</response>
|
||||
/// <param name="MangaConnectorName"><see cref="MangaConnector"/>.Name</param>
|
||||
/// <param name="Enabled">Set true to enable, false to disable</param>
|
||||
/// <response code="202"></response>
|
||||
/// <response code="404"><see cref="MangaConnector"/> (Scanlation-Sites) with Name not found.</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPatch("{MangaConnectorName}/SetEnabled/{enabled}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[HttpPatch("{MangaConnectorName}/SetEnabled/{Enabled}")]
|
||||
[ProducesResponseType(Status202Accepted)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult SetEnabled(string MangaConnectorName, bool enabled)
|
||||
public IActionResult SetEnabled(string MangaConnectorName, bool Enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
MangaConnector? connector = context.MangaConnectors.Find(MangaConnectorName);
|
||||
if (connector is null)
|
||||
return NotFound();
|
||||
|
||||
connector.Enabled = enabled;
|
||||
context.SaveChanges();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if(context.MangaConnectors.Find(MangaConnectorName) is not { } connector)
|
||||
return NotFound();
|
||||
|
||||
connector.Enabled = Enabled;
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Accepted();
|
||||
}
|
||||
}
|
@ -2,9 +2,7 @@
|
||||
using API.Schema.MangaContext.MangaConnectors;
|
||||
using API.Workers;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||
@ -352,7 +350,7 @@ public class MangaController(IServiceScope scope) : Controller
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.Mangas.Find(MangaId) is not { } manga)
|
||||
return NotFound(nameof(MangaId));
|
||||
if(context.LocalLibraries.Find(LibraryId) is not { } library)
|
||||
if(context.FileLibraries.Find(LibraryId) is not { } library)
|
||||
return NotFound(nameof(LibraryId));
|
||||
|
||||
MoveMangaLibraryWorker moveLibrary = new(manga, library, scope);
|
||||
|
@ -1,7 +1,6 @@
|
||||
using API.Schema.MangaContext;
|
||||
using API.Schema.MangaContext.MetadataFetchers;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
@ -12,47 +11,51 @@ namespace API.Controllers;
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class MetadataFetcherController(MangaContext context, ILog Log) : Controller
|
||||
public class MetadataFetcherController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all available Connectors (Metadata-Sites)
|
||||
/// Get all <see cref="MetadataFetcher"/> (Metadata-Sites)
|
||||
/// </summary>
|
||||
/// <response code="200">Names of Metadata-Fetchers</response>
|
||||
/// <response code="200">Names of <see cref="MetadataFetcher"/> (Metadata-Sites)</response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<string[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetConnectors()
|
||||
{
|
||||
string[] connectors = Tranga.MetadataFetchers.Select(f => f.MetadataFetcherName).ToArray();
|
||||
return Ok(connectors);
|
||||
return Ok(Tranga.MetadataFetchers.Select(m => m.Name).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all Mangas which have a linked Metadata-Provider
|
||||
/// Returns all <see cref="MetadataEntry"/>
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("Links")]
|
||||
[ProducesResponseType<MetadataEntry>(Status200OK, "application/json")]
|
||||
[ProducesResponseType<MetadataEntry[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetLinkedEntries()
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
|
||||
return Ok(context.MetadataEntries.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches Metadata-Provider for Manga-Metadata
|
||||
/// Searches <see cref="MetadataFetcher"/> (Metadata-Sites) for Manga-Metadata
|
||||
/// </summary>
|
||||
/// <param name="searchTerm">Instead of using the Manga for search, use a specific term</param>
|
||||
/// <param name="MangaId"><see cref="Manga"/>.Key</param>
|
||||
/// <param name="MetadataFetcherName"><see cref="MetadataFetcher"/>.Name</param>
|
||||
/// <param name="searchTerm">Instead of using the <paramref name="MangaId"/> for search on Website, use a specific term</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="400">Metadata-fetcher with Name does not exist</response>
|
||||
/// <response code="404">Manga with ID not found</response>
|
||||
/// <response code="400"><see cref="MetadataFetcher"/> (Metadata-Sites) with <paramref name="MetadataFetcherName"/> does not exist</response>
|
||||
/// <response code="404"><see cref="Manga"/> with <paramref name="MangaId"/> not found</response>
|
||||
[HttpPost("{MetadataFetcherName}/SearchManga/{MangaId}")]
|
||||
[ProducesResponseType<MetadataSearchResult[]>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult SearchMangaMetadata(string MangaId, string MetadataFetcherName, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]string? searchTerm = null)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if(context.Mangas.Find(MangaId) is not { } manga)
|
||||
return NotFound();
|
||||
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.MetadataFetcherName == MetadataFetcherName) is not { } fetcher)
|
||||
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is not { } fetcher)
|
||||
return BadRequest();
|
||||
|
||||
MetadataSearchResult[] searchResults = searchTerm is null ? fetcher.SearchMetadataEntry(manga) : fetcher.SearchMetadataEntry(searchTerm);
|
||||
@ -60,45 +63,43 @@ public class MetadataFetcherController(MangaContext context, ILog Log) : Control
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Links Metadata-Provider using Provider-Specific Identifier to Manga
|
||||
/// Links <see cref="MetadataFetcher"/> (Metadata-Sites) using Provider-Specific Identifier to <see cref="Manga"/>
|
||||
/// </summary>
|
||||
/// <param name="MangaId"><see cref="Manga"/>.Key</param>
|
||||
/// <param name="MetadataFetcherName"><see cref="MetadataFetcher"/>.Name</param>
|
||||
/// <param name="Identifier"><see cref="MetadataFetcherName"/>-Specific ID</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="400">Metadata-fetcher with Name does not exist</response>
|
||||
/// <response code="404">Manga with ID not found</response>
|
||||
/// <response code="400"><see cref="MetadataFetcher"/> (Metadata-Sites) with <paramref name="MetadataFetcherName"/> does not exist</response>
|
||||
/// <response code="404"><see cref="Manga"/> with <paramref name="MangaId"/> not found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("{MetadataFetcherName}/Link/{MangaId}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType<MetadataEntry>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult LinkMangaMetadata(string MangaId, string MetadataFetcherName, [FromBody]string Identifier)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if(context.Mangas.Find(MangaId) is not { } manga)
|
||||
return NotFound();
|
||||
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.MetadataFetcherName == MetadataFetcherName) is not { } fetcher)
|
||||
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is not { } fetcher)
|
||||
return BadRequest();
|
||||
MetadataEntry entry = fetcher.CreateMetadataEntry(manga, Identifier);
|
||||
|
||||
try
|
||||
{
|
||||
context.MetadataEntries.Add(entry);
|
||||
context.SaveChanges();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
return Ok();
|
||||
MetadataEntry entry = fetcher.CreateMetadataEntry(manga, Identifier);
|
||||
context.MetadataEntries.Add(entry);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Ok(entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Un-Links Metadata-Provider using Provider-Specific Identifier to Manga
|
||||
/// Un-Links <see cref="MetadataFetcher"/> (Metadata-Sites) from <see cref="Manga"/>
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="400">Metadata-fetcher with Name does not exist</response>
|
||||
/// <response code="404">Manga with ID not found</response>
|
||||
/// <response code="412">No Entry linking Manga and Metadata-Provider found</response>
|
||||
/// <response code="400"><see cref="MetadataFetcher"/> (Metadata-Sites) with <paramref name="MetadataFetcherName"/> does not exist</response>
|
||||
/// <response code="404"><see cref="Manga"/> with <paramref name="MangaId"/> not found</response>
|
||||
/// <response code="412">No <see cref="MetadataEntry"/> linking <see cref="Manga"/> and <see cref="MetadataFetcher"/> found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("{MetadataFetcherName}/Unlink/{MangaId}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
@ -108,58 +109,18 @@ public class MetadataFetcherController(MangaContext context, ILog Log) : Control
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult UnlinkMangaMetadata(string MangaId, string MetadataFetcherName)
|
||||
{
|
||||
if(context.Mangas.Find(MangaId) is not { } manga)
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if(context.Mangas.Find(MangaId) is null)
|
||||
return NotFound();
|
||||
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.MetadataFetcherName == MetadataFetcherName) is not { } fetcher)
|
||||
if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is null)
|
||||
return BadRequest();
|
||||
MetadataEntry? entry = context.MetadataEntries.FirstOrDefault(e => e.MangaId == MangaId && e.MetadataFetcherName == MetadataFetcherName);
|
||||
if (entry is null)
|
||||
if(context.MetadataEntries.FirstOrDefault(e => e.MangaId == MangaId && e.MetadataFetcherName == MetadataFetcherName) is not { } entry)
|
||||
return StatusCode(Status412PreconditionFailed, "No entry found");
|
||||
try
|
||||
{
|
||||
context.MetadataEntries.Remove(entry);
|
||||
context.SaveChanges();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries linking a Manga to a Metadata-Provider-Site
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="400">MetadataFetcher Name is invalid</response>
|
||||
/// <response code="404">Manga has no linked entry with MetadataFetcher</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("{MetadataFetcherName}/{MangaId}/UpdateMetadata")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult UpdateMetadata(string MangaId, string MetadataFetcherName)
|
||||
{
|
||||
if(Tranga.MetadataFetchers
|
||||
.FirstOrDefault(f =>
|
||||
f.MetadataFetcherName.Equals(MetadataFetcherName, StringComparison.InvariantCultureIgnoreCase)) is not { } fetcher)
|
||||
return BadRequest();
|
||||
MetadataEntry? entry = context.MetadataEntries
|
||||
.FirstOrDefault(e =>
|
||||
e.MangaId == MangaId && e.MetadataFetcherName.Equals(MetadataFetcherName, StringComparison.InvariantCultureIgnoreCase));
|
||||
if (entry is null)
|
||||
return NotFound();
|
||||
try
|
||||
{
|
||||
fetcher.UpdateMetadata(entry, context);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
context.Remove(entry);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Ok();
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ using API.APIEndpointRecords;
|
||||
using API.Schema.NotificationsContext;
|
||||
using API.Schema.NotificationsContext.NotificationConnectors;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
@ -13,121 +13,103 @@ namespace API.Controllers;
|
||||
[ApiController]
|
||||
[Produces("application/json")]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class NotificationConnectorController(NotificationsContext context, ILog Log) : Controller
|
||||
public class NotificationConnectorController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all configured Notification-Connectors
|
||||
/// Gets all configured <see cref="NotificationConnector"/>
|
||||
/// </summary>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet]
|
||||
[ProducesResponseType<NotificationConnector[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetAllConnectors()
|
||||
{
|
||||
NotificationConnector[] ret = context.NotificationConnectors.ToArray();
|
||||
return Ok(ret);
|
||||
NotificationsContext context = scope.ServiceProvider.GetRequiredService<NotificationsContext>();
|
||||
|
||||
return Ok(context.NotificationConnectors.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Notification-Connector with requested ID
|
||||
/// Returns <see cref="NotificationConnector"/> with requested Name
|
||||
/// </summary>
|
||||
/// <param name="NotificationConnectorId">Notification-Connector-ID</param>
|
||||
/// <param name="Name"><see cref="NotificationConnector"/>.Name</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">NotificationConnector with ID not found</response>
|
||||
[HttpGet("{NotificationConnectorId}")]
|
||||
/// <response code="404"><see cref="NotificationConnector"/> with <paramref name="Name"/> not found</response>
|
||||
[HttpGet("{Name}")]
|
||||
[ProducesResponseType<NotificationConnector>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetConnector(string NotificationConnectorId)
|
||||
public IActionResult GetConnector(string Name)
|
||||
{
|
||||
NotificationConnector? ret = context.NotificationConnectors.Find(NotificationConnectorId);
|
||||
return (ret is not null) switch
|
||||
{
|
||||
true => Ok(ret),
|
||||
false => NotFound()
|
||||
};
|
||||
NotificationsContext context = scope.ServiceProvider.GetRequiredService<NotificationsContext>();
|
||||
if(context.NotificationConnectors.Find(Name) is not { } connector)
|
||||
return NotFound();
|
||||
|
||||
return Ok(connector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new REST-Notification-Connector
|
||||
/// Creates a new <see cref="NotificationConnector"/>
|
||||
/// </summary>
|
||||
/// <remarks>Formatting placeholders: "%title" and "%text" can be placed in url, header-values and body and will be replaced when notifications are sent</remarks>
|
||||
/// <param name="notificationConnector">Notification-Connector</param>
|
||||
/// <response code="201">ID of new connector</response>
|
||||
/// <response code="409">A NotificationConnector with name already exists</response>
|
||||
/// <response code="201"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut]
|
||||
[ProducesResponseType<string>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status201Created)]
|
||||
[ProducesResponseType(Status409Conflict)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateConnector([FromBody]NotificationConnector notificationConnector)
|
||||
{
|
||||
if (context.NotificationConnectors.Find(notificationConnector.Name) is not null)
|
||||
return Conflict();
|
||||
try
|
||||
{
|
||||
context.NotificationConnectors.Add(notificationConnector);
|
||||
context.SaveChanges();
|
||||
return Created(notificationConnector.Name, notificationConnector);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
NotificationsContext context = scope.ServiceProvider.GetRequiredService<NotificationsContext>();
|
||||
|
||||
context.NotificationConnectors.Add(notificationConnector);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Created();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Gotify-Notification-Connector
|
||||
/// Creates a new Gotify-<see cref="NotificationConnector"/>
|
||||
/// </summary>
|
||||
/// <remarks>Priority needs to be between 0 and 10</remarks>
|
||||
/// <response code="201">ID of new connector</response>
|
||||
/// <response code="400"></response>
|
||||
/// <response code="409">A NotificationConnector with name already exists</response>
|
||||
/// <response code="201"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("Gotify")]
|
||||
[ProducesResponseType<string>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status409Conflict)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateGotifyConnector([FromBody]GotifyRecord gotifyData)
|
||||
{
|
||||
if(!gotifyData.Validate())
|
||||
return BadRequest();
|
||||
//TODO Validate Data
|
||||
|
||||
NotificationConnector gotifyConnector = new NotificationConnector(TokenGen.CreateToken("Gotify"),
|
||||
gotifyData.endpoint,
|
||||
new Dictionary<string, string>() { { "X-Gotify-IDOnConnector", gotifyData.appToken } },
|
||||
NotificationConnector gotifyConnector = new (gotifyData.Name,
|
||||
gotifyData.Endpoint,
|
||||
new Dictionary<string, string>() { { "X-Gotify-IDOnConnector", gotifyData.AppToken } },
|
||||
"POST",
|
||||
$"{{\"message\": \"%text\", \"title\": \"%title\", \"priority\": {gotifyData.priority}}}");
|
||||
$"{{\"message\": \"%text\", \"title\": \"%title\", \"Priority\": {gotifyData.Priority}}}");
|
||||
return CreateConnector(gotifyConnector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Ntfy-Notification-Connector
|
||||
/// Creates a new Ntfy-<see cref="NotificationConnector"/>
|
||||
/// </summary>
|
||||
/// <remarks>Priority needs to be between 1 and 5</remarks>
|
||||
/// <response code="201">ID of new connector</response>
|
||||
/// <response code="400"></response>
|
||||
/// <response code="409">A NotificationConnector with name already exists</response>
|
||||
/// <response code="201"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("Ntfy")]
|
||||
[ProducesResponseType<string>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status409Conflict)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreateNtfyConnector([FromBody]NtfyRecord ntfyRecord)
|
||||
{
|
||||
if(!ntfyRecord.Validate())
|
||||
return BadRequest();
|
||||
//TODO Validate Data
|
||||
|
||||
string authHeader = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ntfyRecord.username}:{ntfyRecord.password}"));
|
||||
string authHeader = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ntfyRecord.Username}:{ntfyRecord.Password}"));
|
||||
string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(authHeader)).Replace("=","");
|
||||
|
||||
NotificationConnector ntfyConnector = new (TokenGen.CreateToken("Ntfy"),
|
||||
$"{ntfyRecord.endpoint}?auth={auth}",
|
||||
NotificationConnector ntfyConnector = new (ntfyRecord.Name,
|
||||
$"{ntfyRecord.Endpoint}/{ntfyRecord.Topic}?auth={auth}",
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{"Title", "%title"},
|
||||
{"Priority", ntfyRecord.priority.ToString()},
|
||||
{"Priority", ntfyRecord.Priority.ToString()},
|
||||
},
|
||||
"POST",
|
||||
"%text");
|
||||
@ -135,58 +117,47 @@ public class NotificationConnectorController(NotificationsContext context, ILog
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Pushover-Notification-Connector
|
||||
/// Creates a new Pushover-<see cref="NotificationConnector"/>
|
||||
/// </summary>
|
||||
/// <remarks>https://pushover.net/api</remarks>
|
||||
/// <response code="201">ID of new connector</response>
|
||||
/// <response code="400"></response>
|
||||
/// <response code="409">A NotificationConnector with name already exists</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPut("Pushover")]
|
||||
[ProducesResponseType<string>(Status201Created, "application/json")]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status409Conflict)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CreatePushoverConnector([FromBody]PushoverRecord pushoverRecord)
|
||||
{
|
||||
if(!pushoverRecord.Validate())
|
||||
return BadRequest();
|
||||
//TODO Validate Data
|
||||
|
||||
NotificationConnector pushoverConnector = new (TokenGen.CreateToken("Pushover"),
|
||||
NotificationConnector pushoverConnector = new (pushoverRecord.Name,
|
||||
$"https://api.pushover.net/1/messages.json",
|
||||
new Dictionary<string, string>(),
|
||||
"POST",
|
||||
$"{{\"token\": \"{pushoverRecord.apptoken}\", \"user\": \"{pushoverRecord.user}\", \"message:\":\"%text\", \"%title\" }}");
|
||||
$"{{\"token\": \"{pushoverRecord.AppToken}\", \"user\": \"{pushoverRecord.User}\", \"message:\":\"%text\", \"%title\" }}");
|
||||
return CreateConnector(pushoverConnector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the Notification-Connector with the requested ID
|
||||
/// Deletes the <see cref="NotificationConnector"/> with the requested Name
|
||||
/// </summary>
|
||||
/// <param name="NotificationConnectorId">Notification-Connector-ID</param>
|
||||
/// <param name="Name"><see cref="NotificationConnector"/>.Name</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">NotificationConnector with ID not found</response>
|
||||
/// <response code="404"><see cref="NotificationConnector"/> with Name not found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpDelete("{NotificationConnectorId}")]
|
||||
[HttpDelete("{Name}")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult DeleteConnector(string NotificationConnectorId)
|
||||
public IActionResult DeleteConnector(string Name)
|
||||
{
|
||||
try
|
||||
{
|
||||
NotificationConnector? ret = context.NotificationConnectors.Find(NotificationConnectorId);
|
||||
if(ret is null)
|
||||
return NotFound();
|
||||
|
||||
context.Remove(ret);
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
NotificationsContext context = scope.ServiceProvider.GetRequiredService<NotificationsContext>();
|
||||
if(context.NotificationConnectors.Find(Name) is not { } connector)
|
||||
return NotFound();
|
||||
|
||||
context.NotificationConnectors.Remove(connector);
|
||||
|
||||
if(context.Sync().Result is { } errorMessage)
|
||||
return StatusCode(Status500InternalServerError, errorMessage);
|
||||
return Created();
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using API.Schema.MangaContext;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
// ReSharper disable InconsistentNaming
|
||||
@ -10,102 +9,74 @@ namespace API.Controllers;
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class QueryController(MangaContext context, ILog Log) : Controller
|
||||
public class QueryController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the Author-Information for Author-ID
|
||||
/// Returns the <see cref="Author"/> with <paramref name="AuthorId"/>
|
||||
/// </summary>
|
||||
/// <param name="AuthorId">Author-Id</param>
|
||||
/// <param name="AuthorId"><see cref="Author"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Author with ID not found</response>
|
||||
/// <response code="404"><see cref="Author"/> with <paramref name="AuthorId"/> not found</response>
|
||||
[HttpGet("Author/{AuthorId}")]
|
||||
[ProducesResponseType<Author>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetAuthor(string AuthorId)
|
||||
{
|
||||
Author? ret = context.Authors.Find(AuthorId);
|
||||
if (ret is null)
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.Authors.Find(AuthorId) is not { } author)
|
||||
return NotFound();
|
||||
return Ok(ret);
|
||||
|
||||
return Ok(author);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all Mangas which where Authored by Author with AuthorId
|
||||
/// Returns all <see cref="Manga"/> which where Authored by <see cref="Author"/> with <paramref name="AuthorId"/>
|
||||
/// </summary>
|
||||
/// <param name="AuthorId">Author-ID</param>
|
||||
/// <param name="AuthorId"><see cref="Author"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Author not found</response>
|
||||
/// <response code="404"><see cref="Author"/> with <paramref name="AuthorId"/></response>
|
||||
[HttpGet("Mangas/WithAuthorId/{AuthorId}")]
|
||||
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetMangaWithAuthorIds(string AuthorId)
|
||||
{
|
||||
if(context.Authors.Find(AuthorId) is not { } a)
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.Authors.Find(AuthorId) is not { } author)
|
||||
return NotFound();
|
||||
return Ok(context.Mangas.Where(m => m.Authors.Contains(a)));
|
||||
}
|
||||
/*
|
||||
/// <summary>
|
||||
/// Returns Link-Information for Link-Id
|
||||
/// </summary>
|
||||
/// <param name="LinkId"></param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Link with ID not found</response>
|
||||
[HttpGet("Link/{LinkId}")]
|
||||
[ProducesResponseType<Link>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetLink(string LinkId)
|
||||
{
|
||||
Link? ret = context.Links.Find(LinkId);
|
||||
if (ret is null)
|
||||
return NotFound();
|
||||
return Ok(ret);
|
||||
|
||||
return Ok(context.Mangas.Where(m => m.Authors.Contains(author)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns AltTitle-Information for AltTitle-Id
|
||||
/// Returns all <see cref="Manga"/> with <see cref="Tag"/>
|
||||
/// </summary>
|
||||
/// <param name="AltTitleId"></param>
|
||||
/// <param name="Tag"><see cref="Tag"/>.Tag</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">AltTitle with ID not found</response>
|
||||
[HttpGet("AltTitle/{AltTitleId}")]
|
||||
[ProducesResponseType<AltTitle>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
public IActionResult GetAltTitle(string AltTitleId)
|
||||
{
|
||||
AltTitle? ret = context.AltTitles.Find(AltTitleId);
|
||||
if (ret is null)
|
||||
return NotFound();
|
||||
return Ok(ret);
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Returns all Obj with Tag
|
||||
/// </summary>
|
||||
/// <param name="Tag"></param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Tag not found</response>
|
||||
/// <response code="404"><see cref="Tag"/> not found</response>
|
||||
[HttpGet("Mangas/WithTag/{Tag}")]
|
||||
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
|
||||
public IActionResult GetMangasWithTag(string Tag)
|
||||
{
|
||||
if(context.Tags.Find(Tag) is not { } t)
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.Tags.Find(Tag) is not { } tag)
|
||||
return NotFound();
|
||||
return Ok(context.Mangas.Where(m => m.MangaTags.Contains(t)));
|
||||
|
||||
return Ok(context.Mangas.Where(m => m.MangaTags.Contains(tag)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Chapter-Information for Chapter-Id
|
||||
/// Returns <see cref="Chapter"/> with <paramref name="ChapterId"/>
|
||||
/// </summary>
|
||||
/// <param name="ChapterId"></param>
|
||||
/// <param name="ChapterId"><see cref="Chapter"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">Chapter with ID not found</response>
|
||||
/// <response code="404"><see cref="Chapter"/> with <paramref name="ChapterId"/> not found</response>
|
||||
[HttpGet("Chapter/{ChapterId}")]
|
||||
[ProducesResponseType<Chapter>(Status200OK, "application/json")]
|
||||
public IActionResult GetChapter(string ChapterId)
|
||||
{
|
||||
Chapter? ret = context.Chapters.Find(ChapterId);
|
||||
if (ret is null)
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.Chapters.Find(ChapterId) is not { } chapter)
|
||||
return NotFound();
|
||||
return Ok(ret);
|
||||
|
||||
return Ok(chapter);
|
||||
}
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
using API.Schema.MangaContext;
|
||||
using API.Schema.MangaContext.MangaConnectors;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Soenneker.Utils.String.NeedlemanWunsch;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
@ -12,98 +10,69 @@ namespace API.Controllers;
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class SearchController(MangaContext context, ILog Log) : Controller
|
||||
public class SearchController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Initiate a search for a Obj on a specific Connector
|
||||
/// Initiate a search for a <see cref="Manga"/> on <see cref="MangaConnector"/> with searchTerm
|
||||
/// </summary>
|
||||
/// <param name="MangaConnectorName"></param>
|
||||
/// <param name="Query"></param>
|
||||
/// <param name="MangaConnectorName"><see cref="MangaConnector"/>.Name</param>
|
||||
/// <param name="Query">searchTerm</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404">MangaConnector with ID not found</response>
|
||||
/// <response code="406">MangaConnector with ID is disabled</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
/// <response code="404"><see cref="MangaConnector"/> with Name not found</response>
|
||||
/// <response code="412"><see cref="MangaConnector"/> with Name is disabled</response>
|
||||
[HttpGet("{MangaConnectorName}/{Query}")]
|
||||
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status406NotAcceptable)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult SearchManga(string MangaConnectorName, string Query)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if(context.MangaConnectors.Find(MangaConnectorName) is not { } connector)
|
||||
return NotFound();
|
||||
else if (connector.Enabled is false)
|
||||
return StatusCode(Status406NotAcceptable);
|
||||
if (connector.Enabled is false)
|
||||
return StatusCode(Status412PreconditionFailed);
|
||||
|
||||
(Manga, MangaConnectorId<Manga>)[] mangas = connector.SearchManga(Query);
|
||||
List<Manga> retMangas = new();
|
||||
foreach ((Manga manga, MangaConnectorId<Manga> mcId) manga in mangas)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(AddMangaToContext(manga) is { } add)
|
||||
retMangas.Add(add);
|
||||
}
|
||||
catch (DbUpdateException e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(Status500InternalServerError, e.Message);
|
||||
}
|
||||
if(AddMangaToContext(manga, context) is { } add)
|
||||
retMangas.Add(add);
|
||||
}
|
||||
|
||||
return Ok(retMangas.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for a known Obj
|
||||
/// </summary>
|
||||
/// <param name="Query"></param>
|
||||
/// <response code="200"></response>
|
||||
[HttpGet("Local/{Query}")]
|
||||
[ProducesResponseType<Manga[]>(Status200OK, "application/json")]
|
||||
public IActionResult SearchMangaLocally(string Query)
|
||||
{
|
||||
Dictionary<Manga, double> distance = context.Mangas
|
||||
.ToArray()
|
||||
.ToDictionary(m => m, m => NeedlemanWunschStringUtil.CalculateSimilarityPercentage(Query, m.Name));
|
||||
return Ok(distance.Where(kv => kv.Value > 50).OrderByDescending(kv => kv.Value).Select(kv => kv.Key).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Obj from MangaConnector associated with URL
|
||||
/// Returns <see cref="Manga"/> from the <see cref="MangaConnector"/> associated with <paramref name="url"/>
|
||||
/// </summary>
|
||||
/// <param name="url">Obj-Page URL</param>
|
||||
/// <param name="url"></param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="300">Multiple connectors found for URL</response>
|
||||
/// <response code="404">Obj not found</response>
|
||||
/// <response code="300">Multiple <see cref="MangaConnector"/> found for URL</response>
|
||||
/// <response code="404"><see cref="Manga"/> not found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("Url")]
|
||||
[ProducesResponseType<Manga>(Status200OK, "application/json")]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
[ProducesResponseType(Status500InternalServerError)]
|
||||
public IActionResult GetMangaFromUrl([FromBody]string url)
|
||||
{
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
if (context.MangaConnectors.Find("Global") is not { } connector)
|
||||
return StatusCode(Status500InternalServerError, "Could not find Global Connector.");
|
||||
|
||||
if(connector.GetMangaFromUrl(url) is not { } manga)
|
||||
return NotFound();
|
||||
try
|
||||
{
|
||||
if(AddMangaToContext(manga) is { } add)
|
||||
return Ok(add);
|
||||
|
||||
if(AddMangaToContext(manga, context) is not { } add)
|
||||
return StatusCode(Status500InternalServerError);
|
||||
}
|
||||
catch (DbUpdateException e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(Status500InternalServerError, e.Message);
|
||||
}
|
||||
|
||||
return Ok(add);
|
||||
}
|
||||
|
||||
private Manga? AddMangaToContext((Manga, MangaConnectorId<Manga>) manga) => AddMangaToContext(manga.Item1, manga.Item2, context);
|
||||
|
||||
internal static Manga? AddMangaToContext(Manga addManga, MangaConnectorId<Manga> addMcId, MangaContext context)
|
||||
private Manga? AddMangaToContext((Manga, MangaConnectorId<Manga>) manga, MangaContext context) => AddMangaToContext(manga.Item1, manga.Item2, context);
|
||||
|
||||
private static Manga? AddMangaToContext(Manga addManga, MangaConnectorId<Manga> addMcId, MangaContext context)
|
||||
{
|
||||
Manga manga = context.Mangas.Find(addManga.Key) ?? addManga;
|
||||
MangaConnectorId<Manga> mcId = context.MangaConnectorToManga.Find(addMcId.Key) ?? addMcId;
|
||||
@ -123,16 +92,12 @@ public class SearchController(MangaContext context, ILog Log) : Controller
|
||||
});
|
||||
manga.Authors = mergedAuthors.ToList();
|
||||
|
||||
try
|
||||
{
|
||||
if(context.MangaConnectorToManga.Find(addMcId.Key) is null)
|
||||
context.MangaConnectorToManga.Add(mcId);
|
||||
context.SaveChanges();
|
||||
}
|
||||
catch (DbUpdateException e)
|
||||
{
|
||||
|
||||
if(context.MangaConnectorToManga.Find(addMcId.Key) is null)
|
||||
context.MangaConnectorToManga.Add(mcId);
|
||||
|
||||
if (context.Sync().Result is not null)
|
||||
return null;
|
||||
}
|
||||
return manga;
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
using API.MangaDownloadClients;
|
||||
using API.Schema.JobsContext.Jobs;
|
||||
using API.Schema.MangaContext;
|
||||
using API.Workers;
|
||||
using Asp.Versioning;
|
||||
using log4net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
[ApiVersion(2)]
|
||||
[ApiController]
|
||||
[Route("v{v:apiVersion}/[controller]")]
|
||||
public class SettingsController(MangaContext context, ILog Log) : Controller
|
||||
public class SettingsController(IServiceScope scope) : Controller
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all Settings
|
||||
@ -237,58 +237,25 @@ public class SettingsController(MangaContext context, ILog Log) : Controller
|
||||
/// %C Chapter
|
||||
/// %T Title
|
||||
/// %A Author (first in list)
|
||||
/// %I Chapter Internal ID
|
||||
/// %i Obj Internal ID
|
||||
/// %Y Year (Obj)
|
||||
///
|
||||
/// ?_(...) replace _ with a value from above:
|
||||
/// Everything inside the braces will only be added if the value of %_ is not null
|
||||
/// </remarks>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPatch("ChapterNamingScheme")]
|
||||
[ProducesResponseType(Status200OK)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult SetCustomNamingScheme([FromBody]string namingScheme)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<Chapter, string> oldPaths = context.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath);
|
||||
TrangaSettings.UpdateChapterNamingScheme(namingScheme);
|
||||
MoveFileOrFolderJob[] newJobs = oldPaths
|
||||
.Select(kv => new MoveFileOrFolderJob(kv.Value, kv.Key.FullArchiveFilePath)).ToArray();
|
||||
context.Jobs.AddRange(newJobs);
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a UpdateCoverJob for all Obj
|
||||
/// </summary>
|
||||
/// <response code="200">Array of JobIds</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("CleanupCovers")]
|
||||
[ProducesResponseType<string[]>(Status200OK)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult CleanupCovers()
|
||||
{
|
||||
try
|
||||
{
|
||||
Tranga.RemoveStaleFiles(context);
|
||||
List<UpdateCoverJob> newJobs = context.Mangas.ToList().Select(m => new UpdateCoverJob(m, 0)).ToList();
|
||||
context.Jobs.AddRange(newJobs);
|
||||
return Ok(newJobs.Select(j => j.Key));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e);
|
||||
}
|
||||
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
|
||||
|
||||
Dictionary<Chapter, string> oldPaths = context.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath);
|
||||
TrangaSettings.UpdateChapterNamingScheme(namingScheme);
|
||||
MoveFileOrFolderWorker[] newJobs = oldPaths
|
||||
.Select(kv => new MoveFileOrFolderWorker(kv.Value, kv.Key.FullArchiveFilePath)).ToArray();
|
||||
Tranga.AddWorkers(newJobs);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -84,110 +84,73 @@ public class WorkerController(ILog Log) : Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modify Job with ID
|
||||
/// Modify <see cref="BaseWorker"/> with <paramref name="WorkerId"/>
|
||||
/// </summary>
|
||||
/// <param name="JobId">Job-ID</param>
|
||||
/// <param name="modifyJobRecord">Fields to modify, set to null to keep previous value</param>
|
||||
/// <response code="202">Job modified</response>
|
||||
/// <response code="400">Malformed request</response>
|
||||
/// <response code="404">Job with ID not found</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPatch("{JobId}")]
|
||||
[ProducesResponseType<Job>(Status202Accepted, "application/json")]
|
||||
/// <param name="WorkerId"><see cref="BaseWorker"/>.Key</param>
|
||||
/// <param name="modifyWorkerRecord">Fields to modify, set to null to keep previous value</param>
|
||||
/// <response code="202"></response>
|
||||
/// <response code="400"></response>
|
||||
/// <response code="404"><see cref="BaseWorker"/> with <paramref name="WorkerId"/> could not be found</response>
|
||||
/// <response code="409"><see cref="BaseWorker"/> is not <see cref="IPeriodic"/>, can not modify <paramref name="modifyWorkerRecord.IntervalMs"/></response>
|
||||
[HttpPatch("{WorkerId}")]
|
||||
[ProducesResponseType<BaseWorker>(Status202Accepted, "application/json")]
|
||||
[ProducesResponseType(Status400BadRequest)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult ModifyJob(string JobId, [FromBody]ModifyJobRecord modifyJobRecord)
|
||||
[ProducesResponseType<string>(Status409Conflict, "text/plain")]
|
||||
public IActionResult ModifyJob(string WorkerId, [FromBody]ModifyWorkerRecord modifyWorkerRecord)
|
||||
{
|
||||
try
|
||||
{
|
||||
Job? ret = context.Jobs.Find(JobId);
|
||||
if(ret is null)
|
||||
return NotFound();
|
||||
|
||||
ret.RecurrenceMs = modifyJobRecord.RecurrenceMs ?? ret.RecurrenceMs;
|
||||
ret.Enabled = modifyJobRecord.Enabled ?? ret.Enabled;
|
||||
|
||||
context.SaveChanges();
|
||||
return new AcceptedResult(ret.Key, ret);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
if(Tranga.Workers.FirstOrDefault(w => w.Key == WorkerId) is not { } worker)
|
||||
return NotFound(nameof(WorkerId));
|
||||
|
||||
if(modifyWorkerRecord.IntervalMs is not null && worker is not IPeriodic)
|
||||
return Conflict("Can not modify Interval of non-Periodic worker");
|
||||
else if(modifyWorkerRecord.IntervalMs is not null && worker is IPeriodic periodic)
|
||||
periodic.Interval = TimeSpan.FromMilliseconds((long)modifyWorkerRecord.IntervalMs);
|
||||
|
||||
return Accepted(worker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the Job with the requested ID
|
||||
/// Starts <see cref="BaseWorker"/> with <paramref name="WorkerId"/>
|
||||
/// </summary>
|
||||
/// <param name="JobId">Job-ID</param>
|
||||
/// <param name="startDependencies">Start Jobs necessary for execution</param>
|
||||
/// <response code="202">Job started</response>
|
||||
/// <response code="404">Job with ID not found</response>
|
||||
/// <response code="409">Job was already running</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("{JobId}/Start")]
|
||||
/// <param name="WorkerId"><see cref="BaseWorker"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404"><see cref="BaseWorker"/> with <paramref name="WorkerId"/> could not be found</response>
|
||||
/// <response code="412"><see cref="BaseWorker"/> was already running</response>
|
||||
[HttpPost("{WorkerId}/Start")]
|
||||
[ProducesResponseType(Status202Accepted)]
|
||||
[ProducesResponseType(Status404NotFound)]
|
||||
[ProducesResponseType(Status409Conflict)]
|
||||
[ProducesResponseType<string>(Status500InternalServerError, "text/plain")]
|
||||
public IActionResult StartJob(string JobId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]bool startDependencies = false)
|
||||
[ProducesResponseType<string>(Status412PreconditionFailed, "text/plain")]
|
||||
public IActionResult StartJob(string WorkerId)
|
||||
{
|
||||
Job? ret = context.Jobs.Find(JobId);
|
||||
if (ret is null)
|
||||
return NotFound();
|
||||
List<Job> dependencies = startDependencies ? ret.GetDependenciesAndSelf() : [ret];
|
||||
if(Tranga.Workers.FirstOrDefault(w => w.Key == WorkerId) is not { } worker)
|
||||
return NotFound(nameof(WorkerId));
|
||||
|
||||
try
|
||||
{
|
||||
if(dependencies.Any(d => d.state >= JobState.Running && d.state < JobState.Completed))
|
||||
return new ConflictResult();
|
||||
dependencies.ForEach(d =>
|
||||
{
|
||||
d.LastExecution = DateTime.UnixEpoch;
|
||||
d.state = JobState.CompletedWaiting;
|
||||
});
|
||||
context.SaveChanges();
|
||||
return Accepted();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
if (worker.State >= WorkerExecutionState.Waiting)
|
||||
return StatusCode(Status412PreconditionFailed, "Already running");
|
||||
|
||||
Tranga.StartWorker(worker);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the Job with the requested ID
|
||||
/// Stops <see cref="BaseWorker"/> with <paramref name="WorkerId"/>
|
||||
/// </summary>
|
||||
/// <param name="JobId">Job-ID</param>
|
||||
/// <remarks><h1>NOT IMPLEMENTED</h1></remarks>
|
||||
[HttpPost("{JobId}/Stop")]
|
||||
/// <param name="WorkerId"><see cref="BaseWorker"/>.Key</param>
|
||||
/// <response code="200"></response>
|
||||
/// <response code="404"><see cref="BaseWorker"/> with <paramref name="WorkerId"/> could not be found</response>
|
||||
/// <response code="208"><see cref="BaseWorker"/> was not running</response>
|
||||
[HttpPost("{WorkerId}/Stop")]
|
||||
[ProducesResponseType(Status501NotImplemented)]
|
||||
public IActionResult StopJob(string JobId)
|
||||
public IActionResult StopJob(string WorkerId)
|
||||
{
|
||||
return StatusCode(Status501NotImplemented);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes failed and completed Jobs (that are not recurring)
|
||||
/// </summary>
|
||||
/// <response code="202">Job started</response>
|
||||
/// <response code="500">Error during Database Operation</response>
|
||||
[HttpPost("Cleanup")]
|
||||
public IActionResult CleanupJobs()
|
||||
{
|
||||
try
|
||||
{
|
||||
context.Jobs.RemoveRange(context.Jobs.Where(j => j.state == JobState.Failed || j.state == JobState.Completed));
|
||||
context.SaveChanges();
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
return StatusCode(500, e.Message);
|
||||
}
|
||||
if(Tranga.Workers.FirstOrDefault(w => w.Key == WorkerId) is not { } worker)
|
||||
return NotFound(nameof(WorkerId));
|
||||
|
||||
if(worker.State is < WorkerExecutionState.Running or >= WorkerExecutionState.Completed)
|
||||
return StatusCode(Status208AlreadyReported, "Not running");
|
||||
|
||||
Tranga.StopWorker(worker);
|
||||
return Ok();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user