Update documentation to reflect current architecture

Revised COPILOT_CONTEXT.md to align with the active
EnvelopeGenerator architecture and workflows. Key updates:
- Updated title and purpose for clarity.
- Replaced migration notice with active app structure details.
- Documented hosting model, including `Program.cs` setup.
- Removed outdated deployment architecture section.
- Reorganized route structure for WebAssembly and server pages.
- Expanded authentication model for sender/receiver flows.
- Added details on server-side data loading and caching.
- Updated receiver PDF viewer and signature workflow sections.
- Clarified coordinate system conversions and usage.
- Marked deprecated projects and legacy files as "Do Not Touch."
- Replaced mistakes history with workspace rules.
- Updated last modified date to 2026-06-29.
This commit is contained in:
2026-06-29 10:23:24 +02:00
parent ec0ea72890
commit 96a84ba1a5

View File

@@ -1,564 +1,351 @@
# EnvelopeGenerator — AI Context Reference # EnvelopeGenerator — Current Workspace Context
## Purpose ## Purpose
Digital document signing system with **unified Blazor Auto (Server+WASM hybrid) frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs. Digital document signing system for senders and receivers.
**Primary Libraries:** DevExpress + PDF.js (PSPDFKit removed) - Senders authenticate, view envelope lists, and manage envelope workflows.
- Receivers authenticate per envelope, open PDFs, create signatures, and apply them in the viewer.
**Receiver Architecture:** - The active UI stack is `Blazor Auto` with server-side and WebAssembly render modes.
- Receiver authentication for `EnvelopeReceiverPage.razor` is now validated server-side. - Primary UI/PDF libraries are `DevExpress` and `PDF.js`.
- Receiver page data is loaded directly via MediatR and distributed cache, not through the page's own API calls.
- PDF rendering in `EnvelopeReceiverPage.razor` is PDF.js-based, while `DxPdfViewer` remains the SSR-native viewer target.
--- ---
## Migration Notice ## Active Application Structure
**EnvelopeGenerator.ReceiverUI ? EnvelopeGenerator.WebUI Migration** ### Main Host
**Primary active application:** `EnvelopeGenerator.Server`
The project has been migrated from pure Blazor WebAssembly (`ReceiverUI`) to **Blazor Auto (Server+WASM hybrid)** architecture (`WebUI`) to resolve DevExpress `DxPdfViewer` compatibility issues. `EnvelopeGenerator.Server` is the current runtime host and contains:
- Blazor server host
- WebAssembly host integration
- API controllers
- authentication/authorization setup
- Swagger/Scalar setup
- YARP reverse proxy configuration
- DevExpress server-side services
- SQL Server distributed cache setup
**Reason:** DevExpress `DxPdfViewer` requires backend server-side rendering services that are NOT available in pure WebAssembly projects. ### Client Project
**Client UI project:** `EnvelopeGenerator.Server.Client`
**New Structure:** This project contains:
- **WebUI** (Server project): Hosts server-side components, YARP proxy, DevExpress backend services - WebAssembly-rendered pages
- **WebUI.Client** (WASM project): Client-side components, business logic, services - client-side services
- client models and options
- sender and receiver login flows
**Migration Details:** See `MIGRATION_CONTEXT.md` ### Other Projects
- `EnvelopeGenerator.Application` — MediatR/CQRS handlers and business logic
- `EnvelopeGenerator.Domain` — domain models, constants, shared abstractions
- `EnvelopeGenerator.Infrastructure` — EF Core and infrastructure services
- `EnvelopeGenerator.PdfEditor` — PDF-related backend utilities
- `EnvelopeGenerator.API` — still exists in the solution, but the current merged app host is `EnvelopeGenerator.Server`
### Legacy / Do Not Touch
- `EnvelopeGenerator.Service`
- `EnvelopeGenerator.Form`
- `EnvelopeGenerator.BBTests`
- `EnvelopeGenerator.CommonServices`
--- ---
## Deployment Architecture ## Current Hosting Model
**Two Presentation Projects (Both Required):** `EnvelopeGenerator.Server/Program.cs` currently configures:
- `AddRazorComponents()` with both interactive server and interactive WebAssembly components
- `AddControllers()` and `MapControllers()`
- JWT authentication for sender and receiver flows
- cookie authentication
- authorization policies using `AuthScheme.Sender`, `AuthScheme.Receiver`, `AuthPolicy.Sender`, `AuthPolicy.Receiver`
- `AddReverseProxy()` with `yarp.json`
- Swagger / OpenAPI / Scalar
- distributed SQL Server cache
- DevExpress Blazor and DevExpress PDF Viewer server-side services
- request localization middleware
1. **EnvelopeGenerator.API** (ASP.NET Core Web API) This means the active app is a **merged UI + API host**.
- Runs independently (development & production)
- Backend services for document management, authentication, signature endpoints
- Serves as API endpoint for WebUI
2. **EnvelopeGenerator.WebUI** (Blazor Auto - Server+WASM Hybrid)
- **Server Project (`EnvelopeGenerator.WebUI`):**
- **YARP Reverse Proxy** configured via `yarp.json`
- Proxies `/api/*` requests to `API:8088`
- Hosts server-side components (`@rendermode InteractiveServer`)
- DevExpress server-side services (DxPdfViewer backend)
- **Client Project (`EnvelopeGenerator.WebUI.Client`):**
- Client-side components (`@rendermode InteractiveWebAssembly`)
- Business logic services (AuthService, DocumentService, etc.)
- WASM runtime
**Request Flow:**
```
Client ? WebUI:XXXX (Blazor Auto)
?? Server-side Pages (DxPdfViewer)
?? Client-side Pages (WASM)
?? YARP Proxy: /api/* ? API:8088
```
**Configuration:** `EnvelopeGenerator.WebUI/yarp.json`
--- ---
## WebUI Route Structure ## Reverse Proxy
### Root Route **Config file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
| Route | File | Location | Render Mode |
Current YARP usage is focused on **AuthHub forwarding**, not a general `/api/* -> EnvelopeGenerator.API` proxy.
Configured routes forward:
- `POST /api/auth` -> AuthHub `/api/auth/sign-flow`
- `POST /api/Auth/envelope-receiver/{key}` -> AuthHub `/api/auth/envelope-receiver/{key}?cookie=true`
---
## Active Routes and Files
### WebAssembly Pages (`EnvelopeGenerator.Server.Client`)
| Route | File | Render Mode | Purpose |
|---|---|---|---| |---|---|---|---|
| `/` | `Index.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` | | `/` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/IndexPage.razor` | WebAssembly | Landing page |
| `/sender/login` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginSenderPage.razor` | WebAssembly | Sender login |
| `/sender` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor` | WebAssembly (`prerender: false`) | Sender dashboard |
| `/envelope/login/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/LoginReceiverPage.razor` | WebAssembly | Receiver login |
### Sender Routes ### Server Pages (`EnvelopeGenerator.Server`)
| Route | File | Location | Render Mode | | Route | File | Render Mode | Purpose |
|---|---|---|---| |---|---|---|---|
| `/sender/login` | `LoginSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` | | `/envelope/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` | InteractiveServer | Main receiver PDF viewer and signing page |
| `/sender` | `EnvelopeSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` | | `/envelope/DxPdfViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | InteractiveServer | DevExpress PDF Viewer test page |
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | InteractiveServer | DevExpress report-based PDF rendering |
### Receiver Routes (PDF Viewers) | `/envelope/Embed` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_embed.razor` | InteractiveServer | Embedded browser PDF view test page |
| Route | File | Location | Render Mode |
|---|---|---|---|
| `/envelope/login/{EnvelopeKey}` | `LoginReceiverPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
| `/envelope/{EnvelopeKey}` | `EnvelopeReceiverPage.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/DxPdfViewer` | `EnvelopeReceiverPage_DxPdfViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeReceiverPage_DxReportViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/Embed` | `EnvelopeReceiverPage_embed.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
**Multi-Envelope Support:** Receivers can login to multiple envelopes simultaneously (per-envelope cookie authentication).
**Render Mode Strategy:**
- **Client-side Pages (WASM):** Login, Sender dashboard, Index (no DevExpress backend required)
- **Server-side Pages (Server):** PDF viewers (DevExpress DxPdfViewer requires backend)
--- ---
## Architecture Evolution ## Current API Location
### Old Architecture (Deprecated v1) The active application exposes controllers from:
- **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit) `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers`
- **Receiver UI:** Separate project
- **Backend:** `EnvelopeGenerator.API`
### Intermediate Architecture (Deprecated v2) Current controller set includes:
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM) - `AnnotationController`
- **Backend:** `EnvelopeGenerator.API` - `AuthController`
- **Issue:** DevExpress `DxPdfViewer` displayed blank screen (no backend services in WASM) - `CacheController`
- `ConfigController`
- `DocumentController`
- `EmailTemplateController`
- `EnvelopeController`
- `EnvelopeReceiverController`
- `EnvelopeTypeController`
- `HistoryController`
- `LocalizationController`
- `ReadOnlyController`
- `ReceiverController`
- `SignatureController`
- `TfaRegistrationController`
### Current Architecture (Active) Do not assume API behavior lives only in `EnvelopeGenerator.API`; the active merged host contains controller endpoints directly.
- **Frontend:** `EnvelopeGenerator.WebUI` (Blazor Auto - Server+WASM Hybrid)
- **WebUI** (Server): Server-side components, YARP proxy, DevExpress backend
- **WebUI.Client** (WASM): Client-side components, services, business logic
- **Backend:** `EnvelopeGenerator.API`
- **Libraries:** DevExpress + PDF.js
- **PSPDFKit:** **REMOVED**
--- ---
## Solution Structure ## Authentication Model
| Project | Target | Purpose | ### Sender
|---|---|---| Client login page uses `EnvelopeGenerator.Server.Client/Services/AuthService.cs`.
| `EnvelopeGenerator.API` | net8.0 | ASP.NET Core Web API. Backend for **both Senders & Receivers**. Auth, PDF serving, signature endpoints. |
| `EnvelopeGenerator.WebUI` | net8.0 | **Blazor Auto Server Project**. YARP proxy, server-side components, DevExpress backend services. | Key sender endpoints:
| `EnvelopeGenerator.WebUI.Client` | net8.0 WASM | **Blazor Auto Client Project**. Client-side components, services, business logic. | - `POST /api/auth?cookie=true` login
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **DEPRECATED.** Pure Blazor WASM (migrated to WebUI). | - `GET /api/auth/check` — current sender access check
| `EnvelopeGenerator.Web` | net7/8/9 | **DEPRECATED.** Legacy Razor Pages (Sender UI). No longer used. | - `POST /api/auth/logout` — logout
| `EnvelopeGenerator.Application` | multi | MediatR CQRS handlers. Business logic. |
| `EnvelopeGenerator.Domain` | multi | Domain models, constants, interfaces. | ### Receiver
| `EnvelopeGenerator.Infrastructure` | multi | EF Core repos, DB context. | Receiver authentication is **per envelope**.
| `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in WebUI). |
| `EnvelopeGenerator.DependencyInjection` | multi | DI registration helpers. | Key receiver endpoints used by client services:
| **VB.NET projects** (Service/Form/BBTests) | net462 | **Legacy. Do NOT touch.** | - `POST /api/Auth/envelope-receiver/{envelopeKey}` — submit access code
- `GET /api/auth/check/envelope/{envelopeKey}` — check access
- `POST /api/auth/logout/envelope/{envelopeKey}` — logout receiver for one envelope
Receiver cookie resolution in server auth uses an envelope-specific cookie name derived from:
- `AuthTokenSignFLOWReceiver.{envelopeKey}` pattern
### Receiver Server-Side Authorization
`EnvelopeReceiverPage.razor` does **not** rely on its own API access-check call for page authorization.
It uses:
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
Behavior:
- tries the current `HttpContext.User`
- if needed, reads the per-envelope receiver cookie directly
- validates the JWT with the receiver auth scheme
- verifies the token subject matches the route envelope key
--- ---
## Localization & Culture Management ## Receiver Page Data Loading
**Current Architecture:** Blazor WebAssembly (client-side culture management) Main server-side page data service:
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
### Implementation Details This service loads directly via MediatR and distributed cache:
- document bytes
- receiver envelope data
- signature placeholders
- cached signature data
**Culture Storage:** For signature placeholders, the service:
- Culture preference stored in browser's `localStorage` (key: `AppCulture`) - reads document receiver elements
- Managed by `CultureService.cs` (ReceiverUI/Services) - filters them for the authenticated receiver
- Supported cultures: `de-DE`, `en-US`, `fr-FR` - converts coordinates to `UnitOfLength.Point` before UI use
**Culture Initialization:**
- **Location:** `Program.cs` (lines 53-57)
- Sets `CultureInfo.DefaultThreadCurrentCulture/UICulture` **before** app runs
- **WASM-Safe:** Each user has isolated browser instance
**Language Selector:**
- **Component:** `LanguageSelector.razor` (ReceiverUI/Shared)
- Displays flag icon + language name
- Changes culture via `CultureService.SetCultureAsync()`
- Navigates with `forceLoad: false` (smooth transition, no page reload)
### ⚠️ MIGRATION WARNING: Blazor Server/Auto
**Current approach is WASM-specific and will break in Server/Auto render modes!**
**Why it breaks:**
- `Program.cs:53-57` sets **global** `DefaultThreadCurrentCulture`
- In Server/Auto, one app instance serves **all users**
- User A selects German → User B sees German too (shared state)
- Thread-safety issues and culture conflicts
**Migration Checklist (when moving to Server/Auto):**
1. **Remove global culture initialization** from `Program.cs` (lines 53-57)
- See detailed warning comment in the code
2. **Add RequestLocalizationMiddleware** (Server-side approach):
```csharp
app.UseRequestLocalization(options => {
options.SupportedCultures = new[] { "de-DE", "en-US", "fr-FR" };
options.SupportedUICultures = options.SupportedCultures;
options.RequestCultureProviders.Insert(0, new CookieRequestCultureProvider());
});
```
3. **OR** Use **per-circuit culture** (Blazor Server approach):
- Store culture in circuit-scoped service
- Use `CascadingParameter` to distribute to components
- See: https://learn.microsoft.com/aspnet/core/blazor/globalization-localization
4. **Update `LanguageSelector.razor`:**
- Remove manual `CultureInfo.DefaultThreadCurrentCulture` assignment
- Use middleware/circuit culture provider instead
5. **Update `CultureService.cs`:**
- Integrate with Server-side culture provider
- May need to store in cookies instead of localStorage
**References:**
- Microsoft Docs: [Blazor Globalization/Localization](https://learn.microsoft.com/aspnet/core/blazor/globalization-localization)
- Current implementation: `Program.cs`, `CultureService.cs`, `LanguageSelector.razor`
--- ---
## Key Files & Routes ## Receiver PDF Viewer
### Client-Side Pages (WebUI.Client) **Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
| File | Route | Purpose |
|---|---|---|
| `WebUI.Client/Pages/Index.razor` | `/` | Application entry point (landing page). |
| `WebUI.Client/Pages/EnvelopeSenderPage.razor` | `/sender` | Sender dashboard (envelope list). |
| `WebUI.Client/Pages/LoginSenderPage.razor` | `/sender/login` | Sender username/password auth. |
| `WebUI.Client/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` | Receiver access code auth. |
### Server-Side Pages (WebUI) Current receiver viewer characteristics:
| File | Route | Purpose | - route: `/envelope/{EnvelopeKey}`
|---|---|---| - render mode: `InteractiveServer`
| `WebUI/Components/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` | Receiver PDF viewer & signing page. Uses Interactive Server, server-side auth/data loading, and currently renders with PDF.js overlay logic. | - PDF rendering: `PDF.js`
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | `/envelope/DxPdfViewer` | DevExpress PDF Viewer (test page). | - toolbar: page navigation, zoom, thumbnail toggle, signature navigation, signature reset
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | `/envelope/{key}/DxReportViewer` | DevExpress Report Viewer. | - signature popup: `DxPopup`
| `WebUI/Components/Pages/EnvelopeReceiverPage_embed.razor` | `/envelope/Embed` | Embedded PDF viewer (iframe). | - thumbnail sidebar: resizable and stored in `localStorage`
### Services & Assets ### JS Assets
| File | Purpose | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
|---|---| - `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`
| `WebUI.Client/Services/AuthService.cs` | Receiver + Sender authentication. |
| `WebUI.Client/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). | ### CSS
| `WebUI.Client/Services/DocumentService.cs` | PDF document retrieval. | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css`
| `WebUI/Services/EnvelopeReceiverAuthorizationService.cs` | Server-side receiver authorization for `EnvelopeReceiverPage.razor` using per-envelope cookie/JWT validation. |
| `WebUI/Services/EnvelopeReceiverPageDataService.cs` | Server-side document/signature/receiver data loading via MediatR and distributed cache. | ### PDF.js CDN
| `WebUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). | - `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
| `WebUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). | - `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css`
| `WebUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
| `API/Controllers/CacheController.cs` | Signature cache endpoints. |
--- ---
## Coordinate System — CRITICAL ## Signature Workflow
**Database Format:** INCHES (GdPicture14 native) Receiver signatures are handled as a **viewer overlay workflow**.
**Origin:** Top-left corner
**Axes:** X right, Y down
### Conversion Formulas ### Current behavior
1. Server-side authorization validates receiver access.
2. The page loads document bytes, receiver data, signature placeholders, and cached signature state.
3. If no cached signature exists, the signature popup opens automatically.
4. Receiver creates signature using one of three tabs:
- draw
- text
- image
5. Required metadata:
- full name
- place
6. Optional metadata:
- position
7. Clicking a signature placeholder applies the signature as a client-side overlay in the PDF viewer.
| From INCHES to | Formula | Example | ### Important note
|---|---|---| Although `itext` is referenced by the server project, the current receiver page signing flow is **not PDF stamping-based**. The active receiver UI uses client-side overlay behavior in the viewer.
| **DevExpress DX** | `x_DX = x_inches * 100` | 1.5" ? 150 DX |
| **PDF Points** | `x_pt = x_inches * 72` | 1.5" ? 108 pt |
| **PDF.js Pixels** | Normalize ? scale | `(x_inches / pageWidth) * canvasWidth * scale` |
**A4 Dimensions:** ### Signature DTO
- Width: 8.27" = 595pt = 827 DX `EnvelopeGenerator.Server.Client/Models/SignatureCaptureDto.cs`
- Height: 11.69" = 842pt = 1169 DX
### Unit Systems
| System | Unit | Origin | Y-Axis |
|---|---|---|---|
| **Database (GdPicture14)** | Inches | Top-left | Down |
| PDF.js | Pixels | Top-left | Down |
| iText7 PDF | Points (1/72") | **Bottom-left** | **Up** (flip required) |
| ~~PSPDFKit~~ | ~~Points~~ | ~~Top-left~~ | **REMOVED** |
---
## EnvelopeReceiver — PDF.js Viewer & Signing
**Route:** `/envelope/{EnvelopeKey}`
**Tech:** PDF.js 3.11.174 + Blazor Server (`@rendermode InteractiveServer`) + server-side auth/data loading + configurable quality
**File:** `WebUI/Components/Pages/EnvelopeReceiverPage.razor`
### Current Server-Side Loading Model
- Authorization is performed inside the server project via `EnvelopeReceiverAuthorizationService`.
- The page no longer relies on `GET /api/auth/check/envelope/{EnvelopeKey}` for its own access check.
- Document bytes, receiver data, and signature placeholders are loaded directly through MediatR using `EnvelopeReceiverPageDataService`.
- Cached signatures are loaded from distributed cache directly in the server project.
### Key Features
1. HiDPI/Retina support (4x quality)
2. Configurable quality (`appsettings.json`)
3. Unlimited zoom (50%-300%)
4. Ctrl+Wheel global zoom
5. Resizable thumbnail sidebar (150-400px, localStorage)
6. Responsive (desktop/mobile)
### Configuration
**File:** `WebUI/wwwroot/appsettings.json`
```json
{
"PdfViewer": {
"ThumbnailBaseScale": 0.75,
"ThumbnailEnableHiDPI": true,
"MainCanvasEnableHiDPI": true,
"ZoomStepPercentage": 5
}
}
```
### JavaScript API
**File:** `WebUI/wwwroot/js/pdf-viewer.js`
```javascript
window.pdfViewer = {
initialize(canvasId, pdfDataUrl, dotNetRef),
renderPage(num),
renderSignatureButtons(signatures, pageNum, dotNetRef),
applySignature(signatureId, dataUrl, fullName, position, place),
zoomIn(), zoomOut(), dispose()
}
```
---
## Signature Workflow — EnvelopeReceiver
**IMPORTANT:** iText7 NOT used (GPL license issue). Client-side overlay system only.
### Workflow Steps
1. **Page Load:**
- Validate receiver access server-side using the per-envelope auth cookie
- Load document, receiver, and signature data directly through MediatR
- Check distributed cache for cached signature
- If cached ? skip popup, load signature
- If not ? show automatic popup (mandatory)
2. **Signature Popup (DxPopup):**
- **Cannot close** (no X, no ESC, no outside-click)
- **3 Tabs:** Draw (canvas) / Text (font select) / Image (upload)
- **Required:** Full name, Place
- **Optional:** Position
- **Save ?** Store in `_capturedSignature`, cache via API
3. **Signature Buttons:**
- Render purple "Unterschreiben" buttons at signature field positions
- Coordinates: INCHES ? POINTS ? Pixels (scaled)
- File: `pdf-viewer.js` ? `renderSignatureButtons()`
4. **Apply Signature (Click "Unterschreiben"):**
- JS: Remove button, create HTML overlay
- Format: Image + separator + text (Name, Position, Place, Date)
- **NOT stamped on PDF bytes** (visual overlay only)
5. **Re-rendering:**
- Zoom/Page change ? recalculate button positions
- Session state: `_capturedSignature` (lost on refresh)
### Authentication Notes
- Receiver cookies are stored per envelope: `AuthTokenSignFLOWReceiver.{envelopeKey}`.
- `EnvelopeReceiverPage.razor` uses server-side receiver authorization logic instead of calling its own auth check API endpoint.
- The server-side auth flow must remain compatible with `AuthScheme.Receiver` and `AuthPolicy.Receiver`.
### Data Model
**File:** `WebUI.Client/Models/SignatureCaptureDto.cs`
```csharp ```csharp
public sealed record SignatureCaptureDto { public sealed record SignatureCaptureDto {
public required string DataUrl { get; init; } // base64 PNG public required string DataUrl { get; init; }
public required string FullName { get; init; } public required string FullName { get; init; }
public string Position { get; init; } = ""; // Optional public string Position { get; init; } = "";
public required string Place { get; init; } public required string Place { get; init; }
} }
``` ```
--- ---
## Signature Caching ## Signature Cache
**Purpose:** Persist signature across page refreshes (distributed cache: Redis/SQL) ### Active cache model
The current receiver page cache flow is handled directly in the server project through:
- `EnvelopeReceiverPageDataService`
- `IDistributedCache`
- SQL Server distributed cache configuration from `Program.cs`
### API Endpoints ### Cache key format
**Controller:** `API/Controllers/CacheController.cs` Current server-side key prefix:
- `envelope-generator.receiver-ui.signature:{receiverSignature}`
- `POST /api/Cache/SignatureCapture/{envelopeKey}` — Save This is different from an envelope-key-only cache convention.
- `GET /api/Cache/SignatureCapture/{envelopeKey}` — Load
- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` — Delete
**Cache Key Format:** ### Config
``` `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Options/CacheOptions.cs`
signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey} - section name: `Cache`
``` - option: `SignatureCacheExpiration`
**Configuration:** `appsettings.json` ### Related controller
```json A cache API controller also exists in:
{ - `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/CacheController.cs`
"Cache": {
"SignatureCacheExpiration": null // or "02:00:00" for 2h
}
}
```
### Service
**File:** `WebUI.Client/Services/SignatureCacheService.cs`
```csharp
public class SignatureCacheService {
Task SaveSignatureAsync(string envelopeKey, SignatureCaptureDto signature);
Task<SignatureCaptureDto?> GetSignatureAsync(string envelopeKey);
Task DeleteSignatureAsync(string envelopeKey);
}
```
**Error Handling:** Fire-and-forget saves, graceful degradation on load failure.
--- ---
## Sender Login ## Sender Dashboard
**Route:** `/sender/login` **Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor`
**File:** `WebUI.Client/Pages/LoginSenderPage.razor`
**Tech:** Bootstrap 5 + DevExpress Blazing Berry theme
### AuthService Extension Current behavior:
**File:** `WebUI.Client/Services/AuthService.cs` - checks sender access through `AuthService.CheckSenderAccessAsync()`
- redirects to `/sender/login` when unauthorized
- loads envelope list through client `EnvelopeService`
- separates envelopes into active/completed tabs
- uses `DevExpress DxGrid`
```csharp The sender page is active, but create/edit/delete actions are still marked with TODO behavior in the UI page.
public enum SenderLoginResult { Success, InvalidCredentials, Error }
public async Task<SenderLoginResult> LoginSenderAsync(string username, string password) {
var response = await http.PostAsJsonAsync(
$"{_api.BaseUrl}/api/auth?cookie=true",
new { username, password });
return response.StatusCode switch {
HttpStatusCode.OK => SenderLoginResult.Success,
HttpStatusCode.Unauthorized => SenderLoginResult.InvalidCredentials,
_ => SenderLoginResult.Error
};
}
```
### API Integration
**Endpoint:** `POST /api/auth?cookie=true`
**Request:**
```json
{ "username": "TekH", "password": "***" }
```
**Response:**
- `200 OK` ? Cookie set, redirect to `/sender`
- `401 Unauthorized` ? Show error: "Ungültige Anmeldedaten"
- Other ? Show error: "Serverfehler"
**Cookie:** HTTP-only, Secure (HTTPS), SameSite=Strict
### UI Flow
1. User enters username + password
2. Click "Anmelden" or press Enter
3. Call `AuthService.LoginSenderAsync()`
4. Success ? `Navigation.NavigateTo("/sender", forceLoad: true)`
5. Error ? Display alert
--- ---
## Receiver Login ## Localization
**Route:** `/envelope/login/{EnvelopeKey}` Current server host localization setup in `Program.cs`:
**File:** `WebUI.Client/Pages/LoginReceiverPage.razor` - supported cultures: `de-DE`, `en-US`
- request localization middleware is enabled
- `QueryStringRequestCultureProvider` is added
- cookie-based localization services are registered via `AddCookieBasedLocalizer()`
**Multi-Envelope Support:** Cookies are stored per-envelope (e.g., `AuthTokenSignFLOWReceiver.{envelopeKey}`), allowing simultaneous authentication for multiple envelopes in the same browser session. Do not assume the old ReceiverUI-only `localStorage` culture approach is the current source of truth for the active host.
### AuthService Method
```csharp
public enum EnvelopeLoginResult { Success, InvalidCode, NotFound, Error }
public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, string accessCode) {
var form = new MultipartFormDataContent();
form.Add(new StringContent(accessCode), "AccessCode");
var response = await http.PostAsync(
$"{_api.BaseUrl}/api/Auth/envelope-receiver/{Uri.EscapeDataString(key)}", form);
return response.StatusCode switch {
HttpStatusCode.OK => EnvelopeLoginResult.Success,
HttpStatusCode.Unauthorized => EnvelopeLoginResult.InvalidCode,
HttpStatusCode.NotFound => EnvelopeLoginResult.NotFound,
_ => EnvelopeLoginResult.Error
};
}
```
**Success:** Redirect to `/envelope/{key}`
--- ---
## NuGet Packages (WebUI.Client) ## Coordinate System
| Package | Version | Purpose | ### Source data
|---|---|---| Database signature coordinates are still based on:
| `DevExpress.Blazor.*` | 25.2.3 | UI components (grids, popups, etc.) | - **unit:** inches
| `SkiaSharp.*` | 3.119.1 | WASM rendering | - **origin:** top-left
| ~~`itext`~~ | ~~8.0.5~~ | **NOT USED** (GPL license) | - **axes:** X right, Y down
**External CDN:** ### Relevant conversions
- PDF.js 3.11.174: `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js` - inches -> PDF points: `x_pt = x_inches * 72`
- inches -> DevExpress DX units: `x_dx = x_inches * 100`
### Current receiver page behavior
The server page data service converts signature placeholders to **points** before sending them into the viewer workflow.
### Unit systems to keep in mind
| System | Unit | Origin | Y-axis |
|---|---|---|---|
| Database | Inches | Top-left | Down |
| PDF.js display | Pixels | Top-left | Down |
| PDF points | Points | Depends on PDF model | Depends on consumer |
| DevExpress DX | 1/100 inch style coordinates | Top-left-oriented usage in this app | Down-oriented usage |
--- ---
## Mistakes History — Do NOT Repeat ## Key Services and Files
| Mistake | Why Wrong | ### Client services
|---|---| - `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs`
| Using iText7 in EnvelopeReceiver | GPL license issue. Use overlay system instead. | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs`
| Using PSPDFKit | Removed from architecture. Use PDF.js + DevExpress. | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs`
| Hardcoded quality values in PDF.js | Use `appsettings.json` for configurability. | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs`
| Complex toolbar layouts | User wants simplicity. Keep horizontal layout. |
| Over-designed UI (gradients/badges) | User prefers simple text labels. | ### Server services
| Ignoring "revert" instructions | Revert HTML structure, not just CSS. | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs`
| `BottomMarginBand` for signatures | Repeats on every page. Use DetailBand. | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs`
| `imageY = (page-1) * 1169 + ann.Y` | Inflates DetailBand. Calculate per-page. | - `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
### Server config and host files
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json`
--- ---
## Development Notes ## Working Rules for This Workspace
### Deprecated Projects - Treat `EnvelopeGenerator.Server` as the active main application host.
**DO NOT USE:** - Treat `EnvelopeGenerator.Server.Client` as the active client UI project.
- `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM) — Migrated to WebUI (DevExpress compatibility issue) - Prefer current `Server` / `Server.Client` paths over old `WebUI` / `ReceiverUI` references.
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified WebUI - Do not use `EnvelopeGenerator.Web` or `EnvelopeGenerator.ReceiverUI` as the primary implementation target unless explicitly asked.
- PSPDFKit — Removed, use PDF.js + DevExpress instead - Do not modify the legacy VB.NET projects unless explicitly requested.
- For receiver PDF/signature work, prefer the current `PDF.js`-based flow in `EnvelopeReceiverPage.razor`.
### Legacy Projects (VB.NET) - For DevExpress PDF viewer issues, remember server-side services are registered in `EnvelopeGenerator.Server`.
**DO NOT TOUCH:** `EnvelopeGenerator.Service`, `EnvelopeGenerator.Form`, `EnvelopeGenerator.BBTests`
### Signature Coordinate Evidence
**File:** `EnvelopeGenerator.Form/frmFieldEditor.vb` (VB.NET)
```vb
Private Const SIGNATURE_WIDTH As Single = 1.77 ' inches
Private Const SIGNATURE_HEIGHT As Single = 1.96 ' inches
Sub LoadAnnotation(pElement As Signature, ...)
oAnnotation.Left = CSng(pElement.X) ' Direct INCHES assignment
oAnnotation.Top = CSng(pElement.Y)
End Sub
```
Proves database uses INCHES natively.
--- ---
## Quick Reference **Last Updated:** 2026-06-29
### When working with coordinates:
1. **Database ? UI:** INCHES × 72 = PDF Points
2. **UI ? Display:** Points × scale = Pixels
3. **iText7 stamping:** Flip Y-axis (top-down ? bottom-up)
### When adding features:
1. Check `Mistakes History` first
2. Prefer simplicity over complexity
3. Use `appsettings.json` for configuration
4. Keep consistent with existing design (Bootstrap 5 + Blazing Berry)
5. **Unified frontend:** WebUI serves both Senders and Receivers
6. **Render mode:** Client-side (WASM) for login/dashboard, Server-side for PDF viewers
### When debugging:
1. **Coordinates:** Always check unit system (inches/points/pixels)
2. **Authentication:** Check cookie name/domain/SameSite
3. **Cache:** Check Redis/SQL connection + key format
4. **Frontend confusion:** Only use WebUI (ReceiverUI/Web are deprecated)
5. **Blank DxPdfViewer:** Ensure page has `@rendermode InteractiveServer`
---
**Last Updated:** 2025-01-27 (ReceiverUI ? WebUI migration complete)