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>
This commit was merged in pull request #65.
This commit is contained in:
2026-06-01 16:04:56 +00:00
committed by Marek Jaisński
parent 21c9a66cce
commit bf31effd36
12 changed files with 554 additions and 11 deletions
@@ -7,7 +7,7 @@
@inject IIdentityService IdentityService
@inject NavigationManager NavigationManager
@inject IJSRuntime JS
@inject IConfiguration Configuration
@inject FeatureSettings FeatureSettings
<div class="login-page-container">
<div class="mesh-bg"></div>
@@ -84,7 +84,7 @@
protected override void OnInitialized()
{
var allowRegistration = Configuration.GetValue<bool?>("Features:AllowRegistration") ?? true;
var allowRegistration = FeatureSettings.AllowRegistration;
if (!allowRegistration)
{
NavigationManager.NavigateTo("/account/login?error=RegistrationDisabled", replace: true);