| 1 | $ErrorActionPreference = "Stop" |
| 2 | $BaseUrl = if ($env:BASE_URL) { $env:BASE_URL } else { "https://github.com/gosuda/portal-tunnel/releases/latest/download" } |
| 3 | $RelayUrl = if ($env:RELAY_URL) { $env:RELAY_URL } else { "https://your-relay.example.com" } |
| 4 | $OriginalSecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol |
| 5 | [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 |
| 6 | $WorkDir = $null |
| 7 | try { |
| 8 | $Arch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } |
| 9 | if ($Arch -eq "ARM64") { |
| 10 | $PortalArch = "arm64" |
| 11 | } elseif ($Arch -eq "AMD64" -or $Arch -eq "x86_64") { |
| 12 | $PortalArch = "amd64" |
| 13 | } else { |
| 14 | throw "Unsupported architecture: $Arch" |
| 15 | } |
| 16 | |
| 17 | $BinPathPrefix = if ($env:BIN_PATH_PREFIX) { $env:BIN_PATH_PREFIX.Trim() } else { "" } |
| 18 | if ($env:BIN_URL) { |
| 19 | $BinUrl = $env:BIN_URL |
| 20 | } elseif ([string]::IsNullOrWhiteSpace($BinPathPrefix)) { |
| 21 | $BinUrl = "$BaseUrl/portal-windows-$PortalArch.exe" |
| 22 | } else { |
| 23 | $BinUrl = "$BaseUrl/$BinPathPrefix/windows-$PortalArch" |
| 24 | } |
| 25 | $ChecksumUrl = if ($env:CHECKSUM_URL) { $env:CHECKSUM_URL } else { "$BinUrl.sha256" } |
| 26 | $WorkDir = Join-Path $env:TEMP ("portal-install-" + [Guid]::NewGuid().ToString()) |
| 27 | New-Item -ItemType Directory -Force -Path $WorkDir | Out-Null |
| 28 | $BinPath = Join-Path $WorkDir "portal.exe" |
| 29 | |
| 30 | Write-Host "Downloading portal (windows/$PortalArch)..." |
| 31 | Invoke-WebRequest -UseBasicParsing -Uri $BinUrl -OutFile $BinPath |
| 32 | |
| 33 | Write-Host "Verifying SHA256 checksum..." |
| 34 | $ChecksumResponse = Invoke-WebRequest -UseBasicParsing -Uri $ChecksumUrl |
| 35 | if ($ChecksumResponse.Content -is [byte[]]) { |
| 36 | $ChecksumPayload = [System.Text.Encoding]::UTF8.GetString($ChecksumResponse.Content) |
| 37 | } else { |
| 38 | $ChecksumPayload = [string]$ChecksumResponse.Content |
| 39 | } |
| 40 | $ChecksumMatch = [regex]::Match($ChecksumPayload, '([A-Fa-f0-9]{64})') |
| 41 | if (-not $ChecksumMatch.Success) { |
| 42 | throw "Invalid checksum payload from $ChecksumUrl. Expected '<sha256> <filename>'." |
| 43 | } |
| 44 | |
| 45 | $ExpectedHash = $ChecksumMatch.Groups[1].Value.ToLowerInvariant() |
| 46 | $ActualHash = (Get-FileHash -Algorithm SHA256 -Path $BinPath).Hash.ToLowerInvariant() |
| 47 | if ($ActualHash -ne $ExpectedHash) { |
| 48 | throw "Checksum mismatch for portal binary." |
| 49 | } |
| 50 | |
| 51 | $InstallDir = Join-Path $env:LOCALAPPDATA "portal\bin" |
| 52 | New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null |
| 53 | $InstallPath = Join-Path $InstallDir "portal.exe" |
| 54 | Copy-Item -Force $BinPath $InstallPath |
| 55 | $UserPath = [Environment]::GetEnvironmentVariable("Path", "User") |
| 56 | $UserEntries = @() |
| 57 | if (-not [string]::IsNullOrWhiteSpace($UserPath)) { |
| 58 | $UserEntries = @($UserPath -split ';' | Where-Object { $_ -ne "" }) |
| 59 | } |
| 60 | if (-not ($UserEntries -contains $InstallDir)) { |
| 61 | $NewUserPath = if ([string]::IsNullOrWhiteSpace($UserPath)) { |
| 62 | $InstallDir |
| 63 | } else { |
| 64 | "$InstallDir;$UserPath" |
| 65 | } |
| 66 | [Environment]::SetEnvironmentVariable("Path", $NewUserPath, "User") |
| 67 | } |
| 68 | |
| 69 | $SessionEntries = @($env:Path -split ';' | Where-Object { $_ -ne "" }) |
| 70 | if (-not ($SessionEntries -contains $InstallDir)) { |
| 71 | $env:Path = "$InstallDir;$env:Path" |
| 72 | } |
| 73 | |
| 74 | Write-Host "Installed portal to $InstallPath" |
| 75 | Write-Host "Next step:" |
| 76 | Write-Host " portal expose 3000 --relays $RelayUrl" |
| 77 | } finally { |
| 78 | [System.Net.ServicePointManager]::SecurityProtocol = $OriginalSecurityProtocol |
| 79 | if ($WorkDir -and (Test-Path $WorkDir)) { |
| 80 | Remove-Item -Recurse -Force $WorkDir |
| 81 | } |
| 82 | } |