docs: add culture management migration warnings for Server/Auto
Prepare for Blazor Server/Auto migration by documenting current WASM-specific culture initialization approach and providing detailed migration checklist in COPILOT_CONTEXT.md.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# EnvelopeGenerator — AI Context Reference
|
||||
# EnvelopeGenerator — AI Context Reference
|
||||
|
||||
## Purpose
|
||||
Digital document signing system with **unified Blazor WASM frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs.
|
||||
@@ -65,8 +65,8 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
|
||||
- **Backend:** `EnvelopeGenerator.API`
|
||||
|
||||
### Current Architecture
|
||||
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM) — **Both Senders & Receivers**
|
||||
- **Backend:** `EnvelopeGenerator.API` — **Both Senders & Receivers**
|
||||
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM) — **Both Senders & Receivers**
|
||||
- **Backend:** `EnvelopeGenerator.API` — **Both Senders & Receivers**
|
||||
- **Libraries:** DevExpress + PDF.js
|
||||
- **PSPDFKit:** **REMOVED**
|
||||
|
||||
@@ -88,15 +88,80 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
|
||||
|
||||
---
|
||||
|
||||
## Localization & Culture Management
|
||||
|
||||
**Current Architecture:** Blazor WebAssembly (client-side culture management)
|
||||
|
||||
### Implementation Details
|
||||
|
||||
**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`
|
||||
|
||||
---
|
||||
|
||||
## Key Files & Routes
|
||||
|
||||
| File | Route/Purpose |
|
||||
|---|---|
|
||||
| `ReceiverUI/Pages/Index.razor` | `/` — Application entry point (landing page). |
|
||||
| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `/sender` — Sender dashboard (envelope list). |
|
||||
| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` — Receiver PDF viewer & signing. |
|
||||
| `ReceiverUI/Pages/LoginSenderPage.razor` | `/sender/login` — Sender username/password auth. |
|
||||
| `ReceiverUI/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` — Receiver access code auth. |
|
||||
| `ReceiverUI/Pages/Index.razor` | `/` — Application entry point (landing page). |
|
||||
| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `/sender` — Sender dashboard (envelope list). |
|
||||
| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` — Receiver PDF viewer & signing. |
|
||||
| `ReceiverUI/Pages/LoginSenderPage.razor` | `/sender/login` — Sender username/password auth. |
|
||||
| `ReceiverUI/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` — Receiver access code auth. |
|
||||
| `ReceiverUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). |
|
||||
| `ReceiverUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
|
||||
| `ReceiverUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
|
||||
@@ -106,7 +171,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
|
||||
|
||||
---
|
||||
|
||||
## Coordinate System — CRITICAL
|
||||
## Coordinate System — CRITICAL
|
||||
|
||||
**Database Format:** INCHES (GdPicture14 native)
|
||||
**Origin:** Top-left corner
|
||||
@@ -135,7 +200,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
|
||||
|
||||
---
|
||||
|
||||
## EnvelopeReceiver — PDF.js Viewer & Signing
|
||||
## EnvelopeReceiver — PDF.js Viewer & Signing
|
||||
|
||||
**Route:** `/envelope/{EnvelopeKey}`
|
||||
**Tech:** PDF.js 3.11.174 + Blazor WASM + configurable quality
|
||||
@@ -178,7 +243,7 @@ window.pdfViewer = {
|
||||
|
||||
---
|
||||
|
||||
## Signature Workflow — EnvelopeReceiver
|
||||
## Signature Workflow — EnvelopeReceiver
|
||||
|
||||
**IMPORTANT:** iText7 NOT used (GPL license issue). Client-side overlay system only.
|
||||
|
||||
@@ -231,9 +296,9 @@ public sealed record SignatureCaptureDto {
|
||||
### 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
|
||||
- `POST /api/Cache/SignatureCapture/{envelopeKey}` — Save
|
||||
- `GET /api/Cache/SignatureCapture/{envelopeKey}` — Load
|
||||
- `DELETE /api/Cache/SignatureCapture/{envelopeKey}` — Delete
|
||||
|
||||
**Cache Key Format:**
|
||||
```
|
||||
@@ -299,7 +364,7 @@ public async Task<SenderLoginResult> LoginSenderAsync(string username, string pa
|
||||
|
||||
**Response:**
|
||||
- `200 OK` ? Cookie set, redirect to `/sender`
|
||||
- `401 Unauthorized` ? Show error: "Ungültige Anmeldedaten"
|
||||
- `401 Unauthorized` ? Show error: "Ungültige Anmeldedaten"
|
||||
- Other ? Show error: "Serverfehler"
|
||||
|
||||
**Cookie:** HTTP-only, Secure (HTTPS), SameSite=Strict
|
||||
@@ -357,7 +422,7 @@ public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, st
|
||||
|
||||
---
|
||||
|
||||
## Mistakes History — Do NOT Repeat
|
||||
## Mistakes History — Do NOT Repeat
|
||||
|
||||
| Mistake | Why Wrong |
|
||||
|---|---|
|
||||
@@ -376,8 +441,8 @@ public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, st
|
||||
|
||||
### Deprecated Projects
|
||||
**DO NOT USE:**
|
||||
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified ReceiverUI
|
||||
- PSPDFKit — Removed, use PDF.js + DevExpress instead
|
||||
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified ReceiverUI
|
||||
- PSPDFKit — Removed, use PDF.js + DevExpress instead
|
||||
|
||||
### Legacy Projects (VB.NET)
|
||||
**DO NOT TOUCH:** `EnvelopeGenerator.Service`, `EnvelopeGenerator.Form`, `EnvelopeGenerator.BBTests`
|
||||
@@ -402,8 +467,8 @@ Proves database uses INCHES natively.
|
||||
## Quick Reference
|
||||
|
||||
### When working with coordinates:
|
||||
1. **Database ? UI:** INCHES × 72 = PDF Points
|
||||
2. **UI ? Display:** Points × scale = Pixels
|
||||
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:
|
||||
|
||||
@@ -50,7 +50,25 @@ ReportStorageWebExtension.RegisterExtensionGlobal(new InMemoryReportStorageWebEx
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
// Initialize culture BEFORE running the app
|
||||
// ⚠️ IMPORTANT: BLAZOR WASM-SPECIFIC CULTURE INITIALIZATION
|
||||
// This approach sets DefaultThreadCurrentCulture globally, which is SAFE for WebAssembly
|
||||
// because each user runs their own isolated app instance in their browser.
|
||||
//
|
||||
// ⚠️ TODO: REMOVE/REFACTOR WHEN MIGRATING TO BLAZOR SERVER/AUTO
|
||||
// In Server/Auto render modes, this is DANGEROUS because:
|
||||
// - Server runs a single shared instance for all users
|
||||
// - Setting global culture affects ALL connected users simultaneously
|
||||
// - Race conditions and culture conflicts will occur
|
||||
//
|
||||
// Migration Guide:
|
||||
// - Option 1: Use RequestLocalizationMiddleware for per-request culture
|
||||
// - Option 2: Use CascadingParameter with per-circuit culture state
|
||||
// - See: https://learn.microsoft.com/aspnet/core/blazor/globalization-localization
|
||||
//
|
||||
// Related files to update on migration:
|
||||
// - LanguageSelector.razor (remove manual culture setting)
|
||||
// - App.razor (may need CascadingValue for culture)
|
||||
// - Startup/Program.cs (add middleware)
|
||||
var cultureService = host.Services.GetRequiredService<CultureService>();
|
||||
var culture = await cultureService.InitializeCultureAsync();
|
||||
CultureInfo.DefaultThreadCurrentCulture = culture;
|
||||
|
||||
Reference in New Issue
Block a user