[CLAUDE] Skill · Docs: fix iis-deploy-runbook stale paths + S88 web.config closeout

iis-deploy-runbook SKILL corrected (discovered during the index.html-hardening task):
- physical paths apps\SolutionErp\{Api,Admin,User} were STALE (empty dir) -> real
  solution-erp\{api,fe-admin,fe-user} (verified via appcmd list site).
- web.config rule list fixed to the ACTUAL live config (HTTP->HTTPS + SPA-Routes excl
  /api + .webmanifest + 3 security headers; NO /api|/hubs ARR proxy -- FE calls api via CORS)
  + documented the S88 cache hardening (index.html no-cache, assets immutable).
- log/appsettings/scp/backup paths swept to the real base.
Session log: index.html no-cache hardening DONE (ca136d8) + lesson reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-25 11:42:23 +07:00
parent ca136d8e2e
commit d085436e49
2 changed files with 21 additions and 18 deletions

View File

@ -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``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 `<clientCache cacheControlMode="DisableCache">``index.html` (+ SPA-rewrite shell `/dashboard`…) = `Cache-Control: no-cache` (luôn revalidate → chống stale-shell-after-deploy = white-screen S88) · `<location path="assets"><clientCache UseMaxAge 365d + cacheControlCustom="public, immutable">` → 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<ID>\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 <pw> -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_<ts>.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
```

View File

@ -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) + **`<location path="assets">` `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`.**