diff --git a/COPILOT_CONTEXT.md b/COPILOT_CONTEXT.md index 4a31d22c..81ecf93b 100644 --- a/COPILOT_CONTEXT.md +++ b/COPILOT_CONTEXT.md @@ -1,564 +1,351 @@ -# EnvelopeGenerator — AI Context Reference +# EnvelopeGenerator — Current Workspace Context ## 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) - -**Receiver Architecture:** -- Receiver authentication for `EnvelopeReceiverPage.razor` is now validated server-side. -- 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. +- Senders authenticate, view envelope lists, and manage envelope workflows. +- Receivers authenticate per envelope, open PDFs, create signatures, and apply them in the viewer. +- The active UI stack is `Blazor Auto` with server-side and WebAssembly render modes. +- Primary UI/PDF libraries are `DevExpress` and `PDF.js`. --- -## 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:** -- **WebUI** (Server project): Hosts server-side components, YARP proxy, DevExpress backend services -- **WebUI.Client** (WASM project): Client-side components, business logic, services +This project contains: +- WebAssembly-rendered pages +- 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) - - 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` +This means the active app is a **merged UI + API host**. --- -## WebUI Route Structure +## Reverse Proxy -### Root Route -| Route | File | Location | Render Mode | +**Config file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/yarp.json` + +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 -| Route | File | Location | Render Mode | +### Server Pages (`EnvelopeGenerator.Server`) +| Route | File | Render Mode | Purpose | |---|---|---|---| -| `/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) +| `/envelope/{EnvelopeKey}` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` | InteractiveServer | Main receiver PDF viewer and signing page | +| `/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 | +| `/envelope/Embed` | `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_embed.razor` | InteractiveServer | Embedded browser PDF view test page | --- -## Architecture Evolution +## Current API Location -### Old Architecture (Deprecated v1) -- **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit) -- **Receiver UI:** Separate project -- **Backend:** `EnvelopeGenerator.API` +The active application exposes controllers from: +`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers` -### 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 controller set includes: +- `AnnotationController` +- `AuthController` +- `CacheController` +- `ConfigController` +- `DocumentController` +- `EmailTemplateController` +- `EnvelopeController` +- `EnvelopeReceiverController` +- `EnvelopeTypeController` +- `HistoryController` +- `LocalizationController` +- `ReadOnlyController` +- `ReceiverController` +- `SignatureController` +- `TfaRegistrationController` -### 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** +Do not assume API behavior lives only in `EnvelopeGenerator.API`; the active merged host contains controller endpoints directly. --- -## Solution Structure +## Authentication Model -| 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.** | +### Sender +Client login page uses `EnvelopeGenerator.Server.Client/Services/AuthService.cs`. + +Key sender endpoints: +- `POST /api/auth?cookie=true` — login +- `GET /api/auth/check` — current sender access check +- `POST /api/auth/logout` — logout + +### Receiver +Receiver authentication is **per envelope**. + +Key receiver endpoints used by client services: +- `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:** -- Culture preference stored in browser's `localStorage` (key: `AppCulture`) -- Managed by `CultureService.cs` (ReceiverUI/Services) -- Supported cultures: `de-DE`, `en-US`, `fr-FR` - -**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` +For signature placeholders, the service: +- reads document receiver elements +- filters them for the authenticated receiver +- converts coordinates to `UnitOfLength.Point` before UI use --- -## Key Files & Routes +## Receiver PDF Viewer -### 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. | +**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor` -### Server-Side Pages (WebUI) -| File | Route | Purpose | -|---|---|---| -| `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. | -| `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). | +Current receiver viewer characteristics: +- route: `/envelope/{EnvelopeKey}` +- render mode: `InteractiveServer` +- PDF rendering: `PDF.js` +- toolbar: page navigation, zoom, thumbnail toggle, signature navigation, signature reset +- signature popup: `DxPopup` +- thumbnail sidebar: resizable and stored in `localStorage` -### 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/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. | -| `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. | +### JS Assets +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js` + +### CSS +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css` + +### PDF.js CDN +- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js` +- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css` --- -## Coordinate System — CRITICAL +## Signature Workflow -**Database Format:** INCHES (GdPicture14 native) -**Origin:** Top-left corner -**Axes:** X right, Y down +Receiver signatures are handled as a **viewer overlay workflow**. -### 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 | -|---|---|---| -| **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` | +### 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. -**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`) + 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` +### Signature DTO +`EnvelopeGenerator.Server.Client/Models/SignatureCaptureDto.cs` ```csharp 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 string Position { get; init; } = ""; // Optional + public string Position { 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 -**Controller:** `API/Controllers/CacheController.cs` +### Cache key format +Current server-side key prefix: +- `envelope-generator.receiver-ui.signature:{receiverSignature}` -- `POST /api/Cache/SignatureCapture/{envelopeKey}` — Save -- `GET /api/Cache/SignatureCapture/{envelopeKey}` — Load -- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` — Delete +This is different from an envelope-key-only cache convention. -**Cache Key Format:** -``` -signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey} -``` +### Config +`EnvelopeGenerator.Server/EnvelopeGenerator.Server/Options/CacheOptions.cs` +- section name: `Cache` +- option: `SignatureCacheExpiration` -**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 GetSignatureAsync(string envelopeKey); - Task DeleteSignatureAsync(string envelopeKey); -} -``` - -**Error Handling:** Fire-and-forget saves, graceful degradation on load failure. +### Related controller +A cache API controller also exists in: +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/CacheController.cs` --- -## Sender Login +## Sender Dashboard -**Route:** `/sender/login` -**File:** `WebUI.Client/Pages/LoginSenderPage.razor` -**Tech:** Bootstrap 5 + DevExpress Blazing Berry theme +**Main file:** `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Pages/EnvelopeSenderPage.razor` -### AuthService Extension -**File:** `WebUI.Client/Services/AuthService.cs` +Current behavior: +- 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 -public enum SenderLoginResult { Success, InvalidCredentials, Error } - -public async Task 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 +The sender page is active, but create/edit/delete actions are still marked with TODO behavior in the UI page. --- -## Receiver Login +## Localization -**Route:** `/envelope/login/{EnvelopeKey}` -**File:** `WebUI.Client/Pages/LoginReceiverPage.razor` +Current server host localization setup in `Program.cs`: +- 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. - -### AuthService Method -```csharp -public enum EnvelopeLoginResult { Success, InvalidCode, NotFound, Error } - -public async Task 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}` +Do not assume the old ReceiverUI-only `localStorage` culture approach is the current source of truth for the active host. --- -## NuGet Packages (WebUI.Client) +## Coordinate System -| 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) | +### Source data +Database signature coordinates are still based on: +- **unit:** inches +- **origin:** top-left +- **axes:** X right, Y down -**External CDN:** -- PDF.js 3.11.174: `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js` +### Relevant conversions +- 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 | -|---|---| -| 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. | +### Client services +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/AuthService.cs` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/EnvelopeService.cs` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/DocumentService.cs` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server.Client/Services/SignatureCacheService.cs` + +### Server services +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeAuthService.cs` +- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/IEnvelopeAuthService.cs` +- `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 -**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. +- Treat `EnvelopeGenerator.Server` as the active main application host. +- Treat `EnvelopeGenerator.Server.Client` as the active client UI project. +- Prefer current `Server` / `Server.Client` paths over old `WebUI` / `ReceiverUI` references. +- Do not use `EnvelopeGenerator.Web` or `EnvelopeGenerator.ReceiverUI` as the primary implementation target unless explicitly asked. +- 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`. +- For DevExpress PDF viewer issues, remember server-side services are registered in `EnvelopeGenerator.Server`. --- -## 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) +**Last Updated:** 2026-06-29