feat: implement WASM-compatible service proxies and update repository logic for ingestion state and database compatibility

This commit is contained in:
2026-05-13 20:03:55 +02:00
parent 150cbcdc29
commit 92ea11a51a
11 changed files with 330 additions and 32 deletions
+72
View File
@@ -20,6 +20,9 @@ using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using NexusReader.Infrastructure.Services;
using Stripe;
using Microsoft.Extensions.AI;
using NexusReader.Application.Abstractions.Persistence;
using NexusReader.Application.Abstractions.Messaging;
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
@@ -250,6 +253,13 @@ app.MapGet("/api/epub/{ebookId:guid}/{index:int}", async (Guid ebookId, int inde
return Results.BadRequest(errorMsg);
}).RequireAuthorization();
// Proxy API for AI services (Embeddings)
app.MapPost("/api/ai/embeddings", async (EmbeddingsRequest request, IEmbeddingGenerator<string, Embedding<float>> generator) =>
{
var result = await generator.GenerateAsync(request.Values, request.Options);
return Results.Ok(result);
}).RequireAuthorization();
var knowledgeApi = app.MapGroup("/api/knowledge").RequireAuthorization("HasAvailableTokens");
knowledgeApi.MapPost("/", async (KnowledgeRequest request, ClaimsPrincipal user, IKnowledgeService knowledgeService) =>
@@ -293,6 +303,63 @@ knowledgeApi.MapDelete("/", async (IKnowledgeService knowledgeService) =>
return Results.BadRequest(errorMsg);
});
// Proxy API for WASM Repository calls
var repoApi = app.MapGroup("/api/repository").RequireAuthorization();
repoApi.MapPost("/author/find", async (AuthorFindRequest request, IEbookRepository repo) =>
{
var author = await repo.FindAuthorByNameAsync(request.Name);
return author != null ? Results.Ok(author) : Results.NotFound();
});
repoApi.MapPost("/author/add", (Author author, IEbookRepository repo) =>
{
repo.AddAuthor(author);
return Results.Ok();
});
repoApi.MapPost("/ebook/add", (Ebook ebook, IEbookRepository repo) =>
{
repo.AddEbook(ebook);
return Results.Ok();
});
repoApi.MapPost("/save", async (IEbookRepository repo) =>
{
await repo.SaveChangesAsync();
return Results.Ok();
});
// Proxy API for WASM Broadcaster calls
var broadcasterApi = app.MapGroup("/api/broadcaster").RequireAuthorization();
broadcasterApi.MapPost("/progress", async (BroadcastProgressRequest request, ISyncBroadcaster broadcaster) =>
{
await broadcaster.BroadcastProgressAsync(request.UserId, request.PageId, request.Timestamp, request.ExcludedConnectionId);
return Results.Ok();
});
broadcasterApi.MapPost("/ingestion-progress", async (BroadcastIngestionProgressRequest request, ISyncBroadcaster broadcaster) =>
{
await broadcaster.BroadcastIngestionProgressAsync(request.UserId, request.Message, request.Progress);
return Results.Ok();
});
// Proxy API for WASM Storage calls
var storageApi = app.MapGroup("/api/storage").RequireAuthorization();
storageApi.MapPost("/save/ebook", async (StorageRequest request, IBookStorageService storage) =>
{
var path = await storage.SaveEbookAsync(request.Data, request.FileName);
return Results.Ok(new { Path = path });
});
storageApi.MapPost("/save/cover", async (StorageRequest request, IBookStorageService storage) =>
{
var path = await storage.SaveCoverAsync(request.Data, request.FileName);
return Results.Ok(new { Path = path });
});
app.MapPost("/api/library/ingest", async ([FromBody] IngestEbookRequest request, ClaimsPrincipal user, IMediator mediator) =>
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
@@ -518,3 +585,8 @@ app.Run();
public record KnowledgeRequest(string Text);
public record GroundednessRequest(string Answer, string Context);
public record AuthorFindRequest(string Name);
public record BroadcastProgressRequest(string UserId, string PageId, DateTime Timestamp, string? ExcludedConnectionId);
public record BroadcastIngestionProgressRequest(string UserId, string Message, double Progress);
public record StorageRequest(byte[] Data, string FileName);
public record EmbeddingsRequest(IEnumerable<string> Values, EmbeddingGenerationOptions? Options);