diff --git a/.claude/skills/iis-deploy-runbook/SKILL.md b/.claude/skills/iis-deploy-runbook/SKILL.md index 2571778..ec73f12 100644 --- a/.claude/skills/iis-deploy-runbook/SKILL.md +++ b/.claude/skills/iis-deploy-runbook/SKILL.md @@ -46,15 +46,17 @@ Internet | Site | Binding | Physical path | Apool | Purpose | |---|---|---|---|---| -| `SolutionErp-Api` | `*:443:api.solutions.com.vn` HTTPS | `C:\inetpub\apps\SolutionErp\Api\` | out-of-process Kestrel | ASP.NET Core 10 API (port 5443 internal) | -| `SolutionErp-Admin` | `*:443:admin.solutions.com.vn` HTTPS + `*:80` redirect | `C:\inetpub\apps\SolutionErp\Admin\` | static (no app pool .NET) | React build fe-admin | -| `SolutionErp-User` | `*:443:eoffice.solutions.com.vn` HTTPS + `*:80` redirect | `C:\inetpub\apps\SolutionErp\User\` | static | React build fe-user | +| `SolutionErp-Api` | `*:443:api.solutions.com.vn` (+`api.huypham.vn`) HTTPS | `C:\inetpub\solution-erp\api\` | out-of-process Kestrel | ASP.NET Core 10 API (port 5443 internal) | +| `SolutionErp-Admin` | `*:443:admin.solutions.com.vn` (+`admin.huypham.vn`) HTTPS + `*:80` redirect | `C:\inetpub\solution-erp\fe-admin\` | static (no app pool .NET) | React build fe-admin | +| `SolutionErp-User` | `*:443:eoffice.solutions.com.vn` HTTPS + `*:80` redirect | `C:\inetpub\solution-erp\fe-user\` | static | React build fe-user | -**SPA web.config:** 2 FE có `URL Rewrite` rule: -1. HTTP → HTTPS redirect (bắt buộc, CORS whitelist chỉ https) -2. `/api/* → http://127.0.0.1:5443/api/*` (ARR reverse proxy) -3. `/hubs/* → http://127.0.0.1:5443/hubs/*` (SignalR) -4. React Router fallback: `/*` → `/index.html` +> ⚠️ **Physical paths verified via `appcmd list site` S88** — runbook trước ghi `C:\inetpub\apps\SolutionErp\{Api,Admin,User}` (STALE, dir rỗng). Đường THẬT = `C:\inetpub\solution-erp\{api,fe-admin,fe-user}`. + +**SPA web.config** (`{fe-admin,fe-user}/public/web.config`, **version-controlled S88** — Vite copy `public/*`→`dist/`→deploy `Copy-Item -Force`; `deploy.yml` cũ `Remove-Item …\* -Exclude web.config` = trước S88 file manage-tay trên server). Rule THẬT (đọc live S88): +1. **HTTP → HTTPS** redirect (`{HTTPS} ^OFF$`, Permanent; CORS whitelist chỉ https). KHÔNG có `.well-known` exclude (win-acme validation khác). +2. **SPA Routes** fallback: non-file/non-dir/non-`^/api` → rewrite `/` (index.html). *(KHÔNG có `/api` hay `/hubs` ARR proxy — FE gọi thẳng `https://api.solutions.com.vn` qua CORS.)* +3. `.webmanifest` mime + 3 security header (X-Content-Type-Options / X-Frame-Options DENY / Referrer-Policy). +4. **[S88 hardening] Cache:** root `` → `index.html` (+ SPA-rewrite shell `/dashboard`…) = `Cache-Control: no-cache` (luôn revalidate → chống stale-shell-after-deploy = white-screen S88) · `` → hashed asset `immutable, max-age=1y`. **Verify:** `curl -I https://eoffice.solutions.com.vn/` → `no-cache`; `curl -I .../assets/index-*.js` → `immutable`; `/dashboard` → 200 (routing intact). ## Quick commands @@ -77,13 +79,13 @@ Get-Website -Name "SolutionErp-*" | Format-Table Name, State, Bindings ```powershell # Serilog file rolling daily -Get-Content "C:\inetpub\apps\SolutionErp\Api\Logs\log-$(Get-Date -Format 'yyyyMMdd').txt" -Tail 50 +Get-Content "C:\inetpub\solution-erp\api\Logs\log-$(Get-Date -Format 'yyyyMMdd').txt" -Tail 50 # IIS log Get-Content "C:\inetpub\logs\LogFiles\W3SVC\u_ex$(Get-Date -Format 'yyMMdd').log" -Tail 30 # Stdout log khi crash startup -Get-Content "C:\inetpub\apps\SolutionErp\Api\Logs\stdout_*.log" -Tail 30 +Get-Content "C:\inetpub\solution-erp\api\Logs\stdout_*.log" -Tail 30 ``` ### Health check @@ -258,7 +260,7 @@ sqlcmd -S .\SQLEXPRESS -U vrapp -P -Q "SELECT DB_NAME()" # Expect: SolutionErp # 4. appsettings connection string (qua Gitea secrets) -# Check C:\inetpub\apps\SolutionErp\Api\appsettings.Production.json có ConnectionStrings:DefaultConnection +# Check C:\inetpub\solution-erp\api\appsettings.Production.json có ConnectionStrings:DefaultConnection ``` ## Deploy steps (CI/CD xanh) @@ -275,7 +277,7 @@ Push to main → npm run build (fe-admin + fe-user) → render appsettings.Production.json từ secrets (JWT_SECRET, DB_CONNECTION) → stop app pool SolutionErp-Api - → xcopy publish → C:\inetpub\apps\SolutionErp\{Api,Admin,User} + → Copy-Item publish/dist → C:\inetpub\solution-erp\{api,fe-admin,fe-user} (Remove-Item -Exclude web.config first) → start app pool → curl /health/ready → must be 200 trong 30s → report status @@ -290,9 +292,9 @@ cd fe-admin; npm ci; npm run build; cd .. cd fe-user; npm ci; npm run build; cd .. # Scp sang server (cần plink/pscp hoặc rsync) -scp -r .\publish\api\* user@server:C:/inetpub/apps/SolutionErp/Api/ -scp -r .\fe-admin\dist\* user@server:C:/inetpub/apps/SolutionErp/Admin/ -scp -r .\fe-user\dist\* user@server:C:/inetpub/apps/SolutionErp/User/ +scp -r .\publish\api\* user@server:C:/inetpub/solution-erp/api/ +scp -r .\fe-admin\dist\* user@server:C:/inetpub/solution-erp/fe-admin/ +scp -r .\fe-user\dist\* user@server:C:/inetpub/solution-erp/fe-user/ # Trên server: Restart-WebAppPool -Name "SolutionErp-Api" @@ -303,12 +305,12 @@ curl http://127.0.0.1:5443/health/ready ```powershell # DB backup (script sẵn, chưa schedule): -& "C:\inetpub\apps\SolutionErp\scripts\backup-sql.ps1" +& "C:\inetpub\solution-erp\scripts\backup-sql.ps1" # Output: backup/SolutionErp_.bak (compressed + retention 30d) # Schedule daily 03:00: schtasks /create /tn "SolutionErp Backup" ` - /tr "powershell -ExecutionPolicy Bypass -File C:\inetpub\apps\SolutionErp\scripts\backup-sql.ps1" ` + /tr "powershell -ExecutionPolicy Bypass -File C:\inetpub\solution-erp\scripts\backup-sql.ps1" ` /sc DAILY /st 03:00 /ru SYSTEM ``` diff --git a/docs/changelog/sessions/2026-06-25-S88-pe-stability-audit-d1-d2.md b/docs/changelog/sessions/2026-06-25-S88-pe-stability-audit-d1-d2.md index 24c6ebf..4555636 100644 --- a/docs/changelog/sessions/2026-06-25-S88-pe-stability-audit-d1-d2.md +++ b/docs/changelog/sessions/2026-06-25-S88-pe-stability-audit-d1-d2.md @@ -59,4 +59,5 @@ em-main-led BE core (financial/coupled/design-heavy = create-contract multi + Bl ## Post-closeout addenda (cùng phiên S88) - **🔥 White-screen incident (eoffice/fe-user) — KHÔNG phải code bug:** anh báo màn-hình-trắng-tinh sau D2 deploy. Diagnose: deploy KHỎE — fresh load eoffice boot OK (login render, 0 console error qua Chrome MCP), BE all 200, MỌI asset 200 (old+new, KHÔNG 404), changes PE-only + landing `/dashboard` (em không đụng). ⟹ **client-side transient/stale-cache** (tab mở trong lúc IIS app-pool recycle khi deploy). Fix: anh hard-refresh `Ctrl+Shift+R` → khỏi. KHÔNG revert (code lành). **🔴 Robustness finding (NEXT/ops):** IIS serve `index.html` KHÔNG `Cache-Control: no-cache` (chỉ ETag/Last-Modified) → tab cũ có thể kẹt index stale qua deploy. Repo KHÔNG có `web.config` (SPA routing + headers = server-side VPS). Đề-xuất: add `web.config` vào `fe-user/public` + `fe-admin/public` (`index.html`→no-cache + `assets/*`→immutable 1y) SAU khi inspect server config (đừng vỡ SPA rewrite) — iis-deploy-runbook task, cẩn-thận-prod. (Lesson: build+deploy PASS ≠ runtime-render OK; nhưng white-screen ≠ luôn-là-code — verify deploy-health TRƯỚC khi revert.) -- **PE Block B table-align (`3f9dc32`):** 2 bảng "TỔNG HỢP NGÂN SÁCH TRÌNH KÝ" (A NGÂN SÁCH / B THỰC HIỆN) lệch cột Dự án/PRO/CCM (A `w-40` vs B `w-44`, auto-layout). Fix: cả 2 `table-fixed` + đồng-nhất `w-20/w-44/w-44` → thẳng hàng 1 lưới. FE-only 2-app SHA `93a4e8a1`, build PASS. +- **PE Block B table-align (`3f9dc32` Run PASS):** 2 bảng "TỔNG HỢP NGÂN SÁCH TRÌNH KÝ" (A NGÂN SÁCH / B THỰC HIỆN) lệch cột Dự án/PRO/CCM (A `w-40` vs B `w-44`, auto-layout). Fix: cả 2 `table-fixed` + đồng-nhất `w-20/w-44/w-44` → thẳng hàng 1 lưới. FE-only 2-app SHA `93a4e8a1`, build PASS. Bundle rotate admin `BQq2eTP6`→`DcAC7npx` / user `dHa64I59`→`PPg8JXNc`. +- **✅ index.html no-cache hardening DONE (`ca136d8`, task-chip — đóng white-screen-class):** Inspect live (runbook path **STALE** → THẬT `C:\inetpub\solution-erp\{api,fe-admin,fe-user}` qua `appcmd list site`) → author `web.config` replicate **EXACT** live rule (HTTP→HTTPS + SPA-Routes excl `^/api` + .webmanifest mime + 3 security header) + **root `clientCache=DisableCache`** (index.html + SPA-shell `/dashboard`… = `Cache-Control: no-cache` luôn-revalidate) + **`` `immutable, max-age=1y`**. Version-control `fe-*/public/web.config` (Vite→dist→deploy `Copy-Item -Force`; `deploy.yml` `Remove-Item -Exclude web.config` = manage-tay trước S88). **Cẩn-thận-prod: backup→apply-live→verify-on-server TRƯỚC commit** — both site `index.html` no-cache + asset immutable + `/dashboard` 200 + http→https 301, **live-sha===committed-sha** (`1239B467`). `iis-deploy-runbook` SKILL FIXED (stale path table + web.config desc + log/scp/backup paths). `api`=.NET API → N/A. **Lesson `feedback_prod_config_inspect_before_change`.**