[CLAUDE] Rebrand: 3 domain huypham.vn → solutions.com.vn + migrate script
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m52s

User request: anh trỏ 3 subdomain mới về VPS IP 103.124.94.38:
  - api.huypham.vn        → api.solutions.com.vn
  - admin.huypham.vn      → admin.solutions.com.vn
  - user.huypham.vn       → eoffice.solutions.com.vn

Verified DNS: cả 3 resolve 103.124.94.38 ✓

Update 17 file repo:
FE (4): fe-admin/.env.production + fe-user/.env.production
        (VITE_API_BASE_URL → https://api.solutions.com.vn)
        fe-admin/src/lib/{api,realtime}.ts + fe-user equivalents (comment)
BE (1): appsettings.Production.json.example — CORS AllowedOrigins
CI/CD (1): .gitea/workflows/deploy.yml — smoke test URL
Scripts (3): setup-iis-sites (DomainApi/Admin/User), setup-ssl (3 host),
             deploy-all (verify curls)
Docs (5): STATUS, HANDOFF, PROJECT-MAP, vps-setup, gotchas
Skill (1): iis-deploy-runbook — 3 site table + description
Email admin@huypham.vn giữ nguyên (Let's Encrypt contact — không phải
domain serve).

Thêm scripts/migrate-domains.ps1 — 1-shot VPS migration:
  1. Pre-flight: resolve DNS 3 domain → verify IP VPS khớp
  2. Add HTTP binding mới cho 3 IIS site (giữ binding cũ làm fallback)
  3. Run win-acme xin 3 cert Let's Encrypt qua HTTP-01 challenge
     (auto add HTTPS binding + http→https redirect)
  4. Verify /health/live + /health/ready + 2 FE endpoint
  5. (Optional -RemoveOld) xóa binding huypham.vn sau verify OK
Rollback: nếu fail, binding cũ vẫn active → site serve qua huypham.vn.

Anh chạy trên VPS:
  cd C:\solution-erp\scripts  ;  .\migrate-domains.ps1
  # Sau 1-2 ngày verify stable:
  .\migrate-domains.ps1 -RemoveOld -SkipCert
This commit is contained in:
pqhuy1987
2026-04-24 09:43:05 +07:00
parent 7ca6c914fa
commit 66c1a5c170
18 changed files with 263 additions and 53 deletions

210
scripts/migrate-domains.ps1 Normal file
View File

@ -0,0 +1,210 @@
# 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.
#
# 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
#
# 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)
#
# 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
[CmdletBinding()]
param(
[string]$NewApi = "api.solutions.com.vn",
[string]$NewAdmin = "admin.solutions.com.vn",
[string]$NewUser = "eoffice.solutions.com.vn",
[string]$OldApi = "api.huypham.vn",
[string]$OldAdmin = "admin.huypham.vn",
[string]$OldUser = "user.huypham.vn",
[string]$AdminEmail = "admin@huypham.vn",
[switch]$RemoveOld,
[switch]$SkipCert
)
$ErrorActionPreference = 'Stop'
Import-Module WebAdministration
function Write-Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan }
function Write-OK($msg) { Write-Host " [OK] $msg" -ForegroundColor Green }
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()
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"
} 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)"
}
} catch {
Write-Warn2 "DNS resolve FAIL cho $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 },
@{ Site = "SolutionErp-User"; Old = $OldUser; New = $NewUser }
)
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."
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)"
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
Write-OK "Add HTTP binding: $site *:80:$new"
} else {
Write-Host " HTTP binding exists: $site *:80:$new"
}
}
# ===================== 3. Request cert Let's Encrypt (win-acme) =====================
if (-not $SkipCert) {
Write-Step "Request cert Let's Encrypt cho 3 domain moi"
$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."
exit 1
}
foreach ($s in $siteMap) {
Write-Host ""
Write-Host "==> Issue cert $($s.New)" -ForegroundColor Cyan
$siteId = (Get-Website $s.Site).Id
$wacsArgs = @(
"--target", "manual",
"--host", $s.New,
"--store", "certificatestore",
"--installation", "iis",
"--installationsiteid", $siteId,
"--accepttos",
"--emailaddress", $AdminEmail
)
& $WacsExe @wacsArgs
if ($LASTEXITCODE -ne 0) {
Write-Warn2 "Cert $($s.New) FAIL exit $LASTEXITCODE — skip, 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?"
} else {
Write-OK "Cert $($s.New) installed (auto HTTPS binding + http->https redirect)"
}
}
} else {
Write-Host "==> SkipCert flag — bo qua xin cert Let's Encrypt" -ForegroundColor Yellow
}
# ===================== 4. Verify endpoint moi =====================
Write-Step "Verify endpoint moi"
Start-Sleep -Seconds 3 # cho IIS reload binding
function Test-Endpoint($url, $expectOk) {
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
} catch {
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
$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)"
} else {
Write-Step "Remove binding cu (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
Write-OK "Removed HTTPS *:443:$old from $site"
}
}
}
} else {
Write-Host ""
Write-Host "[INFO] Binding cu (*.huypham.vn) van giu active. Run -RemoveOld sau khi verify xong." -ForegroundColor Yellow
}
# ===================== 6. Summary =====================
Write-Host ""
Write-Host "=" * 70 -ForegroundColor Cyan
Write-Host " MIGRATION DONE" -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 ""
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 ""
if (-not $allOk) {
Write-Warn2 "CO ENDPOINT FAIL — kiem tra Event Log + IIS log truoc khi proceed."
exit 1
}