From 1b7d8872ab7829966dab1ba3af7fd263a2c2661c Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Tue, 21 Apr 2026 13:49:55 +0700 Subject: [PATCH] [CLAUDE] Scripts: deploy-all.ps1 one-command gop 4 step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pre-check prerequisites (admin, sqlcmd, WebAdministration, .NET 10) - Chay theo thu tu SQL → IIS → appsettings.Production.json (auto write tu template + ACL restrict) → SSL (voi confirm) → Runner - Idempotent: chay lai khong pha - -SkipSsl / -SkipRunner flag cho debug - Summary voi next steps Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/deploy-all.ps1 | 167 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 scripts/deploy-all.ps1 diff --git a/scripts/deploy-all.ps1 b/scripts/deploy-all.ps1 new file mode 100644 index 0000000..db3fc3a --- /dev/null +++ b/scripts/deploy-all.ps1 @@ -0,0 +1,167 @@ +# Deploy SOLUTION_ERP all-in-one trên VPS Windows Server. +# Chạy 4 step: SQL → IIS → SSL → Runner. Idempotent. Stop ngay khi step fail. +# +# Usage: +# .\deploy-all.ps1 ` +# -SaPassword '' ` +# -JwtSecret '<64-char hex>' ` +# -VrappPassword '' ` +# -RunnerToken '' ` +# -AdminEmail 'admin@huypham.vn' +# +# Prereq (pre-check script sẽ verify): +# - Windows Server + Admin PowerShell +# - IIS + URL Rewrite installed +# - SQL Server với login 'sa' + 'vrapp' tồn tại +# - .NET 10 Hosting Bundle installed +# - Port 80+443 firewall open +# - DNS api/admin/user.huypham.vn đã trỏ VPS + +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)] [string]$SaPassword, + [Parameter(Mandatory=$true)] [string]$JwtSecret, + [Parameter(Mandatory=$true)] [string]$VrappPassword, + [Parameter(Mandatory=$true)] [string]$RunnerToken, + [string]$AdminEmail = "admin@huypham.vn", + [switch]$SkipSsl, + [switch]$SkipRunner +) + +$ErrorActionPreference = 'Stop' +$ScriptRoot = $PSScriptRoot +$StartTime = Get-Date + +function Write-Banner($text) { + Write-Host "" + Write-Host ("=" * 70) -ForegroundColor Cyan + Write-Host " $text" -ForegroundColor Cyan + Write-Host ("=" * 70) -ForegroundColor Cyan +} + +function Test-Prereq { + $issues = @() + + if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + $issues += "Không chạy với Admin privilege" + } + + if (-not (Get-Command sqlcmd -ErrorAction SilentlyContinue)) { + $issues += "sqlcmd không có trong PATH (SQL Server Client tools)" + } + + if (-not (Get-Module -ListAvailable -Name WebAdministration)) { + $issues += "IIS WebAdministration module không cài (Install-WindowsFeature Web-Server,Web-Scripting-Tools)" + } + + $dotnet10 = & dotnet --list-runtimes 2>$null | Select-String "Microsoft.AspNetCore.App 10" + if (-not $dotnet10) { + $issues += ".NET 10 Hosting Bundle chưa cài (https://dotnet.microsoft.com/download/dotnet/10.0)" + } + + return $issues +} + +Write-Banner "Pre-check prerequisites" +$issues = Test-Prereq +if ($issues.Count -gt 0) { + Write-Host "❌ Các vấn đề:" -ForegroundColor Red + $issues | ForEach-Object { Write-Host " - $_" -ForegroundColor Red } + Write-Host "" + Write-Host "Fix các vấn đề trên rồi chạy lại." -ForegroundColor Yellow + exit 1 +} +Write-Host "✅ Tất cả prerequisite OK" + +# ===================== Step 1: SQL DB ===================== +Write-Banner "Step 1/4: SQL Database setup" +try { + & "$ScriptRoot\setup-sql-db.ps1" -SaPassword $SaPassword + if ($LASTEXITCODE -ne 0) { throw "setup-sql-db.ps1 exit $LASTEXITCODE" } +} catch { + Write-Host "❌ Step 1 FAIL: $_" -ForegroundColor Red + exit 1 +} + +# ===================== Step 2: IIS sites ===================== +Write-Banner "Step 2/4: IIS sites + app pool" +try { + & "$ScriptRoot\setup-iis-sites.ps1" + if ($LASTEXITCODE -ne 0) { throw "setup-iis-sites.ps1 exit $LASTEXITCODE" } +} catch { + Write-Host "❌ Step 2 FAIL: $_" -ForegroundColor Red + exit 1 +} + +# ===================== Step 3: appsettings.Production.json ===================== +Write-Banner "Step 3/4: Write appsettings.Production.json" +$apiPath = "C:\inetpub\solution-erp\api" +$example = "$apiPath\appsettings.Production.json.example" +$prod = "$apiPath\appsettings.Production.json" + +if (Test-Path $example) { + $content = Get-Content $example -Raw + $content = $content -replace '__SET_VIA_SECRETS__', [regex]::Escape($VrappPassword).Replace('\\','\') + $content = $content -replace '__SET_VIA_USER_SECRETS_OR_ENV__minimum_64_chars_random', [regex]::Escape($JwtSecret).Replace('\\','\') + # Unescape khi json serialization — vì json có escape char riêng, replace simple: + $content = (Get-Content $example -Raw) ` + -replace [regex]::Escape('__SET_VIA_SECRETS__'), $VrappPassword ` + -replace [regex]::Escape('__SET_VIA_USER_SECRETS_OR_ENV__minimum_64_chars_random'), $JwtSecret + Set-Content -Path $prod -Value $content -Encoding UTF8 + Write-Host "✅ Wrote $prod" + # ACL: chỉ Administrators + app pool identity đọc + icacls $prod /inheritance:r | Out-Null + icacls $prod /grant:r 'Administrators:(R,W)' 'IIS AppPool\SolutionErp-Api:(R)' | Out-Null + Write-Host " ACL restricted (Admins RW + AppPool R only)" +} else { + Write-Warning "Template không tồn tại tại $example — skip (maybe deploy chưa chạy, cần Gitea Actions deploy trước)" + Write-Warning "Sau khi CI deploy xong, rerun step này hoặc copy thủ công." +} + +# ===================== Step 4: SSL ===================== +if (-not $SkipSsl) { + Write-Banner "Step 4/4 (SSL): win-acme Let's Encrypt" + Write-Host "⚠️ Port 80 phải reachable từ Internet (Let's Encrypt HTTP-01)" + Write-Host " Test từ máy ngoài: curl http://api.huypham.vn" + $confirm = Read-Host "Tiếp tục? (y/N)" + if ($confirm -eq 'y') { + try { + & "$ScriptRoot\setup-ssl.ps1" + } catch { + Write-Warning "SSL fail: $_ — có thể fix sau + rerun setup-ssl.ps1" + } + } else { + Write-Host "⏭ Skip SSL — chạy setup-ssl.ps1 thủ công sau" + } +} else { + Write-Host "⏭ Skip SSL (-SkipSsl flag)" +} + +# ===================== Step 5: Runner ===================== +if (-not $SkipRunner -and $RunnerToken -ne "SKIP") { + Write-Banner "Step 5 (Runner): Gitea Actions runner" + try { + & "$ScriptRoot\setup-gitea-runner.ps1" -RegistrationToken $RunnerToken + } catch { + Write-Warning "Runner setup fail: $_ — chạy setup-gitea-runner.ps1 thủ công sau" + } +} + +# ===================== Summary ===================== +$duration = (Get-Date) - $StartTime +Write-Banner "✅ DEPLOY ALL DONE — $([int]$duration.TotalMinutes)m $([int]($duration.TotalSeconds % 60))s" +Write-Host "" +Write-Host "Next:" +Write-Host " 1. Verify 3 domain:" +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 "" +Write-Host " 2. Set 2 Gitea secrets còn thiếu (nếu chưa):" +Write-Host " https://git.baocaogiaoduc.vn/vietreport-admin/solution-erp/settings/actions/secrets" +Write-Host " - JWT_SECRET (update với 64-char)" +Write-Host " - IIS_PASSWORD (Windows admin password)" +Write-Host "" +Write-Host " 3. Trigger deploy: push commit main → Gitea Actions pick up → workflow chạy" +Write-Host "" +Write-Host " 4. Đổi admin password mặc định sau login (security-checklist.md)"