[CLAUDE] Rebrand: 3 domain huypham.vn → solutions.com.vn + migrate script
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m52s
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:
@ -15,7 +15,7 @@
|
||||
# - SQL Server with login 'sa' + 'vrapp' exists
|
||||
# - .NET 10 Hosting Bundle installed
|
||||
# - Port 80+443 firewall open
|
||||
# - DNS api/admin/user.huypham.vn pointing to VPS
|
||||
# - DNS api/admin/eoffice.solutions.com.vn pointing to VPS
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
@ -120,7 +120,7 @@ if (Test-Path $example) {
|
||||
if (-not $SkipSsl) {
|
||||
Write-Banner "Step 4/4 (SSL): win-acme Let's Encrypt"
|
||||
Write-Host "WARNING: Port 80 must be reachable from Internet (Let's Encrypt HTTP-01)"
|
||||
Write-Host " Test from outside: curl http://api.huypham.vn"
|
||||
Write-Host " Test from outside: curl http://api.solutions.com.vn"
|
||||
$confirm = Read-Host "Continue? (y/N)"
|
||||
if ($confirm -eq 'y') {
|
||||
try {
|
||||
@ -151,9 +151,9 @@ Write-Banner "[OK] DEPLOY ALL DONE - $([int]$duration.TotalMinutes)m $([int]($du
|
||||
Write-Host ""
|
||||
Write-Host "Next:"
|
||||
Write-Host " 1. Verify 3 domains:"
|
||||
Write-Host " curl https://api.huypham.vn/health/live"
|
||||
Write-Host " curl -I https://admin.huypham.vn"
|
||||
Write-Host " curl -I https://user.huypham.vn"
|
||||
Write-Host " curl https://api.solutions.com.vn/health/live"
|
||||
Write-Host " curl -I https://admin.solutions.com.vn"
|
||||
Write-Host " curl -I https://eoffice.solutions.com.vn"
|
||||
Write-Host ""
|
||||
Write-Host " 2. Set remaining 2 Gitea secrets (if not done):"
|
||||
Write-Host " https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/settings/actions/secrets"
|
||||
|
||||
210
scripts/migrate-domains.ps1
Normal file
210
scripts/migrate-domains.ps1
Normal 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
|
||||
}
|
||||
@ -24,9 +24,9 @@ $PathTemplates = "$Root\api\wwwroot\templates"
|
||||
|
||||
$AppPoolApi = "SolutionErp-Api"
|
||||
|
||||
$DomainApi = "api.huypham.vn"
|
||||
$DomainAdmin = "admin.huypham.vn"
|
||||
$DomainUser = "user.huypham.vn"
|
||||
$DomainApi = "api.solutions.com.vn"
|
||||
$DomainAdmin = "admin.solutions.com.vn"
|
||||
$DomainUser = "eoffice.solutions.com.vn"
|
||||
|
||||
$SiteApi = "SolutionErp-Api"
|
||||
$SiteAdmin = "SolutionErp-Admin"
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
# Prereq:
|
||||
# - IIS sites created (run setup-iis-sites.ps1 first)
|
||||
# - Port 80 from Internet -> VPS open (Let's Encrypt HTTP-01 challenge)
|
||||
# - 3 domains api/admin/user.huypham.vn pointing DNS to VPS IP
|
||||
# - 3 domains api/admin/eoffice.solutions.com.vn pointing DNS to VPS IP
|
||||
#
|
||||
# Output:
|
||||
# - 3 cert in Windows Cert Store (LocalMachine\My)
|
||||
@ -37,9 +37,9 @@ if (-not (Test-Path $WacsExe)) {
|
||||
# ===================== 2. Check IIS sites exist =====================
|
||||
Import-Module WebAdministration
|
||||
$domains = @(
|
||||
@{ Site = "SolutionErp-Api"; HostName = "api.huypham.vn" },
|
||||
@{ Site = "SolutionErp-Admin"; HostName = "admin.huypham.vn" },
|
||||
@{ Site = "SolutionErp-User"; HostName = "user.huypham.vn" }
|
||||
@{ Site = "SolutionErp-Api"; HostName = "api.solutions.com.vn" },
|
||||
@{ Site = "SolutionErp-Admin"; HostName = "admin.solutions.com.vn" },
|
||||
@{ Site = "SolutionErp-User"; HostName = "eoffice.solutions.com.vn" }
|
||||
)
|
||||
|
||||
foreach ($d in $domains) {
|
||||
@ -100,5 +100,5 @@ if ($task) {
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "[OK] SSL setup DONE" -ForegroundColor Green
|
||||
Write-Host " Test: openssl s_client -connect api.huypham.vn:443 < /dev/null | openssl x509 -noout -subject -dates"
|
||||
Write-Host " Or browser: https://api.huypham.vn/health/live"
|
||||
Write-Host " Test: openssl s_client -connect api.solutions.com.vn:443 < /dev/null | openssl x509 -noout -subject -dates"
|
||||
Write-Host " Or browser: https://api.solutions.com.vn/health/live"
|
||||
|
||||
Reference in New Issue
Block a user