using API.Schema.MangaContext; using API.Schema.MangaContext.MetadataFetchers; using Asp.Versioning; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.EntityFrameworkCore; using static Microsoft.AspNetCore.Http.StatusCodes; // ReSharper disable InconsistentNaming namespace API.Controllers; [ApiVersion(2)] [ApiController] [Route("v{v:apiVersion}/[controller]")] public class MetadataFetcherController(MangaContext context) : Controller { /// /// Get all (Metadata-Sites) /// /// Names of (Metadata-Sites) [HttpGet] [ProducesResponseType>(Status200OK, "application/json")] public Ok> GetConnectors () { return TypedResults.Ok(Tranga.MetadataFetchers.Select(m => m.Name).ToList()); } /// /// Returns all /// /// /// Error during Database Operation [HttpGet("Links")] [ProducesResponseType>(Status200OK, "application/json")] [ProducesResponseType(Status500InternalServerError)] public async Task>, InternalServerError>> GetLinkedEntries () { if (await context.MetadataEntries.ToListAsync() is not { } result) return TypedResults.InternalServerError(); return TypedResults.Ok(result); } /// /// Searches (Metadata-Sites) for Manga-Metadata /// /// .Key /// .Name /// Instead of using the for search on Website, use a specific term /// /// (Metadata-Sites) with does not exist /// with not found [HttpPost("{MetadataFetcherName}/SearchManga/{MangaId}")] [ProducesResponseType(Status200OK, "application/json")] [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status404NotFound, "text/plain")] public async Task>, BadRequest, NotFound>> SearchMangaMetadata(string MangaId, string MetadataFetcherName, [FromBody (EmptyBodyBehavior = EmptyBodyBehavior.Allow)]string? searchTerm = null) { if (await context.Mangas.FirstOrDefaultAsync(m => m.Key == MangaId, HttpContext.RequestAborted) is not { } manga) return TypedResults.NotFound(nameof(MangaId)); if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is not { } fetcher) return TypedResults.BadRequest(); MetadataSearchResult[] searchResults = searchTerm is null ? fetcher.SearchMetadataEntry(manga) : fetcher.SearchMetadataEntry(searchTerm); return TypedResults.Ok(searchResults.ToList()); } /// /// Links (Metadata-Sites) using Provider-Specific Identifier to /// /// .Key /// .Name /// -Specific ID /// /// (Metadata-Sites) with does not exist /// with not found /// Error during Database Operation [HttpPost("{MetadataFetcherName}/Link/{MangaId}")] [ProducesResponseType(Status200OK, "application/json")] [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status404NotFound, "text/plain")] [ProducesResponseType(Status500InternalServerError, "text/plain")] public async Task, BadRequest, NotFound, InternalServerError>> LinkMangaMetadata (string MangaId, string MetadataFetcherName, [FromBody]string Identifier) { if (await context.Mangas.FirstOrDefaultAsync(m => m.Key == MangaId, HttpContext.RequestAborted) is not { } manga) return TypedResults.NotFound(nameof(MangaId)); if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is not { } fetcher) return TypedResults.BadRequest(); MetadataEntry entry = fetcher.CreateMetadataEntry(manga, Identifier); context.MetadataEntries.Add(entry); if(await context.Sync(HttpContext.RequestAborted) is { success: false } result) return TypedResults.InternalServerError(result.exceptionMessage); return TypedResults.Ok(entry); } /// /// Un-Links (Metadata-Sites) from /// /// /// (Metadata-Sites) with does not exist /// with not found /// No linking and found /// Error during Database Operation [HttpPost("{MetadataFetcherName}/Unlink/{MangaId}")] [ProducesResponseType(Status200OK)] [ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status404NotFound, "text/plain")] [ProducesResponseType(Status412PreconditionFailed)] [ProducesResponseType(Status500InternalServerError, "text/plain")] public async Task, InternalServerError, StatusCodeHttpResult>> UnlinkMangaMetadata (string MangaId, string MetadataFetcherName) { if (await context.Mangas.FirstOrDefaultAsync(m => m.Key == MangaId, HttpContext.RequestAborted) is not { } _) return TypedResults.NotFound(nameof(MangaId)); if(Tranga.MetadataFetchers.FirstOrDefault(f => f.Name == MetadataFetcherName) is null) return TypedResults.BadRequest(); if (context.MetadataEntries.FirstOrDefault(e => e.MangaId == MangaId && e.MetadataFetcherName == MetadataFetcherName) is not { } entry) return TypedResults.StatusCode(Status412PreconditionFailed); context.Remove(entry); if(await context.Sync(HttpContext.RequestAborted) is { success: false } result) return TypedResults.InternalServerError(result.exceptionMessage); return TypedResults.Ok(); } }