diff --git a/API/APIEndpointRecords/GotifyRecord.cs b/API/APIEndpointRecords/GotifyRecord.cs
index 59c5287..28e2314 100644
--- a/API/APIEndpointRecords/GotifyRecord.cs
+++ b/API/APIEndpointRecords/GotifyRecord.cs
@@ -1,14 +1,14 @@
namespace API.APIEndpointRecords;
-public record GotifyRecord(string endpoint, string appToken, int priority)
+public record GotifyRecord(string Name, string Endpoint, string AppToken, int Priority)
{
public bool Validate()
{
- if (endpoint == string.Empty)
+ if (Endpoint == string.Empty)
return false;
- if (appToken == string.Empty)
+ if (AppToken == string.Empty)
return false;
- if (priority < 0 || priority > 10)
+ if (Priority < 0 || Priority > 10)
return false;
return true;
diff --git a/API/APIEndpointRecords/ModifyJobRecord.cs b/API/APIEndpointRecords/ModifyJobRecord.cs
deleted file mode 100644
index 4e110fa..0000000
--- a/API/APIEndpointRecords/ModifyJobRecord.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace API.APIEndpointRecords;
-
-public record ModifyJobRecord(ulong? RecurrenceMs, bool? Enabled);
\ No newline at end of file
diff --git a/API/APIEndpointRecords/ModifyWorkerRecord.cs b/API/APIEndpointRecords/ModifyWorkerRecord.cs
new file mode 100644
index 0000000..91434aa
--- /dev/null
+++ b/API/APIEndpointRecords/ModifyWorkerRecord.cs
@@ -0,0 +1,3 @@
+namespace API.APIEndpointRecords;
+
+public record ModifyWorkerRecord(ulong? IntervalMs);
\ No newline at end of file
diff --git a/API/APIEndpointRecords/NewLibraryRecord.cs b/API/APIEndpointRecords/NewLibraryRecord.cs
deleted file mode 100644
index b30b7b5..0000000
--- a/API/APIEndpointRecords/NewLibraryRecord.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace API.APIEndpointRecords;
-
-public record NewLibraryRecord(string path, string name)
-{
- public bool Validate()
- {
- if (path.Length < 1) //TODO Better Path validation
- return false;
- if (name.Length < 1)
- return false;
- return true;
- }
-}
\ No newline at end of file
diff --git a/API/APIEndpointRecords/NtfyRecord.cs b/API/APIEndpointRecords/NtfyRecord.cs
index a8c2c22..8aa3099 100644
--- a/API/APIEndpointRecords/NtfyRecord.cs
+++ b/API/APIEndpointRecords/NtfyRecord.cs
@@ -1,16 +1,16 @@
namespace API.APIEndpointRecords;
-public record NtfyRecord(string endpoint, string username, string password, string topic, int priority)
+public record NtfyRecord(string Name, string Endpoint, string Username, string Password, string Topic, int Priority)
{
public bool Validate()
{
- if (endpoint == string.Empty)
+ if (Endpoint == string.Empty)
return false;
- if (username == string.Empty)
+ if (Username == string.Empty)
return false;
- if (password == string.Empty)
+ if (Password == string.Empty)
return false;
- if (priority < 1 || priority > 5)
+ if (Priority < 1 || Priority > 5)
return false;
return true;
}
diff --git a/API/APIEndpointRecords/PushoverRecord.cs b/API/APIEndpointRecords/PushoverRecord.cs
index 0f79919..6086da6 100644
--- a/API/APIEndpointRecords/PushoverRecord.cs
+++ b/API/APIEndpointRecords/PushoverRecord.cs
@@ -1,12 +1,12 @@
namespace API.APIEndpointRecords;
-public record PushoverRecord(string apptoken, string user)
+public record PushoverRecord(string Name, string AppToken, string User)
{
public bool Validate()
{
- if (apptoken == string.Empty)
+ if (AppToken == string.Empty)
return false;
- if (user == string.Empty)
+ if (User == string.Empty)
return false;
return true;
}
diff --git a/API/Controllers/FileLibraryController.cs b/API/Controllers/FileLibraryController.cs
new file mode 100644
index 0000000..4611e4f
--- /dev/null
+++ b/API/Controllers/FileLibraryController.cs
@@ -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
+{
+ ///
+ /// Returns all
+ ///
+ ///
+ [HttpGet]
+ [ProducesResponseType(Status200OK, "application/json")]
+ public IActionResult GetFileLibraries()
+ {
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+
+ return Ok(context.FileLibraries.ToArray());
+ }
+
+ ///
+ /// Returns with
+ ///
+ /// .Key
+ ///
+ /// with not found.
+ [HttpGet("{FileLibraryId}")]
+ [ProducesResponseType(Status200OK, "application/json")]
+ [ProducesResponseType(Status404NotFound)]
+ public IActionResult GetFileLibrary(string FileLibraryId)
+ {
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ if (context.FileLibraries.Find(FileLibraryId) is not { } library)
+ return NotFound();
+
+ return Ok(library);
+ }
+
+ ///
+ /// Changes the .BasePath with
+ ///
+ /// .Key
+ /// New .BasePath
+ ///
+ /// with not found.
+ /// Error during Database Operation
+ [HttpPatch("{FileLibraryId}/ChangeBasePath")]
+ [ProducesResponseType(Status200OK)]
+ [ProducesResponseType(Status404NotFound)]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult ChangeLibraryBasePath(string FileLibraryId, [FromBody]string newBasePath)
+ {
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ 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();
+ }
+
+ ///
+ /// Changes the .LibraryName with
+ ///
+ /// .Key
+ /// New .LibraryName
+ ///
+ /// with not found.
+ /// Error during Database Operation
+ [HttpPatch("{FileLibraryId}/ChangeName")]
+ [ProducesResponseType(Status200OK)]
+ [ProducesResponseType(Status404NotFound)]
+ [ProducesResponseType(Status400BadRequest)]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult ChangeLibraryName(string FileLibraryId, [FromBody] string newName)
+ {
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ 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();
+ }
+
+ ///
+ /// Creates new
+ ///
+ /// New to add
+ ///
+ /// Error during Database Operation
+ [HttpPut]
+ [ProducesResponseType(Status201Created)]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult CreateNewLibrary([FromBody]FileLibrary library)
+ {
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+
+ //TODO Parameter check
+ context.FileLibraries.Add(library);
+
+ if(context.Sync().Result is { } errorMessage)
+ return StatusCode(Status500InternalServerError, errorMessage);
+ return Created();
+ }
+
+ ///
+ /// Deletes the .LibraryName with
+ ///
+ /// .Key
+ ///
+ /// Error during Database Operation
+ [HttpDelete("{FileLibraryId}")]
+ [ProducesResponseType(Status200OK)]
+ [ProducesResponseType(Status404NotFound)]
+ [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ public IActionResult DeleteLocalLibrary(string FileLibraryId)
+ {
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ 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();
+ }
+}
\ No newline at end of file
diff --git a/API/Controllers/LibraryConnectorController.cs b/API/Controllers/LibraryConnectorController.cs
index f69fdf8..f947ebc 100644
--- a/API/Controllers/LibraryConnectorController.cs
+++ b/API/Controllers/LibraryConnectorController.cs
@@ -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
{
///
- /// Gets all configured ToFileLibrary-Connectors
+ /// Gets all configured
///
///
[HttpGet]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetAllConnectors()
{
+ LibraryContext context = scope.ServiceProvider.GetRequiredService();
+
LibraryConnector[] connectors = context.LibraryConnectors.ToArray();
+
return Ok(connectors);
}
///
- /// Returns ToFileLibrary-Connector with requested ID
+ /// Returns with
///
- /// ToFileLibrary-Connector-ID
+ /// .Key
///
- /// Connector with ID not found.
- [HttpGet("{LibraryControllerId}")]
+ /// with not found.
+ [HttpGet("{LibraryConnectorId}")]
[ProducesResponseType(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();
+ if (context.LibraryConnectors.Find(LibraryConnectorId) is not { } connector)
+ return NotFound();
+
+ return Ok(connector);
}
///
- /// Creates a new ToFileLibrary-Connector
+ /// Creates a new
///
- /// ToFileLibrary-Connector
+ ///
///
/// Error during Database Operation
[HttpPut]
@@ -54,46 +56,36 @@ public class LibraryConnectorController(LibraryContext context, ILog Log) : Cont
[ProducesResponseType(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();
+
+ context.LibraryConnectors.Add(libraryConnector);
+
+ if(context.Sync().Result is { } errorMessage)
+ return StatusCode(Status500InternalServerError, errorMessage);
+ return Created();
}
///
- /// Deletes the ToFileLibrary-Connector with the requested ID
+ /// Deletes with
///
- /// ToFileLibrary-Connector-ID
+ /// ToFileLibrary-Connector-ID
///
- /// Connector with ID not found.
+ /// with < not found.
/// Error during Database Operation
- [HttpDelete("{LibraryControllerId}")]
+ [HttpDelete("{LibraryConnectorId}")]
[ProducesResponseType(Status200OK)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(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();
+ 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();
}
}
\ No newline at end of file
diff --git a/API/Controllers/LocalLibrariesController.cs b/API/Controllers/LocalLibrariesController.cs
deleted file mode 100644
index 8302662..0000000
--- a/API/Controllers/LocalLibrariesController.cs
+++ /dev/null
@@ -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(Status200OK, "application/json")]
- public IActionResult GetLocalLibraries()
- {
- return Ok(context.LocalLibraries);
- }
-
- [HttpGet("{LibraryId}")]
- [ProducesResponseType(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(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(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(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(Status200OK, "application/json")]
- [ProducesResponseType(Status400BadRequest)]
- [ProducesResponseType(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(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);
- }
- }
-}
\ No newline at end of file
diff --git a/API/Controllers/MangaConnectorController.cs b/API/Controllers/MangaConnectorController.cs
index 60d8077..9e2ad72 100644
--- a/API/Controllers/MangaConnectorController.cs
+++ b/API/Controllers/MangaConnectorController.cs
@@ -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
{
///
- /// Get all available Connectors (Scanlation-Sites)
+ /// Get all (Scanlation-Sites)
///
- ///
+ /// Names of (Scanlation-Sites)
[HttpGet]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetConnectors()
{
- MangaConnector[] connectors = context.MangaConnectors.ToArray();
- return Ok(connectors);
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ return Ok(context.MangaConnectors.Select(c => c.Name).ToArray());
}
///
- /// Returns the MangaConnector with the requested Name
+ /// Returns the (Scanlation-Sites) with the requested Name
///
- ///
+ /// .Name
///
- /// Connector with ID not found.
- /// Error during Database Operation
+ /// (Scanlation-Sites) with Name not found.
[HttpGet("{MangaConnectorName}")]
[ProducesResponseType(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();
+ if(context.MangaConnectors.Find(MangaConnectorName) is not { } connector)
+ return NotFound();
+
+ return Ok(connector);
}
///
- /// Get all enabled Connectors (Scanlation-Sites)
+ /// Get all enabled (Scanlation-Sites)
///
///
- [HttpGet("enabled")]
+ [HttpGet("Enabled")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetEnabledConnectors()
{
- MangaConnector[] connectors = context.MangaConnectors.Where(c => c.Enabled == true).ToArray();
- return Ok(connectors);
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+
+ return Ok(context.MangaConnectors.Where(c => c.Enabled).ToArray());
}
///
- /// Get all disabled Connectors (Scanlation-Sites)
+ /// Get all disabled (Scanlation-Sites)
///
///
- [HttpGet("disabled")]
+ [HttpGet("Disabled")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetDisabledConnectors()
{
- MangaConnector[] connectors = context.MangaConnectors.Where(c => c.Enabled == false).ToArray();
- return Ok(connectors);
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+
+ return Ok(context.MangaConnectors.Where(c => c.Enabled == false).ToArray());
}
///
- /// Enabled or disables a Connector
+ /// Enabled or disables (Scanlation-Sites) with Name
///
- /// ID of the connector
- /// Set true to enable
- ///
- /// Connector with ID not found.
+ /// .Name
+ /// Set true to enable, false to disable
+ ///
+ /// (Scanlation-Sites) with Name not found.
/// Error during Database Operation
- [HttpPatch("{MangaConnectorName}/SetEnabled/{enabled}")]
- [ProducesResponseType(Status200OK)]
+ [HttpPatch("{MangaConnectorName}/SetEnabled/{Enabled}")]
+ [ProducesResponseType(Status202Accepted)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(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();
+ 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();
}
}
\ No newline at end of file
diff --git a/API/Controllers/MangaController.cs b/API/Controllers/MangaController.cs
index 554c339..45b6bba 100644
--- a/API/Controllers/MangaController.cs
+++ b/API/Controllers/MangaController.cs
@@ -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();
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);
diff --git a/API/Controllers/MetadataFetcherController.cs b/API/Controllers/MetadataFetcherController.cs
index 4653a2b..071bdb8 100644
--- a/API/Controllers/MetadataFetcherController.cs
+++ b/API/Controllers/MetadataFetcherController.cs
@@ -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
{
///
- /// Get all available Connectors (Metadata-Sites)
+ /// Get all (Metadata-Sites)
///
- /// Names of Metadata-Fetchers
+ /// Names of (Metadata-Sites)
[HttpGet]
[ProducesResponseType(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());
}
///
- /// Returns all Mangas which have a linked Metadata-Provider
+ /// Returns all
///
///
[HttpGet("Links")]
- [ProducesResponseType(Status200OK, "application/json")]
+ [ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetLinkedEntries()
{
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+
return Ok(context.MetadataEntries.ToArray());
}
///
- /// Searches Metadata-Provider for Manga-Metadata
+ /// Searches (Metadata-Sites) for Manga-Metadata
///
- /// Instead of using the Manga for search, use a specific term
+ /// .Key
+ /// .Name
+ /// Instead of using the for search on Website, use a specific term
///
- /// Metadata-fetcher with Name does not exist
- /// Manga with ID not found
+ /// (Metadata-Sites) with does not exist
+ /// with not found
[HttpPost("{MetadataFetcherName}/SearchManga/{MangaId}")]
[ProducesResponseType(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();
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
}
///
- /// Links Metadata-Provider using Provider-Specific Identifier to Manga
+ /// Links (Metadata-Sites) using Provider-Specific Identifier to
///
+ /// .Key
+ /// .Name
+ /// -Specific ID
///
- /// Metadata-fetcher with Name does not exist
- /// Manga with ID not found
+ /// (Metadata-Sites) with does not exist
+ /// with not found
/// Error during Database Operation
[HttpPost("{MetadataFetcherName}/Link/{MangaId}")]
- [ProducesResponseType(Status200OK)]
+ [ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult LinkMangaMetadata(string MangaId, string MetadataFetcherName, [FromBody]string Identifier)
{
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
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);
}
///
- /// Un-Links Metadata-Provider using Provider-Specific Identifier to Manga
+ /// Un-Links (Metadata-Sites) from
///
///
- /// Metadata-fetcher with Name does not exist
- /// Manga with ID not found
- /// No Entry linking Manga and Metadata-Provider found
+ /// (Metadata-Sites) with does not exist
+ /// with not found
+ /// No linking and found
/// Error during Database Operation
[HttpPost("{MetadataFetcherName}/Unlink/{MangaId}")]
[ProducesResponseType(Status200OK)]
@@ -108,58 +109,18 @@ public class MetadataFetcherController(MangaContext context, ILog Log) : Control
[ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult UnlinkMangaMetadata(string MangaId, string MetadataFetcherName)
{
- if(context.Mangas.Find(MangaId) is not { } manga)
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ 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();
- }
- ///
- /// Tries linking a Manga to a Metadata-Provider-Site
- ///
- ///
- /// MetadataFetcher Name is invalid
- /// Manga has no linked entry with MetadataFetcher
- /// Error during Database Operation
- [HttpPost("{MetadataFetcherName}/{MangaId}/UpdateMetadata")]
- [ProducesResponseType(Status200OK)]
- [ProducesResponseType(Status400BadRequest)]
- [ProducesResponseType(Status404NotFound)]
- [ProducesResponseType(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();
}
}
\ No newline at end of file
diff --git a/API/Controllers/NotificationConnectorController.cs b/API/Controllers/NotificationConnectorController.cs
index e521ad5..1da22be 100644
--- a/API/Controllers/NotificationConnectorController.cs
+++ b/API/Controllers/NotificationConnectorController.cs
@@ -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
{
///
- /// Gets all configured Notification-Connectors
+ /// Gets all configured
///
///
[HttpGet]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetAllConnectors()
{
- NotificationConnector[] ret = context.NotificationConnectors.ToArray();
- return Ok(ret);
+ NotificationsContext context = scope.ServiceProvider.GetRequiredService();
+
+ return Ok(context.NotificationConnectors.ToArray());
}
///
- /// Returns Notification-Connector with requested ID
+ /// Returns with requested Name
///
- /// Notification-Connector-ID
+ /// .Name
///
- /// NotificationConnector with ID not found
- [HttpGet("{NotificationConnectorId}")]
+ /// with not found
+ [HttpGet("{Name}")]
[ProducesResponseType(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();
+ if(context.NotificationConnectors.Find(Name) is not { } connector)
+ return NotFound();
+
+ return Ok(connector);
}
///
- /// Creates a new REST-Notification-Connector
+ /// Creates a new
///
/// Formatting placeholders: "%title" and "%text" can be placed in url, header-values and body and will be replaced when notifications are sent
- /// Notification-Connector
- /// ID of new connector
- /// A NotificationConnector with name already exists
+ ///
/// Error during Database Operation
[HttpPut]
- [ProducesResponseType(Status201Created, "application/json")]
+ [ProducesResponseType(Status201Created)]
[ProducesResponseType(Status409Conflict)]
[ProducesResponseType(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();
+
+ context.NotificationConnectors.Add(notificationConnector);
+
+ if(context.Sync().Result is { } errorMessage)
+ return StatusCode(Status500InternalServerError, errorMessage);
+ return Created();
}
///
- /// Creates a new Gotify-Notification-Connector
+ /// Creates a new Gotify-
///
/// Priority needs to be between 0 and 10
- /// ID of new connector
- ///
- /// A NotificationConnector with name already exists
+ ///
/// Error during Database Operation
[HttpPut("Gotify")]
[ProducesResponseType(Status201Created, "application/json")]
- [ProducesResponseType(Status400BadRequest)]
- [ProducesResponseType(Status409Conflict)]
[ProducesResponseType(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() { { "X-Gotify-IDOnConnector", gotifyData.appToken } },
+ NotificationConnector gotifyConnector = new (gotifyData.Name,
+ gotifyData.Endpoint,
+ new Dictionary() { { "X-Gotify-IDOnConnector", gotifyData.AppToken } },
"POST",
- $"{{\"message\": \"%text\", \"title\": \"%title\", \"priority\": {gotifyData.priority}}}");
+ $"{{\"message\": \"%text\", \"title\": \"%title\", \"Priority\": {gotifyData.Priority}}}");
return CreateConnector(gotifyConnector);
}
///
- /// Creates a new Ntfy-Notification-Connector
+ /// Creates a new Ntfy-
///
/// Priority needs to be between 1 and 5
- /// ID of new connector
- ///
- /// A NotificationConnector with name already exists
+ ///
/// Error during Database Operation
[HttpPut("Ntfy")]
[ProducesResponseType(Status201Created, "application/json")]
- [ProducesResponseType(Status400BadRequest)]
- [ProducesResponseType(Status409Conflict)]
[ProducesResponseType(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()
{
{"Title", "%title"},
- {"Priority", ntfyRecord.priority.ToString()},
+ {"Priority", ntfyRecord.Priority.ToString()},
},
"POST",
"%text");
@@ -135,58 +117,47 @@ public class NotificationConnectorController(NotificationsContext context, ILog
}
///
- /// Creates a new Pushover-Notification-Connector
+ /// Creates a new Pushover-
///
/// https://pushover.net/api
/// ID of new connector
- ///
- /// A NotificationConnector with name already exists
/// Error during Database Operation
[HttpPut("Pushover")]
[ProducesResponseType(Status201Created, "application/json")]
- [ProducesResponseType(Status400BadRequest)]
- [ProducesResponseType(Status409Conflict)]
[ProducesResponseType(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(),
"POST",
- $"{{\"token\": \"{pushoverRecord.apptoken}\", \"user\": \"{pushoverRecord.user}\", \"message:\":\"%text\", \"%title\" }}");
+ $"{{\"token\": \"{pushoverRecord.AppToken}\", \"user\": \"{pushoverRecord.User}\", \"message:\":\"%text\", \"%title\" }}");
return CreateConnector(pushoverConnector);
}
///
- /// Deletes the Notification-Connector with the requested ID
+ /// Deletes the with the requested Name
///
- /// Notification-Connector-ID
+ /// .Name
///
- /// NotificationConnector with ID not found
+ /// with Name not found
/// Error during Database Operation
- [HttpDelete("{NotificationConnectorId}")]
+ [HttpDelete("{Name}")]
[ProducesResponseType(Status200OK)]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(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();
+ 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();
}
}
\ No newline at end of file
diff --git a/API/Controllers/QueryController.cs b/API/Controllers/QueryController.cs
index d258260..3341162 100644
--- a/API/Controllers/QueryController.cs
+++ b/API/Controllers/QueryController.cs
@@ -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
{
///
- /// Returns the Author-Information for Author-ID
+ /// Returns the with
///
- /// Author-Id
+ /// .Key
///
- /// Author with ID not found
+ /// with not found
[HttpGet("Author/{AuthorId}")]
[ProducesResponseType(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();
+ if (context.Authors.Find(AuthorId) is not { } author)
return NotFound();
- return Ok(ret);
+
+ return Ok(author);
}
///
- /// Returns all Mangas which where Authored by Author with AuthorId
+ /// Returns all which where Authored by with
///
- /// Author-ID
+ /// .Key
///
- /// Author not found
+ /// with
[HttpGet("Mangas/WithAuthorId/{AuthorId}")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetMangaWithAuthorIds(string AuthorId)
{
- if(context.Authors.Find(AuthorId) is not { } a)
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ if (context.Authors.Find(AuthorId) is not { } author)
return NotFound();
- return Ok(context.Mangas.Where(m => m.Authors.Contains(a)));
- }
- /*
- ///
- /// Returns Link-Information for Link-Id
- ///
- ///
- ///
- /// Link with ID not found
- [HttpGet("Link/{LinkId}")]
- [ProducesResponseType(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)));
}
///
- /// Returns AltTitle-Information for AltTitle-Id
+ /// Returns all with
///
- ///
+ /// .Tag
///
- /// AltTitle with ID not found
- [HttpGet("AltTitle/{AltTitleId}")]
- [ProducesResponseType(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);
- }*/
-
- ///
- /// Returns all Obj with Tag
- ///
- ///
- ///
- /// Tag not found
+ /// not found
[HttpGet("Mangas/WithTag/{Tag}")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetMangasWithTag(string Tag)
{
- if(context.Tags.Find(Tag) is not { } t)
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ 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)));
}
///
- /// Returns Chapter-Information for Chapter-Id
+ /// Returns with
///
- ///
+ /// .Key
///
- /// Chapter with ID not found
+ /// with not found
[HttpGet("Chapter/{ChapterId}")]
[ProducesResponseType(Status200OK, "application/json")]
public IActionResult GetChapter(string ChapterId)
{
- Chapter? ret = context.Chapters.Find(ChapterId);
- if (ret is null)
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
+ if (context.Chapters.Find(ChapterId) is not { } chapter)
return NotFound();
- return Ok(ret);
+
+ return Ok(chapter);
}
}
\ No newline at end of file
diff --git a/API/Controllers/SearchController.cs b/API/Controllers/SearchController.cs
index 9edd200..258c72b 100644
--- a/API/Controllers/SearchController.cs
+++ b/API/Controllers/SearchController.cs
@@ -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
{
///
- /// Initiate a search for a Obj on a specific Connector
+ /// Initiate a search for a on with searchTerm
///
- ///
- ///
+ /// .Name
+ /// searchTerm
///
- /// MangaConnector with ID not found
- /// MangaConnector with ID is disabled
- /// Error during Database Operation
+ /// with Name not found
+ /// with Name is disabled
[HttpGet("{MangaConnectorName}/{Query}")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
[ProducesResponseType(Status406NotAcceptable)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult SearchManga(string MangaConnectorName, string Query)
{
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
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)[] mangas = connector.SearchManga(Query);
List retMangas = new();
foreach ((Manga manga, MangaConnectorId 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());
}
-
- ///
- /// Search for a known Obj
- ///
- ///
- ///
- [HttpGet("Local/{Query}")]
- [ProducesResponseType(Status200OK, "application/json")]
- public IActionResult SearchMangaLocally(string Query)
- {
- Dictionary 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());
- }
///
- /// Returns Obj from MangaConnector associated with URL
+ /// Returns from the associated with
///
- /// Obj-Page URL
+ ///
///
- /// Multiple connectors found for URL
- /// Obj not found
+ /// Multiple found for URL
+ /// not found
/// Error during Database Operation
[HttpPost("Url")]
[ProducesResponseType(Status200OK, "application/json")]
[ProducesResponseType(Status404NotFound)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
+ [ProducesResponseType(Status500InternalServerError)]
public IActionResult GetMangaFromUrl([FromBody]string url)
{
+ MangaContext context = scope.ServiceProvider.GetRequiredService();
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) => AddMangaToContext(manga.Item1, manga.Item2, context);
-
- internal static Manga? AddMangaToContext(Manga addManga, MangaConnectorId addMcId, MangaContext context)
+ private Manga? AddMangaToContext((Manga, MangaConnectorId) manga, MangaContext context) => AddMangaToContext(manga.Item1, manga.Item2, context);
+
+ private static Manga? AddMangaToContext(Manga addManga, MangaConnectorId addMcId, MangaContext context)
{
Manga manga = context.Mangas.Find(addManga.Key) ?? addManga;
MangaConnectorId 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;
}
}
\ No newline at end of file
diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs
index 09a2a48..597f92c 100644
--- a/API/Controllers/SettingsController.cs
+++ b/API/Controllers/SettingsController.cs
@@ -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
{
///
/// 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
///
///
- /// Error during Database Operation
[HttpPatch("ChapterNamingScheme")]
[ProducesResponseType(Status200OK)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
public IActionResult SetCustomNamingScheme([FromBody]string namingScheme)
{
- try
- {
- Dictionary 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);
- }
- }
-
- ///
- /// Creates a UpdateCoverJob for all Obj
- ///
- /// Array of JobIds
- /// Error during Database Operation
- [HttpPost("CleanupCovers")]
- [ProducesResponseType(Status200OK)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult CleanupCovers()
- {
- try
- {
- Tranga.RemoveStaleFiles(context);
- List 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();
+
+ Dictionary 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();
}
///
diff --git a/API/Controllers/WorkerController.cs b/API/Controllers/WorkerController.cs
index 4349433..0b3816e 100644
--- a/API/Controllers/WorkerController.cs
+++ b/API/Controllers/WorkerController.cs
@@ -84,110 +84,73 @@ public class WorkerController(ILog Log) : Controller
}
///
- /// Modify Job with ID
+ /// Modify with
///
- /// Job-ID
- /// Fields to modify, set to null to keep previous value
- /// Job modified
- /// Malformed request
- /// Job with ID not found
- /// Error during Database Operation
- [HttpPatch("{JobId}")]
- [ProducesResponseType(Status202Accepted, "application/json")]
+ /// .Key
+ /// Fields to modify, set to null to keep previous value
+ ///
+ ///
+ /// with could not be found
+ /// is not , can not modify
+ [HttpPatch("{WorkerId}")]
+ [ProducesResponseType(Status202Accepted, "application/json")]
[ProducesResponseType(Status400BadRequest)]
[ProducesResponseType(Status404NotFound)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult ModifyJob(string JobId, [FromBody]ModifyJobRecord modifyJobRecord)
+ [ProducesResponseType(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);
}
///
- /// Starts the Job with the requested ID
+ /// Starts with
///
- /// Job-ID
- /// Start Jobs necessary for execution
- /// Job started
- /// Job with ID not found
- /// Job was already running
- /// Error during Database Operation
- [HttpPost("{JobId}/Start")]
+ /// .Key
+ ///
+ /// with could not be found
+ /// was already running
+ [HttpPost("{WorkerId}/Start")]
[ProducesResponseType(Status202Accepted)]
[ProducesResponseType(Status404NotFound)]
- [ProducesResponseType(Status409Conflict)]
- [ProducesResponseType(Status500InternalServerError, "text/plain")]
- public IActionResult StartJob(string JobId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)]bool startDependencies = false)
+ [ProducesResponseType(Status412PreconditionFailed, "text/plain")]
+ public IActionResult StartJob(string WorkerId)
{
- Job? ret = context.Jobs.Find(JobId);
- if (ret is null)
- return NotFound();
- List 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();
}
///
- /// Stops the Job with the requested ID
+ /// Stops with
///
- /// Job-ID
- /// NOT IMPLEMENTED
- [HttpPost("{JobId}/Stop")]
+ /// .Key
+ ///
+ /// with could not be found
+ /// was not running
+ [HttpPost("{WorkerId}/Stop")]
[ProducesResponseType(Status501NotImplemented)]
- public IActionResult StopJob(string JobId)
+ public IActionResult StopJob(string WorkerId)
{
- return StatusCode(Status501NotImplemented);
- }
-
- ///
- /// Removes failed and completed Jobs (that are not recurring)
- ///
- /// Job started
- /// Error during Database Operation
- [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();
}
}
\ No newline at end of file
diff --git a/API/Program.cs b/API/Program.cs
index 1b5cc90..9e013d8 100644
--- a/API/Program.cs
+++ b/API/Program.cs
@@ -118,8 +118,8 @@ using (IServiceScope scope = app.Services.CreateScope())
];
MangaConnector[] newConnectors = connectors.Where(c => !context.MangaConnectors.Contains(c)).ToArray();
context.MangaConnectors.AddRange(newConnectors);
- if (!context.LocalLibraries.Any())
- context.LocalLibraries.Add(new FileLibrary(TrangaSettings.downloadLocation, "Default FileLibrary"));
+ if (!context.FileLibraries.Any())
+ context.FileLibraries.Add(new FileLibrary(TrangaSettings.downloadLocation, "Default FileLibrary"));
context.Sync();
}
diff --git a/API/Schema/LibraryContext/LibraryConnectors/Kavita.cs b/API/Schema/LibraryContext/LibraryConnectors/Kavita.cs
index 4543c40..04bcdf8 100644
--- a/API/Schema/LibraryContext/LibraryConnectors/Kavita.cs
+++ b/API/Schema/LibraryContext/LibraryConnectors/Kavita.cs
@@ -6,7 +6,7 @@ namespace API.Schema.LibraryContext.LibraryConnectors;
public class Kavita : LibraryConnector
{
- public Kavita(string baseUrl, string auth) : base(TokenGen.CreateToken(typeof(Kavita), baseUrl), LibraryType.Kavita, baseUrl, auth)
+ public Kavita(string baseUrl, string auth) : base(LibraryType.Kavita, baseUrl, auth)
{
}
diff --git a/API/Schema/LibraryContext/LibraryConnectors/Komga.cs b/API/Schema/LibraryContext/LibraryConnectors/Komga.cs
index 2e94e84..601f4ab 100644
--- a/API/Schema/LibraryContext/LibraryConnectors/Komga.cs
+++ b/API/Schema/LibraryContext/LibraryConnectors/Komga.cs
@@ -5,8 +5,7 @@ namespace API.Schema.LibraryContext.LibraryConnectors;
public class Komga : LibraryConnector
{
- public Komga(string baseUrl, string auth) : base(TokenGen.CreateToken(typeof(Komga), baseUrl), LibraryType.Komga,
- baseUrl, auth)
+ public Komga(string baseUrl, string auth) : base(LibraryType.Komga, baseUrl, auth)
{
}
diff --git a/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs b/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs
index 750fd16..e58bba2 100644
--- a/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs
+++ b/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs
@@ -7,26 +7,51 @@ using Newtonsoft.Json;
namespace API.Schema.LibraryContext.LibraryConnectors;
[PrimaryKey("LibraryConnectorId")]
-public abstract class LibraryConnector(string libraryConnectorId, LibraryType libraryType, string baseUrl, string auth)
+public abstract class LibraryConnector : Identifiable
{
- [StringLength(64)]
[Required]
- public string LibraryConnectorId { get; } = libraryConnectorId;
-
- [Required]
- public LibraryType LibraryType { get; init; } = libraryType;
+ public LibraryType LibraryType { get; init; }
[StringLength(256)]
[Required]
[Url]
- public string BaseUrl { get; init; } = baseUrl;
+ public string BaseUrl { get; init; }
[StringLength(256)]
[Required]
- public string Auth { get; init; } = auth;
+ public string Auth { get; init; }
[JsonIgnore]
[NotMapped]
- protected ILog Log { get; init; } = LogManager.GetLogger($"{libraryType.ToString()} {baseUrl}");
+ protected ILog Log { get; init; }
+
+ protected LibraryConnector(LibraryType libraryType, string baseUrl, string auth)
+ : base()
+ {
+ this.LibraryType = libraryType;
+ this.BaseUrl = baseUrl;
+ this.Auth = auth;
+ this.Log = LogManager.GetLogger(GetType());
+ }
+
+ ///
+ /// EF CORE ONLY!!!!
+ ///
+ internal LibraryConnector(string key, LibraryType libraryType, string baseUrl, string auth)
+ : base(key)
+ {
+ this.LibraryType = libraryType;
+ this.BaseUrl = baseUrl;
+ this.Auth = auth;
+ this.Log = LogManager.GetLogger(GetType());
+ }
+
+ public override string ToString() => $"{base.ToString()} {this.LibraryType} {this.BaseUrl}";
protected abstract void UpdateLibraryInternal();
internal abstract bool Test();
+}
+
+public enum LibraryType : byte
+{
+ Komga = 0,
+ Kavita = 1
}
\ No newline at end of file
diff --git a/API/Schema/LibraryContext/LibraryConnectors/LibraryType.cs b/API/Schema/LibraryContext/LibraryConnectors/LibraryType.cs
deleted file mode 100644
index b2454a8..0000000
--- a/API/Schema/LibraryContext/LibraryConnectors/LibraryType.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace API.Schema.LibraryContext.LibraryConnectors;
-
-public enum LibraryType : byte
-{
- Komga = 0,
- Kavita = 1
-}
\ No newline at end of file
diff --git a/API/Schema/MangaContext/MangaContext.cs b/API/Schema/MangaContext/MangaContext.cs
index 2371690..3e8e9e6 100644
--- a/API/Schema/MangaContext/MangaContext.cs
+++ b/API/Schema/MangaContext/MangaContext.cs
@@ -8,7 +8,7 @@ public class MangaContext(DbContextOptions options) : TrangaBaseCo
{
public DbSet MangaConnectors { get; set; }
public DbSet Mangas { get; set; }
- public DbSet LocalLibraries { get; set; }
+ public DbSet FileLibraries { get; set; }
public DbSet Chapters { get; set; }
public DbSet Authors { get; set; }
public DbSet Tags { get; set; }
diff --git a/API/Schema/MangaContext/MetadataFetchers/MetadataEntry.cs b/API/Schema/MangaContext/MetadataFetchers/MetadataEntry.cs
index e8550fa..a239a08 100644
--- a/API/Schema/MangaContext/MetadataFetchers/MetadataEntry.cs
+++ b/API/Schema/MangaContext/MetadataFetchers/MetadataEntry.cs
@@ -3,7 +3,7 @@ using Newtonsoft.Json;
namespace API.Schema.MangaContext.MetadataFetchers;
-[PrimaryKey("MetadataFetcherName", "Identifier")]
+[PrimaryKey("Name", "Identifier")]
public class MetadataEntry
{
[JsonIgnore]
@@ -19,7 +19,7 @@ public class MetadataEntry
this.Manga = manga;
this.MangaId = manga.Key;
this.MetadataFetcher = fetcher;
- this.MetadataFetcherName = fetcher.MetadataFetcherName;
+ this.MetadataFetcherName = fetcher.Name;
this.Identifier = identifier;
}
diff --git a/API/Schema/MangaContext/MetadataFetchers/MetadataFetcher.cs b/API/Schema/MangaContext/MetadataFetchers/MetadataFetcher.cs
index 48f8104..37c36b8 100644
--- a/API/Schema/MangaContext/MetadataFetchers/MetadataFetcher.cs
+++ b/API/Schema/MangaContext/MetadataFetchers/MetadataFetcher.cs
@@ -2,23 +2,23 @@ using Microsoft.EntityFrameworkCore;
namespace API.Schema.MangaContext.MetadataFetchers;
-[PrimaryKey("MetadataFetcherName")]
+[PrimaryKey("Name")]
public abstract class MetadataFetcher
{
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
- public string MetadataFetcherName { get; init; }
+ public string Name { get; init; }
protected MetadataFetcher()
{
- this.MetadataFetcherName = this.GetType().Name;
+ this.Name = this.GetType().Name;
}
///
/// EFCORE ONLY!!!
///
- internal MetadataFetcher(string metadataFetcherName)
+ internal MetadataFetcher(string name)
{
- this.MetadataFetcherName = metadataFetcherName;
+ this.Name = name;
}
internal MetadataEntry CreateMetadataEntry(Manga manga, string identifier) =>
diff --git a/API/Tranga.cs b/API/Tranga.cs
index ac6eb83..7a8c6c1 100644
--- a/API/Tranga.cs
+++ b/API/Tranga.cs
@@ -71,4 +71,14 @@ public static class Tranga
Thread.Sleep(TrangaSettings.workCycleTimeout);
}
}
+
+ internal static void StartWorker(BaseWorker worker)
+ {
+ throw new NotImplementedException();
+ }
+
+ internal static void StopWorker(BaseWorker worker)
+ {
+ throw new NotImplementedException();
+ }
}
\ No newline at end of file
diff --git a/API/Workers/BaseWorker.cs b/API/Workers/BaseWorker.cs
index 497affa..49b6dca 100644
--- a/API/Workers/BaseWorker.cs
+++ b/API/Workers/BaseWorker.cs
@@ -13,7 +13,12 @@ public abstract class BaseWorker : Identifiable
internal WorkerExecutionState State { get; set; }
private static readonly CancellationTokenSource CancellationTokenSource = new(TimeSpan.FromMinutes(10));
protected ILog Log { get; init; }
- public void Cancel() => CancellationTokenSource.Cancel();
+
+ public void Cancel()
+ {
+ this.State = WorkerExecutionState.Cancelled;
+ CancellationTokenSource.Cancel();
+ }
protected void Fail() => this.State = WorkerExecutionState.Failed;
public BaseWorker(IEnumerable? dependsOn = null)
@@ -58,5 +63,6 @@ public enum WorkerExecutionState
Created = 64,
Waiting = 96,
Running = 128,
- Completed = 192
+ Completed = 192,
+ Cancelled = 193
}
\ No newline at end of file
diff --git a/API/Workers/IPeriodic.cs b/API/Workers/IPeriodic.cs
index 4260f9a..14c0fc3 100644
--- a/API/Workers/IPeriodic.cs
+++ b/API/Workers/IPeriodic.cs
@@ -1,9 +1,9 @@
namespace API.Workers;
-public interface IPeriodic where T : BaseWorker
+public interface IPeriodic
{
protected DateTime LastExecution { get; set; }
- protected TimeSpan Interval { get; set; }
-
+ public TimeSpan Interval { get; set; }
public DateTime NextExecution => LastExecution.Add(Interval);
+ public bool IsDue => NextExecution <= DateTime.UtcNow;
}
\ No newline at end of file