[CLAUDE] Docs: chốt session Tier 3 feature-complete + versioned workflow
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m33s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m33s
- Session log 2026-04-22-0300 (A→K): attachment, SignalR, form builder, PDF, dynamic + versioned workflow, nested menu, 3-panel permissions, seed master, brand identity, content polish, Gitea fix - STATUS: Tier 3 feature-complete snapshot + cumulative stats (24 tables, ~50 endpoints, 8 migrations); next-up = UAT + Email SMTP (blocked) + rotate creds + SQL backup schedule - HANDOFF: rewrite brief cho session mới — phase 5 prod done, Tier 3 đóng gói, quick sanity-check 2 app, versioned workflow quick ref, file active hiện trạng, git state - migration-todos: tick Tier 3 items (attachment/realtime/form builder/ PDF/dynamic+versioned workflow/nested menu) + thêm iter-3 versioned workflow section + post-launch list - schema-diagram: +5 table (Notifications, WorkflowTypeAssignments, WorkflowDefinitions, WorkflowSteps, WorkflowStepApprovers); indexes mới, cardinality FK restrict cho pinned policy, truy vấn tiêu biểu - workflow-contract: +section 7bis resolution order, 7ter admin designer flow, updated data model + code pointers Tier 3 - PROJECT-MAP: module map post-Tier-3 (3 box mới Notification/ Attachment/Branding + Infra/DevOps box), API namespace đầy đủ, architectural wins 5 điểm - contract-workflow skill: versioned workflow section, policy resolution code snippet, admin designer flow, code pointers Tier 3, tier 4+ backlog - gotchas +7 bẫy mới (#26-32): SignalR WebSocket headers, interceptor 2-phase pattern, LibreOffice mirror 404, PS 5.1 UTF-16 GITHUB_PATH, PS 5.1 diacritics parse, Dialog size TS, NavLink end query-params Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
107
docs/gotchas.md
107
docs/gotchas.md
@ -197,6 +197,109 @@ Tương tự khi dùng URL Rewrite `<serverVariables>` cần unlock `system.webS
|
||||
|
||||
**Cảnh báo co-existence:** Trên VPS shared với project khác, enable feature mới qua `Install-WindowsFeature` có thể làm sập site project khác. Luôn test all site sau mỗi enable.
|
||||
|
||||
## SignalR / Realtime
|
||||
|
||||
### 26. SignalR WebSocket không cho custom Authorization header
|
||||
|
||||
**Triệu chứng:** `new HubConnectionBuilder().withUrl('/hubs/x', { headers: { Authorization: ... } })` — WebSocket transport vẫn 401.
|
||||
|
||||
**Nguyên nhân:** Browser WebSocket API không cho set custom headers cho handshake. Chỉ 2 transport khác (SSE / LongPolling) mới dùng headers.
|
||||
|
||||
**Fix:**
|
||||
- FE: dùng `accessTokenFactory: () => token` — SignalR client tự append `?access_token=` query cho WebSocket
|
||||
- BE: Wire JWT bearer `OnMessageReceived` để đọc token từ query khi path matches `/hubs/*`:
|
||||
```csharp
|
||||
options.Events = new JwtBearerEvents {
|
||||
OnMessageReceived = ctx => {
|
||||
var accessToken = ctx.Request.Query["access_token"];
|
||||
var path = ctx.HttpContext.Request.Path;
|
||||
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
|
||||
ctx.Token = accessToken;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 27. SignalR SaveChangesInterceptor — capture Added ở SavingChanges, push ở SavedChanges
|
||||
|
||||
**Lý do:** SavedChanges chỉ có entries sau commit thành công. Nhưng ở SavedChanges thì `EntityEntry.State` đã về `Unchanged` → không thể filter `Added`.
|
||||
|
||||
**Fix:** 2-phase pattern:
|
||||
```csharp
|
||||
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(...) {
|
||||
_pending = eventData.Context.ChangeTracker.Entries<Notification>()
|
||||
.Where(e => e.State == EntityState.Added)
|
||||
.Select(e => e.Entity).ToList();
|
||||
return base.SavingChangesAsync(...);
|
||||
}
|
||||
|
||||
public override async ValueTask<int> SavedChangesAsync(..., int result, ...) {
|
||||
foreach (var n in _pending) await _realtimeNotifier.PushAsync(n);
|
||||
_pending.Clear();
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
## DevOps / CI/CD
|
||||
|
||||
### 28. LibreOffice download URL 404 khi pin wrong version
|
||||
|
||||
**Triệu chứng:** `Invoke-WebRequest https://download.documentfoundation.org/libreoffice/stable/25.2.7/...` → 404.
|
||||
|
||||
**Nguyên nhân:** LibreOffice mirror chỉ giữ vài version mới nhất. 25.2.7, 24.8.7 không có. Chỉ 25.8.6 tồn tại tại thời điểm cài.
|
||||
|
||||
**Fix:** Check mirror URL trước khi pin. Dùng `Invoke-WebRequest -Method Head` verify trước download thật.
|
||||
|
||||
### 29. PowerShell 5.1 `>> $GITHUB_PATH` ghi UTF-16 → NUL byte crash Gitea Actions
|
||||
|
||||
**Triệu chứng:** Gitea Actions job fail với "NUL byte in PATH". `echo "C:\\dotnet" >> $env:GITHUB_PATH`.
|
||||
|
||||
**Nguyên nhân:** PS 5.1 default encoding UTF-16 LE BOM khi redirect `>>`. Gitea reads PATH as UTF-8 → NUL byte xuất hiện sau mỗi ASCII char.
|
||||
|
||||
**Fix:** Dùng `Out-File -Encoding utf8 -Append`:
|
||||
```powershell
|
||||
"C:\dotnet" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
```
|
||||
|
||||
Hoặc drop step GITHUB_PATH hoàn toàn nếu NSSM PATH đã có sẵn dotnet+node.
|
||||
|
||||
### 30. PS 5.1 scripts với Vietnamese diacritics → parser error
|
||||
|
||||
**Triệu chứng:** `Cannot parse script: Unexpected character` khi chạy PS script có text tiếng Việt inline.
|
||||
|
||||
**Nguyên nhân:** PS 5.1 đọc file script với ANSI codepage (Windows-1258 hoặc default 1252), không phải UTF-8.
|
||||
|
||||
**Fix (1):** Save script với BOM UTF-8 (Write-Host có dấu vẫn work):
|
||||
```powershell
|
||||
[System.IO.File]::WriteAllText($path, $content, [System.Text.Encoding]::UTF8)
|
||||
```
|
||||
|
||||
**Fix (2, safer):** Rewrite script ASCII-only. Text tiếng Việt nằm trong log messages thay dùng code:
|
||||
```powershell
|
||||
Write-Host "Setup IIS sites done" # thay vi "Hoan tat"
|
||||
```
|
||||
|
||||
## TypeScript / FE
|
||||
|
||||
### 31. Dialog `size="xl"` TS2322 nếu variant không khai báo
|
||||
|
||||
**Triệu chứng:** `<Dialog size="xl">` → `Type '"xl"' is not assignable to type '"sm" | "md" | "lg"'`.
|
||||
|
||||
**Fix:** Sửa usage về `"lg"`, hoặc add `"xl"` vào `DialogSize` type union trong `components/ui/Dialog.tsx`. Đừng lazy `as any`.
|
||||
|
||||
## FE architecture
|
||||
|
||||
### 32. NavLink `end` prop cho query-param URL variants
|
||||
|
||||
**Triệu chứng:** `/contracts?type=1` highlight cả `/contracts` lẫn `/contracts?type=2` cùng lúc.
|
||||
|
||||
**Nguyên nhân:** Default NavLink `startsWith` match. Query string không parse distinct paths.
|
||||
|
||||
**Fix:** `end={path.includes('?')}` trong resolvePath để query-variants match exact:
|
||||
```tsx
|
||||
<NavLink to={path} end={path.includes('?')}>
|
||||
```
|
||||
|
||||
## Checklist debug bug mới
|
||||
|
||||
1. Build pass không? → fail → check using + package version compat
|
||||
@ -207,4 +310,6 @@ Tương tự khi dùng URL Rewrite `<serverVariables>` cần unlock `system.webS
|
||||
6. Nếu TS error → check `erasableSyntaxOnly`, `verbatimModuleSyntax`
|
||||
7. Nếu EF expression tree → tách logic ra ngoài query
|
||||
8. Nếu Unicode CLI → dùng file payload
|
||||
9. Nếu workflow 403 → check FE NEXT_PHASES sync BE
|
||||
9. Nếu workflow 403 → check FE `workflow.nextPhases` sync từ BE pinned policy
|
||||
10. Nếu SignalR 401 → dùng `accessTokenFactory` + BE OnMessageReceived hook (#26)
|
||||
11. Nếu PS 5.1 script fail → check encoding UTF-8 / BOM / ASCII-only (#30)
|
||||
|
||||
Reference in New Issue
Block a user