feat: implement background ebook indexing with progress tracking and real-time UI updates

This commit is contained in:
2026-05-25 08:51:21 +02:00
parent 39717725ec
commit 1c6ee82d01
20 changed files with 718 additions and 57 deletions
+49 -1
View File
@@ -194,6 +194,7 @@ using (var scope = app.Services.CreateScope())
await dbContext.Database.MigrateAsync();
await DbInitializer.SeedAsync(services);
await TriggerBackgroundProcessingForUnindexedBooksAsync(services);
if (logger.IsEnabled(LogLevel.Information))
{
@@ -337,13 +338,16 @@ app.MapPost("/api/library/ingest", async ([FromBody] IngestEbookRequest request,
? Convert.FromBase64String(request.CoverImageBase64)
: null;
var tenantId = user.FindFirst("TenantId")?.Value ?? "global";
var command = new IngestEbookCommand(
request.Title,
request.AuthorName,
coverData,
epubData,
request.Description,
userId
userId,
tenantId
);
var result = await mediator.Send(command);
@@ -563,6 +567,50 @@ app.MapRazorComponents<App>()
app.Run();
async Task TriggerBackgroundProcessingForUnindexedBooksAsync(IServiceProvider services)
{
var logger = services.GetRequiredService<ILogger<Program>>();
try
{
var dbContextFactory = services.GetRequiredService<IDbContextFactory<NexusReader.Data.Persistence.AppDbContext>>();
using var dbContext = await dbContextFactory.CreateDbContextAsync();
var unindexedEbooks = await dbContext.Ebooks
.Where(e => !e.IsReadyForReading)
.ToListAsync();
if (unindexedEbooks.Any())
{
logger.LogInformation("[Startup] Found {Count} un-indexed ebooks. Triggering background processing...", unindexedEbooks.Count);
foreach (var ebook in unindexedEbooks)
{
logger.LogInformation("[Startup] Queuing background processing for ebook: '{Title}' ({Id})", ebook.Title, ebook.Id);
_ = Task.Run(async () =>
{
try
{
using var scope = services.CreateScope();
var scopedMediator = scope.ServiceProvider.GetRequiredService<IMediator>();
await scopedMediator.Send(new ProcessEbookCommand(ebook.Id, ebook.UserId, ebook.TenantId));
}
catch (Exception ex)
{
using var scope = services.CreateScope();
var scopedLogger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
scopedLogger.LogError(ex, "Failed to run background processing for ebook {EbookId} on startup", ebook.Id);
}
});
}
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error checking or triggering background processing for unindexed books on startup.");
}
}
public record KnowledgeRequest(string Text, Guid? EbookId = null);
public record GroundednessRequest(string Answer, string Context);
public record SemanticSearchRequest(string QueryText, int Limit = 5);