feat: ent ORM, admin UI, client auth, Fyne GUI, Windows/MSI packaging

This commit is contained in:
kannn
2026-05-29 08:58:22 +00:00
Unverified
parent 8563a5fc74
commit a0a42a4966
81 changed files with 17144 additions and 89 deletions
+309
View File
@@ -0,0 +1,309 @@
<#
.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"
}
}