[CLAUDE] Scripts: deploy-all.ps1 one-command gop 4 step
- 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) <noreply@anthropic.com>
This commit is contained in:
167
scripts/deploy-all.ps1
Normal file
167
scripts/deploy-all.ps1
Normal file
@ -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 '<SA pw>' `
|
||||
# -JwtSecret '<64-char hex>' `
|
||||
# -VrappPassword '<vrapp pw>' `
|
||||
# -RunnerToken '<gitea runner registration token>' `
|
||||
# -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)"
|
||||
Reference in New Issue
Block a user