Files
EnvelopeGenerator/COPILOT_CONTEXT.md

478 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EnvelopeGenerator — AI Context Reference
## 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.
**Primary Libraries:** DevExpress + PDF.js (PSPDFKit removed)
---
## Migration Notice
**EnvelopeGenerator.ReceiverUI ? EnvelopeGenerator.WebUI Migration**
The project has been migrated from pure Blazor WebAssembly (`ReceiverUI`) to **Blazor Auto (Server+WASM hybrid)** architecture (`WebUI`) to resolve DevExpress `DxPdfViewer` compatibility issues.
**Reason:** DevExpress `DxPdfViewer` requires backend server-side rendering services that are NOT available in pure WebAssembly projects.
**New Structure:**
- **WebUI** (Server project): Hosts server-side components, YARP proxy, DevExpress backend services
- **WebUI.Client** (WASM project): Client-side components, business logic, services
**Migration Details:** See `MIGRATION_CONTEXT.md`
---
## Deployment Architecture
**Two Presentation Projects (Both Required):**
1. **EnvelopeGenerator.API** (ASP.NET Core Web API)
- 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
### Root Route
| Route | File | Location | Render Mode |
|---|---|---|---|
| `/` | `Index.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
### Sender Routes
| Route | File | Location | Render Mode |
|---|---|---|---|
| `/sender/login` | `LoginSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
| `/sender` | `EnvelopeSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
### Receiver Routes (PDF Viewers)
| 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
### Old Architecture (Deprecated v1)
- **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit)
- **Receiver UI:** Separate project
- **Backend:** `EnvelopeGenerator.API`
### Intermediate Architecture (Deprecated v2)
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM)
- **Backend:** `EnvelopeGenerator.API`
- **Issue:** DevExpress `DxPdfViewer` displayed blank screen (no backend services in WASM)
### Current Architecture (Active)
- **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
| Project | Target | Purpose |
|---|---|---|
| `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. |
| `EnvelopeGenerator.WebUI.Client` | net8.0 WASM | **Blazor Auto Client Project**. Client-side components, services, business logic. |
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **DEPRECATED.** Pure Blazor WASM (migrated to WebUI). |
| `EnvelopeGenerator.Web` | net7/8/9 | **DEPRECATED.** Legacy Razor Pages (Sender UI). No longer used. |
| `EnvelopeGenerator.Application` | multi | MediatR CQRS handlers. Business logic. |
| `EnvelopeGenerator.Domain` | multi | Domain models, constants, interfaces. |
| `EnvelopeGenerator.Infrastructure` | multi | EF Core repos, DB context. |
| `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in WebUI). |
| `EnvelopeGenerator.DependencyInjection` | multi | DI registration helpers. |
| **VB.NET projects** (Service/Form/BBTests) | net462 | **Legacy. Do NOT touch.** |
---
## Key Files & Routes
### Client-Side Pages (WebUI.Client)
| 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)
| File | Route | Purpose |
|---|---|---|
| `WebUI/Components/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` | Receiver PDF viewer & signing (PDF.js). |
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | `/envelope/DxPdfViewer` | DevExpress PDF Viewer (test page). |
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | `/envelope/{key}/DxReportViewer` | DevExpress Report Viewer. |
| `WebUI/Components/Pages/EnvelopeReceiverPage_embed.razor` | `/envelope/Embed` | Embedded PDF viewer (iframe). |
### Services & Assets
| File | Purpose |
|---|---|
| `WebUI.Client/Services/AuthService.cs` | Receiver + Sender authentication. |
| `WebUI.Client/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). |
| `WebUI.Client/Services/DocumentService.cs` | PDF document retrieval. |
| `WebUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). |
| `WebUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
| `WebUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
| `API/Controllers/CacheController.cs` | Signature cache endpoints. |
---
## Coordinate System — CRITICAL
**Database Format:** INCHES (GdPicture14 native)
**Origin:** Top-left corner
**Axes:** X right, Y down
### Conversion Formulas
| From INCHES to | Formula | Example |
|---|---|---|
| **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:**
- Width: 8.27" = 595pt = 827 DX
- 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`) + configurable quality
**File:** `WebUI/Components/Pages/EnvelopeReceiverPage.razor`
### 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:**
- Check `SignatureCacheService` 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)
### Data Model
**File:** `WebUI.Client/Models/SignatureCaptureDto.cs`
```csharp
public sealed record SignatureCaptureDto {
public required string DataUrl { get; init; } // base64 PNG
public required string FullName { get; init; }
public string Position { get; init; } = ""; // Optional
public required string Place { get; init; }
}
```
---
## Signature Caching
**Purpose:** Persist signature across page refreshes (distributed cache: Redis/SQL)
### API Endpoints
**Controller:** `API/Controllers/CacheController.cs`
- `POST /api/Cache/SignatureCapture/{envelopeKey}` — Save
- `GET /api/Cache/SignatureCapture/{envelopeKey}` — Load
- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` — Delete
**Cache Key Format:**
```
signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey}
```
**Configuration:** `appsettings.json`
```json
{
"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
**Route:** `/sender/login`
**File:** `WebUI.Client/Pages/LoginSenderPage.razor`
**Tech:** Bootstrap 5 + DevExpress Blazing Berry theme
### AuthService Extension
**File:** `WebUI.Client/Services/AuthService.cs`
```csharp
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
**Route:** `/envelope/login/{EnvelopeKey}`
**File:** `WebUI.Client/Pages/LoginReceiverPage.razor`
**Multi-Envelope Support:** Cookies are stored per-envelope (e.g., `AuthTokenSignFLOWReceiver.{envelopeKey}`), allowing simultaneous authentication for multiple envelopes in the same browser session.
### 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)
| Package | Version | Purpose |
|---|---|---|
| `DevExpress.Blazor.*` | 25.2.3 | UI components (grids, popups, etc.) |
| `SkiaSharp.*` | 3.119.1 | WASM rendering |
| ~~`itext`~~ | ~~8.0.5~~ | **NOT USED** (GPL license) |
**External CDN:**
- PDF.js 3.11.174: `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
---
## Mistakes History — Do NOT Repeat
| Mistake | Why Wrong |
|---|---|
| Using iText7 in EnvelopeReceiver | GPL license issue. Use overlay system instead. |
| Using PSPDFKit | Removed from architecture. Use PDF.js + DevExpress. |
| Hardcoded quality values in PDF.js | Use `appsettings.json` for configurability. |
| Complex toolbar layouts | User wants simplicity. Keep horizontal layout. |
| Over-designed UI (gradients/badges) | User prefers simple text labels. |
| Ignoring "revert" instructions | Revert HTML structure, not just CSS. |
| `BottomMarginBand` for signatures | Repeats on every page. Use DetailBand. |
| `imageY = (page-1) * 1169 + ann.Y` | Inflates DetailBand. Calculate per-page. |
---
## Development Notes
### Deprecated Projects
**DO NOT USE:**
- `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM) — Migrated to WebUI (DevExpress compatibility issue)
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified WebUI
- PSPDFKit — Removed, use PDF.js + DevExpress instead
### Legacy Projects (VB.NET)
**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
### 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)