[CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)] [string]$BackupPath, [string]$ApplicationPoolName = 'XiaoshuLegacySync', [string]$PhysicalPath = 'C:\inetpub\xiaoshu-sync', [Parameter(Mandatory = $true)] [string]$HealthUrl ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest function Assert-Administrator { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = [Security.Principal.WindowsPrincipal]::new($identity) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw '请使用“以管理员身份运行”的 PowerShell 执行回滚。' } } Assert-Administrator Import-Module WebAdministration $resolvedBackupPath = (Resolve-Path -LiteralPath $BackupPath).Path if (-not (Test-Path -LiteralPath (Join-Path $resolvedBackupPath 'Xiaoshu.LegacySyncBridge.dll'))) { throw '回滚目录不是有效的同步桥备份:缺少 Xiaoshu.LegacySyncBridge.dll。' } if (-not (Test-Path "IIS:\AppPools\$ApplicationPoolName")) { throw "IIS 应用池不存在:$ApplicationPoolName" } $timestamp = Get-Date -Format 'yyyyMMdd-HHmmss' $failedPath = "$PhysicalPath.failed-$timestamp" if ($PSCmdlet.ShouldProcess($PhysicalPath, "从 $resolvedBackupPath 回滚小树旧系统同步桥")) { Stop-WebAppPool -Name $ApplicationPoolName -ErrorAction SilentlyContinue if (Test-Path -LiteralPath $PhysicalPath) { Copy-Item -LiteralPath $PhysicalPath -Destination $failedPath -Recurse -Force Remove-Item -LiteralPath $PhysicalPath -Recurse -Force } New-Item -ItemType Directory -Path $PhysicalPath -Force | Out-Null Copy-Item -Path (Join-Path $resolvedBackupPath '*') -Destination $PhysicalPath -Recurse -Force & icacls.exe $PhysicalPath /inheritance:r /grant:r "SYSTEM:(OI)(CI)F" "Administrators:(OI)(CI)F" "IIS AppPool\${ApplicationPoolName}:(OI)(CI)RX" /T /C | Out-Null Start-WebAppPool -Name $ApplicationPoolName $healthy = $false foreach ($attempt in 1..12) { try { $health = Invoke-RestMethod -Uri $HealthUrl -Method Get -TimeoutSec 10 if ($health.status -eq 'ok' -and $health.service -eq 'xiaoshu-legacy-sync' -and $health.version -eq 'v1') { $healthy = $true; break } } catch { Start-Sleep -Seconds 2 } } if (-not $healthy) { throw "文件已回滚,但健康检查没有通过:$HealthUrl。当前失败版本保存在 $failedPath。" } Write-Host "同步桥回滚完成:$resolvedBackupPath" Write-Host "回滚前版本保留在:$failedPath" }