Files
Nexus.Reader/src/NexusReader.Maui/MauiProgram.cs
T
Antigravity bf31effd36 fix: preserve and render EPUB images via dynamic server endpoint (#65)
Fixes #64

### Summary of Changes
1. **Extended `IEpubReader` & `EpubReaderService`**: Added `GetEpubResourceAsync` to handle binary data extraction of static assets (like images) from the EPUB archive.
2. **Added Client-Side HTTP Call**: Extended `WasmEpubService` to retrieve static resources from the server using the API client.
3. **Preserved and Sanitized Images**: Updated `ExtractParagraphs` and `SanitizeParagraph` to treat `<img>` tags as first-class citizens, preserving their `src` attributes and excluding them from sanitization stripping.
4. **Dynamic URL Rewriting**: Introduced a relative-to-absolute path resolution algorithm (`ResolveRelativePath`) and rewrote image `src` attributes to use the dynamic endpoint `/api/epub/{ebookId}/resource?path=...`.
5. **Registered API Resource Serving Endpoint**: Added the `/api/epub/{ebookId:guid}/resource` minimal API endpoint in `Program.cs` that maps requests directly to `GetEpubResourceAsync` and returns files with the correct MIME type.
6. **Added Unit Tests**: Created `EpubReaderServiceTests.cs` to verify all image extraction, path resolution, and sanitization/rewriting rules. All tests pass successfully.

---------

Co-authored-by: Marek Jasiński <jasins.marek@gmail.com>
Reviewed-on: #65
Co-authored-by: Antigravity <antigravity@google.com>
Co-committed-by: Antigravity <antigravity@google.com>
2026-06-01 16:04:56 +00:00

97 lines
3.5 KiB
C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Infrastructure.Mobile.Services;
using NexusReader.UI.Shared.Services;
using NexusReader.Application;
using MediatR;
using NexusReader.Maui.Infrastructure.Logging;
using NexusReader.Maui.Infrastructure.Identity;
namespace NexusReader.Maui;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
try
{
var builder = MauiApp.CreateBuilder();
// Load embedded appsettings.json configuration
var assembly = typeof(App).Assembly;
using (var stream = assembly.GetManifestResourceStream("NexusReader.Maui.appsettings.json"))
{
if (stream != null)
{
((IConfigurationBuilder)builder.Configuration).AddJsonStream(stream);
}
}
builder
.UseMauiApp<App>()
.RegisterLogging();
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
#endif
// Interception bridge for JS/Blazor WebView logs
builder.Services.AddSingleton<BlazorLoggingBridge>();
// Minimal Infrastructure
builder.Services.AddSingleton<IPlatformService, MauiPlatformService>();
builder.Services.AddSingleton<INativeStorageService, MauiStorageService>();
// Minimal Identity (Safe Mode)
builder.Services.AddScoped<NexusAuthenticationStateProvider>();
builder.Services.AddScoped<Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider>(sp =>
sp.GetRequiredService<NexusAuthenticationStateProvider>());
builder.Services.AddAuthorizationCore();
// Basic Network with Secure Token Handler
builder.Services.AddTransient<MobileAuthenticationHeaderHandler>();
builder.Services.AddHttpClient("NexusAPI", client =>
{
var apiBaseUrl = builder.Configuration["ApiSettings:BaseUrl"] ?? "http://localhost:5000";
client.BaseAddress = new Uri(apiBaseUrl);
}).AddHttpMessageHandler<MobileAuthenticationHeaderHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("NexusAPI"));
// UI State
// Feature settings (avoiding direct raw IConfiguration injection in client pages)
var featureSettings = builder.Configuration.GetSection("Features").Get<FeatureSettings>() ?? new FeatureSettings();
builder.Services.AddSingleton(featureSettings);
builder.Services.AddScoped<IThemeService, ThemeService>();
builder.Services.AddScoped<IFocusModeService, FocusModeService>();
builder.Services.AddScoped<IQuizStateService, QuizStateService>();
builder.Services.AddScoped<IReaderNavigationService, ReaderNavigationService>();
builder.Services.AddScoped<IKnowledgeGraphService, KnowledgeGraphService>();
builder.Services.AddScoped<IReaderInteractionService, ReaderInteractionService>();
builder.Services.AddScoped<IReaderStateService, ReaderStateService>();
builder.Services.AddScoped<KnowledgeCoordinator>();
builder.Services.AddScoped<ISyncService, SyncService>();
builder.Services.AddScoped<IIdentityService, IdentityService>();
builder.Services.AddApplication();
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(
NexusReader.Application.DependencyInjection.Assembly
));
return builder.Build();
}
catch (Exception ex)
{
// This might help the debugger catch the exception more reliably
System.Diagnostics.Debug.WriteLine($"MAUI Startup Error: {ex}");
throw;
}
}
}