From b93dacff4431b0224b8c5af2f60fbbf11251b617 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Fri, 24 Apr 2026 09:55:57 +0700 Subject: [PATCH] [CLAUDE] Scripts: fix migrate-domains.ps1 ASCII-only (gotcha #30 PS 5.1) --- scripts/migrate-domains.ps1 | 124 +++++++++++++++++------------------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/scripts/migrate-domains.ps1 b/scripts/migrate-domains.ps1 index 4725220..e3cb6bf 100644 --- a/scripts/migrate-domains.ps1 +++ b/scripts/migrate-domains.ps1 @@ -1,24 +1,18 @@ # Migrate domains from *.huypham.vn to solutions.com.vn (Phase 6+ rebrand). -# Run trên VPS với admin privilege. Idempotent — chạy lại nếu fail giữa chừng. +# Run on VPS with admin privilege. Idempotent - retry if fail mid-way. # # Workflow (3 IIS sites): -# 1. Add binding mới cho domain mới (*:80 + *:443 với cert self-signed tạm) -# 2. Run win-acme xin cert Let's Encrypt qua HTTP-01 challenge (port 80) -# 3. Verify /health/ready + /health/live qua domain mới -# 4. Remove binding cũ (*:huypham.vn + *:user.huypham.vn) — OPTIONAL via -RemoveOld +# 1. Add binding for new domain (port 80) +# 2. Run win-acme for Let's Encrypt cert via HTTP-01 challenge +# 3. Verify /health/ready + /health/live via new domain +# 4. Remove old binding (optional, via -RemoveOld) # # Usage: -# .\migrate-domains.ps1 # giữ binding cũ, thêm mới -# .\migrate-domains.ps1 -RemoveOld # xóa binding cũ sau verify -# .\migrate-domains.ps1 -SkipCert # bỏ bước win-acme (nếu anh đã có cert) +# .\migrate-domains.ps1 # keep old binding, add new +# .\migrate-domains.ps1 -RemoveOld # remove old after verify +# .\migrate-domains.ps1 -SkipCert # skip win-acme step # -# Prereq: -# - DNS 3 domain mới đã trỏ 103.124.94.38 (user confirm) -# - Port 80+443 firewall open cho internet -# - win-acme đã cài (script setup-ssl.ps1 cài sẵn) -# - IIS binding cũ còn active (huypham.vn) -# -# Rollback: nếu fail, binding cũ vẫn còn → site vẫn phục vụ qua *.huypham.vn +# ASCII-only per gotcha #30 (PS 5.1 parser fail on UTF-8 diacritics). [CmdletBinding()] param( @@ -43,26 +37,28 @@ function Write-Warn2($msg){ Write-Host " [!!] $msg" -ForegroundColor Yellow } # ===================== 1. Pre-flight checks ===================== Write-Step "Pre-flight checks" -# 1a. Verify DNS của 3 domain mới trỏ đúng IP VPS -$vpsIp = (Invoke-WebRequest -Uri "https://api.ipify.org" -UseBasicParsing -TimeoutSec 5).Content.Trim() +$vpsIp = "103.124.94.38" +try { + $vpsIp = (Invoke-WebRequest -Uri "https://api.ipify.org" -UseBasicParsing -TimeoutSec 5).Content.Trim() +} catch { + Write-Warn2 "Cannot detect public IP via ipify - fallback to hardcoded $vpsIp" +} Write-Host " VPS public IP: $vpsIp" foreach ($d in @($NewApi, $NewAdmin, $NewUser)) { try { $resolved = (Resolve-DnsName -Name $d -Type A -ErrorAction Stop | Select-Object -First 1).IPAddress if ($resolved -eq $vpsIp) { - Write-OK "DNS $d -> $resolved" + Write-OK "DNS $d = $resolved" } else { - Write-Warn2 "DNS $d -> $resolved (KHONG KHOP VPS IP $vpsIp) — Let's Encrypt se fail!" - Read-Host "Continue anyway? (Ctrl+C abort, Enter continue)" + Write-Warn2 "DNS $d = $resolved (not matching VPS IP $vpsIp) - Let's Encrypt may fail" } } catch { - Write-Warn2 "DNS resolve FAIL cho $d — $_" + Write-Warn2 "DNS resolve FAIL for $d" exit 1 } } -# 1b. Verify 3 IIS site tồn tại $siteMap = @( @{ Site = "SolutionErp-Api"; Old = $OldApi; New = $NewApi }, @{ Site = "SolutionErp-Admin"; Old = $OldAdmin; New = $NewAdmin }, @@ -70,20 +66,19 @@ $siteMap = @( ) foreach ($s in $siteMap) { if (-not (Test-Path "IIS:\Sites\$($s.Site)")) { - Write-Error "Site '$($s.Site)' khong ton tai. Chay setup-iis-sites.ps1 truoc." + Write-Error "Site '$($s.Site)' not found. Run setup-iis-sites.ps1 first." exit 1 } Write-OK "IIS site $($s.Site) ready" } # ===================== 2. Add new bindings (keep old) ===================== -Write-Step "Add binding moi cho 3 site (giu binding cu)" +Write-Step "Add HTTP binding for 3 new domains" foreach ($s in $siteMap) { $site = $s.Site $new = $s.New - # Check binding HTTP (port 80) cho domain moi $existingHttp = Get-WebBinding -Name $site -Protocol http -HostHeader $new -ErrorAction SilentlyContinue if (-not $existingHttp) { New-WebBinding -Name $site -IPAddress "*" -Port 80 -HostHeader $new -Protocol http | Out-Null @@ -93,19 +88,19 @@ foreach ($s in $siteMap) { } } -# ===================== 3. Request cert Let's Encrypt (win-acme) ===================== +# ===================== 3. Request cert via win-acme ===================== if (-not $SkipCert) { - Write-Step "Request cert Let's Encrypt cho 3 domain moi" + Write-Step "Request Let's Encrypt cert for 3 new domains" $WacsExe = "C:\Program Files\win-acme\wacs.exe" if (-not (Test-Path $WacsExe)) { - Write-Error "win-acme chua cai. Run setup-ssl.ps1 truoc, hoac dùng -SkipCert." + Write-Error "win-acme not installed. Run setup-ssl.ps1 first, or use -SkipCert." exit 1 } foreach ($s in $siteMap) { Write-Host "" - Write-Host "==> Issue cert $($s.New)" -ForegroundColor Cyan + Write-Host "==> Cert for $($s.New)" -ForegroundColor Cyan $siteId = (Get-Website $s.Site).Id $wacsArgs = @( @@ -119,59 +114,57 @@ if (-not $SkipCert) { ) & $WacsExe @wacsArgs if ($LASTEXITCODE -ne 0) { - Write-Warn2 "Cert $($s.New) FAIL exit $LASTEXITCODE — skip, check:" + Write-Warn2 "Cert $($s.New) FAIL exit $LASTEXITCODE - check:" Write-Warn2 " 1. Port 80 internet -> VPS open?" Write-Warn2 " 2. DNS $($s.New) -> $vpsIp?" - Write-Warn2 " 3. HTTP binding $($s.Site) *:80:$($s.New) created?" + Write-Warn2 " 3. HTTP binding created?" } else { - Write-OK "Cert $($s.New) installed (auto HTTPS binding + http->https redirect)" + Write-OK "Cert $($s.New) installed (HTTPS binding + http->https)" } } } else { - Write-Host "==> SkipCert flag — bo qua xin cert Let's Encrypt" -ForegroundColor Yellow + Write-Host "==> SkipCert flag - bypass win-acme" -ForegroundColor Yellow } -# ===================== 4. Verify endpoint moi ===================== -Write-Step "Verify endpoint moi" +# ===================== 4. Verify new endpoints ===================== +Write-Step "Verify new endpoints" -Start-Sleep -Seconds 3 # cho IIS reload binding +Start-Sleep -Seconds 3 -function Test-Endpoint($url, $expectOk) { +function Test-Endpoint($url) { try { $r = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10 -SkipCertificateCheck - if ($r.StatusCode -eq 200) { Write-OK "$url -> 200 OK"; return $true } - Write-Warn2 "$url -> $($r.StatusCode) (expected 200)"; return $false + if ($r.StatusCode -eq 200) { Write-OK "$url = 200 OK"; return $true } + Write-Warn2 "$url = $($r.StatusCode) (expected 200)"; return $false } catch { - Write-Warn2 "$url FAIL: $_" + Write-Warn2 "$url FAIL" return $false } } -$apiLive = Test-Endpoint "https://$NewApi/health/live" $true -$apiReady = Test-Endpoint "https://$NewApi/health/ready" $true -$adminOk = Test-Endpoint "https://$NewAdmin" $true -$userOk = Test-Endpoint "https://$NewUser" $true +$apiLive = Test-Endpoint "https://$NewApi/health/live" +$apiReady = Test-Endpoint "https://$NewApi/health/ready" +$adminOk = Test-Endpoint "https://$NewAdmin" +$userOk = Test-Endpoint "https://$NewUser" $allOk = $apiLive -and $apiReady -and $adminOk -and $userOk # ===================== 5. Remove old bindings (optional) ===================== if ($RemoveOld) { if (-not $allOk) { - Write-Warn2 "Endpoint moi CHUA verify OK — skip remove old binding (giu fallback)" + Write-Warn2 "New endpoints not verified OK - skip removing old (keep fallback)" } else { - Write-Step "Remove binding cu (huypham.vn)" + Write-Step "Remove old bindings (huypham.vn)" foreach ($s in $siteMap) { $site = $s.Site $old = $s.Old - # Remove HTTP $httpOld = Get-WebBinding -Name $site -Protocol http -HostHeader $old -ErrorAction SilentlyContinue if ($httpOld) { Remove-WebBinding -Name $site -Protocol http -HostHeader $old -Port 80 Write-OK "Removed HTTP *:80:$old from $site" } - # Remove HTTPS $httpsOld = Get-WebBinding -Name $site -Protocol https -HostHeader $old -ErrorAction SilentlyContinue if ($httpsOld) { Remove-WebBinding -Name $site -Protocol https -HostHeader $old -Port 443 @@ -181,30 +174,33 @@ if ($RemoveOld) { } } else { Write-Host "" - Write-Host "[INFO] Binding cu (*.huypham.vn) van giu active. Run -RemoveOld sau khi verify xong." -ForegroundColor Yellow + Write-Host "[INFO] Old bindings (*.huypham.vn) still active. Run with -RemoveOld after verify." -ForegroundColor Yellow } # ===================== 6. Summary ===================== Write-Host "" -Write-Host "=" * 70 -ForegroundColor Cyan +Write-Host ("=" * 70) -ForegroundColor Cyan Write-Host " MIGRATION DONE" -ForegroundColor Cyan -Write-Host "=" * 70 -ForegroundColor Cyan +Write-Host ("=" * 70) -ForegroundColor Cyan Write-Host "" -Write-Host "3 domain moi:" -Write-Host " - https://$NewApi/health/live $(if ($apiLive) {'[OK]'} else {'[FAIL]'})" -Write-Host " - https://$NewApi/health/ready $(if ($apiReady) {'[OK]'} else {'[FAIL]'})" -Write-Host " - https://$NewAdmin $(if ($adminOk) {'[OK]'} else {'[FAIL]'})" -Write-Host " - https://$NewUser $(if ($userOk) {'[OK]'} else {'[FAIL]'})" +Write-Host "3 new domains:" +$statusApi = if ($apiLive) { "[OK]" } else { "[FAIL]" } +$statusReady = if ($apiReady) { "[OK]" } else { "[FAIL]" } +$statusAdmin = if ($adminOk) { "[OK]" } else { "[FAIL]" } +$statusUser = if ($userOk) { "[OK]" } else { "[FAIL]" } +Write-Host " - https://$NewApi/health/live $statusApi" +Write-Host " - https://$NewApi/health/ready $statusReady" +Write-Host " - https://$NewAdmin $statusAdmin" +Write-Host " - https://$NewUser $statusUser" Write-Host "" -Write-Host "Next step:" -Write-Host " 1. Trigger CI/CD redeploy (push empty commit hoac manual workflow dispatch)" -Write-Host " → BE rebuild voi CORS moi (allow https://admin.solutions.com.vn + eoffice.solutions.com.vn)" -Write-Host " → FE rebuild voi VITE_API_BASE_URL=https://api.solutions.com.vn" -Write-Host " 2. Test login qua browser: https://$NewAdmin + https://$NewUser" -Write-Host " 3. Sau 1-2 ngay verify stable → chay lai script voi -RemoveOld" -Write-Host " .\migrate-domains.ps1 -RemoveOld -SkipCert" +Write-Host "Next:" +Write-Host " 1. Trigger CI/CD redeploy (push empty commit)" +Write-Host " - BE rebuild with new CORS" +Write-Host " - FE rebuild with VITE_API_BASE_URL=https://$NewApi" +Write-Host " 2. Test login via browser" +Write-Host " 3. After 1-2 days verify stable: .\migrate-domains.ps1 -RemoveOld -SkipCert" Write-Host "" if (-not $allOk) { - Write-Warn2 "CO ENDPOINT FAIL — kiem tra Event Log + IIS log truoc khi proceed." + Write-Warn2 "Some endpoints failed - check Event Log and IIS log before proceed." exit 1 }