feat(gui): redesign kanholec GUI with modern interface and WiX v7 installer
golangci-lint / lint (push) Failing after 4s

- Complete GUI overhaul with Fyne framework
  * Modern dark theme with indigo/pink accent colors
  * 4-step setup wizard (Welcome → Server → Auth → Finish)
  * Dashboard with proxy list, real-time logs, and config viewer
  * Windows Service manager (install/start/stop/uninstall)
  * System tray integration with quick controls

- WiX v7 MSI installer
  * Auto-registers as Windows Service (manual start)
  * Feature tree UI for optional components
  * Desktop shortcut and PATH options
  * Build script: packaging/windows/build-msi.ps1

- Build system updates
  * Added kanholec-windows-gui target (CGO required)
  * Added kanholec-windows-msi target
  * Separate main_gui.go entry point for GUI builds

GUI binary: bin/kanholec-windows-amd64.exe (31.4 MB)
MSI installer: bin/kanholec-0.69.0-amd64.msi (12.1 MB)
This commit is contained in:
akukanara
2026-05-29 22:53:41 +07:00
Unverified
parent 2cd3052da1
commit f4a88f4b2c
13 changed files with 1962 additions and 222 deletions
+166
View File
@@ -0,0 +1,166 @@
<#
.SYNOPSIS
Build kanholec MSI installer using WiX Toolset
.DESCRIPTION
Builds the kanholec binary (with GUI support) and packages it into
an MSI installer using WiX v7.
.PARAMETER Arch
Target architecture: amd64 (default) or arm64
.PARAMETER SkipBuild
Skip building the Go binary (use existing one)
.PARAMETER OutputDir
Output directory for the MSI (default: bin/)
.EXAMPLE
.\build-msi.ps1
.\build-msi.ps1 -Arch arm64
.\build-msi.ps1 -SkipBuild
#>
param(
[ValidateSet("amd64", "arm64")]
[string]$Arch = "amd64",
[switch]$SkipBuild,
[string]$OutputDir = ""
)
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $PSCommandPath
$rootDir = Resolve-Path (Join-Path $scriptDir "..\..")
if ($OutputDir -eq "") {
$OutputDir = Join-Path $rootDir "bin"
}
$version = "0.69.0"
$binaryName = "kanholec-windows-$Arch.exe"
$binaryPath = Join-Path $OutputDir $binaryName
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " kanholec MSI Builder (WiX v7)" -ForegroundColor Cyan
Write-Host " Version: $version | Arch: $Arch" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Check WiX
try {
$wixVersion = & wix --version 2>&1
Write-Host " WiX: $wixVersion" -ForegroundColor Green
} catch {
Write-Host " ERROR: WiX Toolset not found. Install with:" -ForegroundColor Red
Write-Host " dotnet tool install --global wix" -ForegroundColor Yellow
exit 1
}
# Check Go
try {
$goVersion = & go version 2>&1
Write-Host " Go: $goVersion" -ForegroundColor Green
} catch {
Write-Host " ERROR: Go not found." -ForegroundColor Red
exit 1
}
# Build binary
if (-not $SkipBuild) {
Write-Host ""
Write-Host " Building kanholec (GUI + CGO)..." -ForegroundColor Yellow
$env:CGO_ENABLED = "1"
$env:GOOS = "windows"
$env:GOARCH = $Arch
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
$ldflags = "-s -w -H windowsgui"
$tags = "kanholec,kanholec_gui"
& go build -trimpath -ldflags $ldflags -tags $tags -o $binaryPath ./cmd/kanholec
if ($LASTEXITCODE -ne 0) {
Write-Host " ERROR: Go build failed" -ForegroundColor Red
exit 1
}
$env:CGO_ENABLED = ""
$env:GOOS = ""
$env:GOARCH = ""
Write-Host " Binary: $binaryPath" -ForegroundColor Green
} else {
if (-not (Test-Path $binaryPath)) {
Write-Host " ERROR: Binary not found at $binaryPath" -ForegroundColor Red
Write-Host " Build it first or remove -SkipBuild flag" -ForegroundColor Yellow
exit 1
}
Write-Host " Using existing binary: $binaryPath" -ForegroundColor Green
}
# Create icon if missing
$iconPath = Join-Path $scriptDir "kanholec.ico"
if (-not (Test-Path $iconPath)) {
Write-Host ""
Write-Host " Creating placeholder icon..." -ForegroundColor Yellow
# Create a minimal valid .ico file (16x16 32-bit)
$ico = New-Object byte[] 318
# ICO header
$ico[0] = 0; $ico[1] = 0 # reserved
$ico[2] = 1; $ico[3] = 0 # type: icon
$ico[4] = 1; $ico[5] = 0 # count: 1
# Directory entry
$ico[6] = 16 # width
$ico[7] = 16 # height
$ico[8] = 0 # colors
$ico[9] = 0 # reserved
$ico[10] = 1; $ico[11] = 0 # planes
$ico[12] = 32; $ico[13] = 0 # bpp
$ico[14] = 0; $ico[15] = 1; $ico[16] = 0; $ico[17] = 0 # size = 256
$ico[18] = 22; $ico[19] = 0; $ico[20] = 0; $ico[21] = 0 # offset = 22
# BMP header (40 bytes)
$ico[22] = 40; $ico[23] = 0; $ico[24] = 0; $ico[25] = 0 # header size
$ico[26] = 16; $ico[27] = 0; $ico[28] = 0; $ico[29] = 0 # width
$ico[30] = 32; $ico[31] = 0; $ico[32] = 0; $ico[33] = 0 # height (2x for icon)
$ico[34] = 1; $ico[35] = 0 # planes
$ico[36] = 32; $ico[37] = 0 # bpp
# Fill pixel data with blue-ish color (kanhole brand)
for ($i = 62; $i -lt 318; $i += 4) {
$ico[$i] = 236 # B
$ico[$i+1] = 152 # G
$ico[$i+2] = 56 # R
$ico[$i+3] = 255 # A
}
[System.IO.File]::WriteAllBytes($iconPath, $ico)
Write-Host " Icon: $iconPath (placeholder)" -ForegroundColor Green
}
# Build MSI
Write-Host ""
Write-Host " Building MSI with WiX..." -ForegroundColor Yellow
$wixprojPath = Join-Path $scriptDir "kanholec.wixproj"
$msiOutput = Join-Path $OutputDir "kanholec-$version-$Arch.msi"
Push-Location $rootDir
try {
& dotnet build $wixprojPath -c Release -p:Platform=x64 -o $OutputDir
if ($LASTEXITCODE -ne 0) {
Write-Host " ERROR: WiX build failed" -ForegroundColor Red
exit 1
}
$builtMsi = Get-ChildItem -Path $OutputDir -Filter "*.msi" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($builtMsi) {
if ($builtMsi.FullName -ne $msiOutput) {
Move-Item -Path $builtMsi.FullName -Destination $msiOutput -Force
}
Write-Host ""
Write-Host " MSI: $msiOutput" -ForegroundColor Green
Write-Host " Size: $([math]::Round((Get-Item $msiOutput).Length / 1KB, 1)) KB" -ForegroundColor Green
}
} finally {
Pop-Location
}
Write-Host ""
Write-Host " Done!" -ForegroundColor Cyan
Write-Host ""
Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

+13
View File
@@ -0,0 +1,13 @@
<Project Sdk="WixToolset.Sdk/7.0.0">
<PropertyGroup>
<InstallerPlatform>x64</InstallerPlatform>
<OutputName>kanholec-0.69.0</OutputName>
<OutputType>Package</OutputType>
<DefineConstants>Version=0.69.0</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="WixToolset.UI.wixext" Version="7.0.0" />
<PackageReference Include="WixToolset.Util.wixext" Version="7.0.0" />
</ItemGroup>
</Project>
+124 -39
View File
@@ -1,47 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v3/wxs">
<Product
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package
Name="kanholec"
Id="*"
Manufacturer="kanhole Contributors"
Version="0.69.0"
UpgradeCode="A1B2C3D4-E5F6-7890-ABCD-EF1234567890"
Language="1033"
Codepage="1252"
Version="0.62.0"
Manufacturer="kanhole Contributors">
Scope="perMachine">
<Package
InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
Description="kanhole Client"
Comments="kanholec is the client component of kanhole - a fast reverse proxy." />
<MajorUpgrade DowngradeErrorMessage="A newer version of kanholec is already installed." />
<Media Id="1" Cabinet="kanholec.cab" EmbedCab="yes" />
<MediaTemplate EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLDIR" Name="kanholec">
<Component Id="FrpcBinary" Guid="E1B2C3D4-E5F6-7890-ABCD-EF1234567894">
<File Id="FrpcExe" Name="kanholec.exe" DiskId="1" Source="bin/kanholec-windows-amd64.exe" KeyPath="yes" />
</Component>
</Directory>
</Directory>
<Icon Id="kanholecIcon" SourceFile="kanholec.ico" />
<Property Id="ARPPRODUCTICON" Value="kanholecIcon" />
<Property Id="ARPHELPLINK" Value="https://github.com/kanhole/kanhole" />
<Property Id="ARPURLINFOABOUT" Value="https://github.com/kanhole/kanhole" />
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="kanholec">
<Component Id="StartMenuShortcuts" Guid="A2B2C3D4-E5F6-7890-ABCD-EF1234567892">
<Shortcut Id="FrpcShortcut" Name="kanholec" Target="[INSTALLDIR]kanholec.exe" WorkingDirectory="INSTALLDIR" Description="kanhole client" />
<Shortcut Id="UninstallShortcut" Name="Uninstall kanholec" Target="[System64Folder]msiexec.exe" Arguments="/x [ProductCode]" />
<RemoveFolder Id="RemoveApplicationProgramsFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\kanhole\kanholec" Name="installed" Type="integer" Value="1" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="Main" Title="kanholec" Description="kanhole client binary" Level="1">
<ComponentRef Id="FrpcBinary" />
<ComponentRef Id="StartMenuShortcuts" />
<Feature Id="Main" Title="kanholec" Description="kanhole reverse proxy client" Level="1"
AllowAdvertise="no">
<ComponentGroupRef Id="BinaryFiles" />
<ComponentGroupRef Id="ConfigFiles" />
<ComponentGroupRef Id="Shortcuts" />
<ComponentRef Id="ServiceComponent" />
</Feature>
</Product>
<Feature Id="DesktopShortcutFeature" Title="Desktop Shortcut" Description="Create a desktop shortcut"
Level="1000" AllowAdvertise="no">
<ComponentRef Id="DesktopShortcutComponent" />
</Feature>
<Feature Id="PathFeature" Title="Add to PATH" Description="Add kanholec to system PATH"
Level="1" AllowAdvertise="no">
<ComponentRef Id="PathComponent" />
</Feature>
<ui:WixUI
Id="WixUI_FeatureTree"
InstallDirectory="INSTALLFOLDER" />
<WixVariable Id="WixUILicenseRtf" Value="license.rtf" />
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="kanholec" />
</StandardDirectory>
<StandardDirectory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="kanholec" />
</StandardDirectory>
<StandardDirectory Id="CommonAppDataFolder">
<Directory Id="ConfigFolder" Name="kanholec" />
</StandardDirectory>
<ComponentGroup Id="BinaryFiles" Directory="INSTALLFOLDER">
<Component Id="KanholeCBinary" Guid="E1B2C3D4-E5F6-7890-ABCD-EF1234567894">
<File Id="kanholecExe"
Name="kanholec.exe"
Source="..\..\bin\kanholec-windows-amd64.exe"
KeyPath="yes" />
</Component>
</ComponentGroup>
<ComponentGroup Id="ConfigFiles" Directory="ConfigFolder">
<Component Id="DefaultConfig" Guid="F1B2C3D4-E5F6-7890-ABCD-EF1234567895" NeverOverwrite="yes">
<File Id="kanholecToml"
Name="kanholec.toml"
Source="kanholec.default.toml"
KeyPath="yes" />
</Component>
</ComponentGroup>
<ComponentGroup Id="Shortcuts" Directory="ApplicationProgramsFolder">
<Component Id="StartMenuShortcuts" Guid="A2B2C3D4-E5F6-7890-ABCD-EF1234567892">
<Shortcut Id="StartMenuShortcut"
Name="kanholec"
Target="[INSTALLFOLDER]kanholec.exe"
WorkingDirectory="INSTALLFOLDER"
Description="kanhole reverse proxy client"
Icon="kanholecIcon" />
<Shortcut Id="ConfigFolderShortcut"
Name="Config Directory"
Target="[ConfigFolder]"
Description="Open kanholec config directory" />
<Shortcut Id="UninstallShortcut"
Name="Uninstall kanholec"
Target="[System64Folder]msiexec.exe"
Arguments="/x [ProductCode]"
Description="Uninstall kanholec" />
<RemoveFolder Id="RemoveApplicationProgramsFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\kanhole\kanholec" Name="installed" Type="integer" Value="1" KeyPath="yes" />
</Component>
</ComponentGroup>
<Component Id="DesktopShortcutComponent" Directory="DesktopFolder" Guid="B2B2C3D4-E5F6-7890-ABCD-EF1234567893">
<Shortcut Id="DesktopShortcut"
Name="kanholec"
Target="[INSTALLFOLDER]kanholec.exe"
WorkingDirectory="INSTALLFOLDER"
Description="kanhole reverse proxy client"
Icon="kanholecIcon" />
<RegistryValue Root="HKCU" Key="Software\kanhole\kanholec" Name="desktop_shortcut" Type="integer" Value="1" KeyPath="yes" />
</Component>
<Component Id="PathComponent" Directory="INSTALLFOLDER" Guid="C2B2C3D4-E5F6-7890-ABCD-EF1234567896">
<Environment Id="PATH" Name="PATH" Value="[INSTALLFOLDER]" Permanent="no" Part="last" Action="set" System="yes" />
<RegistryValue Root="HKCU" Key="Software\kanhole\kanholec" Name="path_added" Type="integer" Value="1" KeyPath="yes" />
</Component>
<Component Id="ServiceComponent" Directory="INSTALLFOLDER" Guid="D2B2C3D4-E5F6-7890-ABCD-EF1234567897">
<RegistryValue Root="HKCU" Key="Software\kanhole\kanholec" Name="service_registered" Type="integer" Value="1" KeyPath="yes" />
<ServiceInstall
Id="kanholecService"
Name="kanholec"
DisplayName="kanholec - kanhole Client"
Description="kanhole reverse proxy client service"
Start="demand"
Type="ownProcess"
ErrorControl="ignore"
Arguments='-c "[ConfigFolder]kanholec.toml"'
Account="LocalSystem" />
<ServiceControl
Id="kanholecServiceControl"
Name="kanholec"
Stop="both"
Remove="uninstall"
Wait="no" />
</Component>
</Package>
</Wix>