310 lines
11 KiB
PowerShell
310 lines
11 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
frpc Windows Setup Wizard (Interactive PowerShell Installer)
|
|
.DESCRIPTION
|
|
Interactive installer for frpc with EULA acceptance, directory selection,
|
|
and optional features (PATH, shortcuts, desktop icon).
|
|
.PARAMETER Unattended
|
|
Run in silent mode with defaults.
|
|
.PARAMETER InstallDir
|
|
Installation directory (default: $env:ProgramFiles\frpc).
|
|
.EXAMPLE
|
|
.\install.ps1
|
|
.\install.ps1 -Unattended
|
|
#>
|
|
|
|
param(
|
|
[switch]$Unattended,
|
|
[string]$InstallDir = "$env:ProgramFiles\frpc"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$host.UI.RawUI.WindowTitle = "frpc Setup Wizard"
|
|
$env:FRPC_VERSION = "0.62.0"
|
|
|
|
function Write-Banner {
|
|
Clear-Host
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host " frpc - frp Client v$env:FRPC_VERSION" -ForegroundColor Cyan
|
|
Write-Host " Setup Wizard" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
}
|
|
|
|
function Show-License {
|
|
param([string]$Path)
|
|
Write-Banner
|
|
Write-Host "License Agreement" -ForegroundColor Yellow
|
|
Write-Host "==================" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
if (Test-Path $Path) {
|
|
Get-Content $Path | ForEach-Object { Write-Host $_ }
|
|
} else {
|
|
Write-Host "frpc is licensed under the Apache License, Version 2.0."
|
|
Write-Host "See http://www.apache.org/licenses/LICENSE-2.0"
|
|
}
|
|
Write-Host ""
|
|
if (-not $Unattended) {
|
|
$choice = $host.UI.PromptForChoice(
|
|
"License Agreement",
|
|
"Do you accept the terms?",
|
|
[System.Management.Automation.Host.ChoiceDescription[]]@(
|
|
@{Label="&Agree"; Help="I accept the agreement"},
|
|
@{Label="&Decline"; Help="I do not accept the agreement"}
|
|
),
|
|
1
|
|
)
|
|
if ($choice -ne 0) {
|
|
Write-Host "Setup cancelled. You must accept the license to install." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
}
|
|
}
|
|
|
|
function Select-Directory {
|
|
param([string]$DefaultPath)
|
|
Write-Banner
|
|
Write-Host "Installation Directory" -ForegroundColor Yellow
|
|
Write-Host "======================" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host "Default: $DefaultPath" -ForegroundColor Gray
|
|
Write-Host ""
|
|
|
|
if (-not $Unattended) {
|
|
$input = Read-Host "Press Enter to accept, or type a custom path"
|
|
if ($input -ne "") {
|
|
return $input
|
|
}
|
|
}
|
|
return $DefaultPath
|
|
}
|
|
|
|
function Select-Components {
|
|
Write-Banner
|
|
Write-Host "Select Components" -ForegroundColor Yellow
|
|
Write-Host "=================" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
|
|
$components = @{
|
|
"Main" = @{Desc="frpc binary and config"; Default=$true}
|
|
"Path" = @{Desc="Add to system PATH"; Default=$true}
|
|
"StartMenu" = @{Desc="Start Menu shortcuts"; Default=$true}
|
|
"Desktop" = @{Desc="Desktop shortcut"; Default=$false}
|
|
}
|
|
|
|
$selected = @{}
|
|
$i = 1
|
|
$choices = @()
|
|
|
|
foreach ($key in $components.Keys) {
|
|
$c = $components[$key]
|
|
$default = if ($c.Default) { "Y" } else { "N" }
|
|
$displayDefault = if ($c.Default) { "Yes" } else { "No" }
|
|
Write-Host " $i. $($c.Desc) [$displayDefault]" -ForegroundColor Gray
|
|
$choices += @{Key=$key; Default=$default}
|
|
$i++
|
|
}
|
|
|
|
Write-Host ""
|
|
if (-not $Unattended) {
|
|
Write-Host "Press Enter for defaults, or type numbers to toggle (e.g. 1,3):" -ForegroundColor Yellow
|
|
$input = Read-Host
|
|
if ($input -ne "") {
|
|
$toggleIndices = $input -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
|
|
for ($j = 0; $j -lt $choices.Count; $j++) {
|
|
$idx = $j + 1
|
|
$isToggled = [string]$idx -in $toggleIndices
|
|
$c = $components[$choices[$j].Key]
|
|
$default = $c.Default
|
|
$selected[$choices[$j].Key] = if ($isToggled) { -not $default } else { $default }
|
|
}
|
|
} else {
|
|
foreach ($ch in $choices) { $selected[$ch.Key] = $components[$ch.Key].Default }
|
|
}
|
|
} else {
|
|
foreach ($ch in $choices) { $selected[$ch.Key] = $components[$ch.Key].Default }
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Selected components:" -ForegroundColor Green
|
|
foreach ($key in $selected.Keys) {
|
|
$mark = if ($selected[$key]) { "[X]" } else { "[ ]" }
|
|
Write-Host " $mark $($components[$key].Desc)" -ForegroundColor $(if ($selected[$key]) { "Green" } else { "DarkGray" })
|
|
}
|
|
Start-Sleep 1
|
|
|
|
return $selected
|
|
}
|
|
|
|
function Install-Frpc {
|
|
param(
|
|
[string]$InstallDir,
|
|
[hashtable]$Components,
|
|
[string]$BinarySource
|
|
)
|
|
|
|
Write-Banner
|
|
Write-Host "Installing frpc..." -ForegroundColor Yellow
|
|
Write-Host "==================" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
|
|
# Create directories
|
|
Write-Host " Creating directories..." -NoNewline
|
|
New-Item -Path $InstallDir -ItemType Directory -Force | Out-Null
|
|
$configDir = "$env:ProgramData\frpc"
|
|
New-Item -Path $configDir -ItemType Directory -Force | Out-Null
|
|
Write-Host " OK" -ForegroundColor Green
|
|
|
|
# Copy binary
|
|
Write-Host " Copying frpc.exe..." -NoNewline
|
|
if ($BinarySource -and (Test-Path $BinarySource)) {
|
|
Copy-Item -Path $BinarySource -Destination "$InstallDir\frpc.exe" -Force
|
|
} else {
|
|
# Download from GitHub
|
|
$url = "https://github.com/fatedier/frp/releases/download/v$env:FRPC_VERSION/frp_${env:FRPC_VERSION}_windows_amd64.zip"
|
|
$zipPath = "$env:TEMP\frpc.zip"
|
|
Write-Host ""
|
|
Write-Host " Downloading from GitHub..." -NoNewline
|
|
Invoke-WebRequest -Uri $url -OutFile $zipPath -UseBasicParsing
|
|
Expand-Archive -Path $zipPath -DestinationPath "$env:TEMP\frpc" -Force
|
|
$exePath = Get-ChildItem -Path "$env:TEMP\frpc" -Recurse -Filter "frpc.exe" | Select-Object -First 1 -ExpandProperty FullName
|
|
if (-not $exePath) { throw "frpc.exe not found in archive" }
|
|
Copy-Item -Path $exePath -Destination "$InstallDir\frpc.exe" -Force
|
|
}
|
|
Write-Host " OK" -ForegroundColor Green
|
|
|
|
# Config file
|
|
Write-Host " Creating config..." -NoNewline
|
|
$configFile = "$configDir\frpc.toml"
|
|
if (-not (Test-Path $configFile)) {
|
|
@"
|
|
# frpc configuration
|
|
# Edit this file to configure your frp client.
|
|
|
|
serverAddr = "127.0.0.1"
|
|
serverPort = 7000
|
|
auth.token = ""
|
|
"@ | Out-File -FilePath $configFile -Encoding utf8
|
|
}
|
|
Write-Host " OK" -ForegroundColor Green
|
|
|
|
# PATH
|
|
if ($Components["Path"]) {
|
|
Write-Host " Adding to PATH..." -NoNewline
|
|
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "Machine")
|
|
if ($currentPath -notlike "*$InstallDir*") {
|
|
$newPath = "$currentPath;$InstallDir"
|
|
[Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine")
|
|
$env:PATH = "$env:PATH;$InstallDir"
|
|
}
|
|
Write-Host " OK" -ForegroundColor Green
|
|
}
|
|
|
|
# Start Menu
|
|
if ($Components["StartMenu"]) {
|
|
Write-Host " Creating Start Menu shortcuts..." -NoNewline
|
|
$startMenu = "$([Environment]::GetFolderPath('CommonStartMenu'))\Programs\frpc"
|
|
New-Item -Path $startMenu -ItemType Directory -Force | Out-Null
|
|
$wshell = New-Object -ComObject WScript.Shell
|
|
$shortcut = $wshell.CreateShortcut("$startMenu\frpc.lnk")
|
|
$shortcut.TargetPath = "$InstallDir\frpc.exe"
|
|
$shortcut.Save()
|
|
$unlink = $wshell.CreateShortcut("$startMenu\Uninstall frpc.lnk")
|
|
$unlink.TargetPath = "$InstallDir\uninstall.ps1"
|
|
$unlink.Save()
|
|
Write-Host " OK" -ForegroundColor Green
|
|
}
|
|
|
|
# Desktop
|
|
if ($Components["Desktop"]) {
|
|
Write-Host " Creating desktop shortcut..." -NoNewline
|
|
$desktop = [Environment]::GetFolderPath('Desktop')
|
|
$wshell = New-Object -ComObject WScript.Shell
|
|
$shortcut = $wshell.CreateShortcut("$desktop\frpc.lnk")
|
|
$shortcut.TargetPath = "$InstallDir\frpc.exe"
|
|
$shortcut.Save()
|
|
Write-Host " OK" -ForegroundColor Green
|
|
}
|
|
|
|
# Write uninstall script
|
|
Write-Host " Creating uninstaller..." -NoNewline
|
|
@"
|
|
param([switch]`$Silent)
|
|
`$InstallDir = "$InstallDir"
|
|
if (-not `$Silent) {
|
|
`$choice = `$host.UI.PromptForChoice("Uninstall frpc", "Remove frpc from this computer?", @(@{Label="&Yes"; Help=""}, @{Label="&No"; Help=""}), 1)
|
|
if (`$choice -ne 0) { exit }
|
|
}
|
|
# Remove files
|
|
Remove-Item -Path "`$InstallDir\frpc.exe" -Force -ErrorAction SilentlyContinue
|
|
Remove-Item -Path "`$InstallDir\uninstall.ps1" -Force -ErrorAction SilentlyContinue
|
|
Remove-Item -Path "`$InstallDir" -Force -ErrorAction SilentlyContinue
|
|
# Remove shortcuts
|
|
Remove-Item -Path "$([Environment]::GetFolderPath('CommonStartMenu'))\Programs\frpc" -Recurse -Force -ErrorAction SilentlyContinue
|
|
Remove-Item -Path "$([Environment]::GetFolderPath('Desktop'))\frpc.lnk" -Force -ErrorAction SilentlyContinue
|
|
Write-Host "frpc has been uninstalled."
|
|
"@ | Out-File -FilePath "$InstallDir\uninstall.ps1" -Encoding utf8
|
|
Write-Host " OK" -ForegroundColor Green
|
|
|
|
Write-Host ""
|
|
Write-Host "Installation complete!" -ForegroundColor Green
|
|
Write-Host ""
|
|
Write-Host "Quick start:" -ForegroundColor Yellow
|
|
Write-Host " frpc --help"
|
|
Write-Host " frpc --server-config http://your-server:7500/admin/api/frpc/proxy-config/YOUR_KEY"
|
|
Write-Host " frpc -c $configFile"
|
|
Write-Host ""
|
|
Write-Host "Auth with provisioning token:" -ForegroundColor Yellow
|
|
Write-Host " frpc auth login --server http://your-server:7500 --client-name myclient"
|
|
Write-Host ""
|
|
}
|
|
|
|
# ===== Main =====
|
|
|
|
$scriptDir = Split-Path -Parent $PSCommandPath
|
|
$binarySource = Join-Path $scriptDir "..\bin\frpc-windows-amd64.exe"
|
|
if (-not (Test-Path $binarySource)) {
|
|
$binarySource = Join-Path $scriptDir "frpc-windows-amd64.exe"
|
|
}
|
|
if (-not (Test-Path $binarySource)) {
|
|
$binarySource = $null
|
|
}
|
|
|
|
# Check admin rights
|
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
if (-not $isAdmin) {
|
|
Write-Host "This installer requires administrator privileges." -ForegroundColor Red
|
|
Write-Host "Please run PowerShell as Administrator and try again." -ForegroundColor Yellow
|
|
if (-not $Unattended) {
|
|
Start-Sleep 2
|
|
# Self-elevate
|
|
$script = "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`""
|
|
Start-Process powershell -Verb RunAs -ArgumentList $script
|
|
}
|
|
exit 1
|
|
}
|
|
|
|
if ($Unattended) {
|
|
Install-Frpc -InstallDir $InstallDir -Components @{
|
|
Main=$true; Path=$true; StartMenu=$true; Desktop=$false
|
|
} -BinarySource $binarySource
|
|
} else {
|
|
Show-License -Path (Join-Path $scriptDir "license.txt")
|
|
$dir = Select-Directory -DefaultPath $InstallDir
|
|
$components = Select-Components
|
|
Install-Frpc -InstallDir $dir -Components $components -BinarySource $binarySource
|
|
Write-Host ""
|
|
$runNow = $host.UI.PromptForChoice(
|
|
"Setup Complete",
|
|
"Run frpc now?",
|
|
[System.Management.Automation.Host.ChoiceDescription[]]@(
|
|
@{Label="&Yes"; Help="Run frpc"},
|
|
@{Label="&No"; Help="Close"}
|
|
),
|
|
0
|
|
)
|
|
if ($runNow -eq 0) {
|
|
Start-Process "$dir\frpc.exe"
|
|
}
|
|
}
|