param( [Parameter(Mandatory = $true)][string]$Root, [ValidateRange(1, 4)][int]$Level = 1 ) $ErrorActionPreference = 'Continue' $Root = $Root.TrimEnd('\') $configJson = Join-Path $Root 'cloudflare\tunnel-config.json' $installBat = Join-Path $Root 'cloudflare\install-tunnel.bat' $writer = Join-Path $Root 'cloudflare\write-tunnel-config.ps1' $cfDir = Join-Path $env:ProgramData 'AetherForge\.cloudflared' $cfConfig = Join-Path $cfDir 'config.yml' $cfExe = Join-Path $env:ProgramData 'AetherForge\bin\cloudflared.exe' function Write-RepairLog([string]$Message) { $line = "{0} [CF-HEAL-L{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message Write-Output $line } function Get-ServiceRunning { $q = sc.exe query cloudflared 2>&1 | Out-String return ($q -match 'STATE\s+:\s+\d+\s+RUNNING') } function Start-CloudflaredService { $null = net.exe start cloudflared 2>&1 Start-Sleep -Seconds 3 return (Get-ServiceRunning) } function Stop-CloudflaredService { $null = net.exe stop cloudflared 2>&1 Start-Sleep -Seconds 2 } function Restart-CloudflaredService { Stop-CloudflaredService return (Start-CloudflaredService) } Write-RepairLog "Repair level $Level starting..." switch ($Level) { 1 { Write-RepairLog "Action: net start cloudflared" if (Start-CloudflaredService) { Write-RepairLog "Service is RUNNING" exit 0 } Write-RepairLog "net start failed" exit 1 } 2 { Write-RepairLog "Action: restart cloudflared service" if (Restart-CloudflaredService) { Write-RepairLog "Service restarted OK" exit 0 } Write-RepairLog "Restart failed" exit 1 } 3 { Write-RepairLog "Action: rewrite config.yml + restart + DNS route" if (-not (Test-Path $configJson)) { Write-RepairLog "Missing tunnel-config.json" exit 1 } if (-not (Test-Path $writer)) { Write-RepairLog "Missing write-tunnel-config.ps1" exit 1 } $tunnelId = $null $hostname = $null & $writer -ConfigJson $configJson -CloudflaredDir $cfDir | ForEach-Object { if ($_ -match '^TUNNEL_ID=(.+)$') { $tunnelId = $Matches[1] } if ($_ -match '^HOSTNAME=(.+)$') { $hostname = $Matches[1] } Write-RepairLog $_ } if (-not (Test-Path $cfConfig)) { Write-RepairLog "config.yml write failed" exit 1 } Restart-CloudflaredService | Out-Null if ($cfExe -and (Test-Path $cfExe) -and $tunnelId -and $hostname) { Write-RepairLog "Action: tunnel route dns $hostname" & $cfExe --config $cfConfig tunnel route dns $tunnelId $hostname 2>&1 | ForEach-Object { Write-RepairLog $_ } } if (Get-ServiceRunning) { Write-RepairLog "Config refresh + service RUNNING" exit 0 } Write-RepairLog "Still not RUNNING after config refresh" exit 1 } 4 { Write-RepairLog "Action: full tunnel reinstall (install-tunnel.bat)" if (-not (Test-Path $installBat)) { Write-RepairLog "Missing install-tunnel.bat" exit 1 } $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ) if ($isAdmin) { & cmd.exe /c "`"$installBat`" `"$Root`"" exit $LASTEXITCODE } Write-RepairLog "Requesting elevated reinstall (UAC)..." $p = Start-Process -FilePath $installBat -ArgumentList "`"$Root`"" -Verb RunAs -Wait -PassThru exit $p.ExitCode } } Write-RepairLog "Unknown level" exit 1