From b7c621f294f0f4012d75b66ce81d16935e9a6119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=9C=EA=B3=A8=EC=95=BD=EC=82=AC?= Date: Thu, 11 Sep 2025 01:00:28 +0900 Subject: [PATCH] Fix PowerShell encoding issues: Add English-only version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add farmq-install-en.ps1 with UTF-8 encoding support - Remove Korean characters to prevent PowerShell encoding issues - Use ASCII-only status indicators ([*], [+], [!]) - Maintain all functionality while ensuring compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- farmq-install-en.ps1 | 481 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 farmq-install-en.ps1 diff --git a/farmq-install-en.ps1 b/farmq-install-en.ps1 new file mode 100644 index 0000000..f2545dc --- /dev/null +++ b/farmq-install-en.ps1 @@ -0,0 +1,481 @@ +# FARMQ Headscale Windows One-Click Installation Script +# Usage: Run in Administrator PowerShell +# iex ((New-Object System.Net.WebClient).DownloadString('https://git.0bin.in/.../farmq-install-en.ps1')) + +param( + [switch]$Force, + [string]$HeadscaleServer = "https://head.0bin.in", + [string]$PreAuthKey = "8b3df41d37cb158ea39f41fc32c9af46e761de817ad06038", + [string]$FarmqNetwork = "100.64.0.0/10" +) + +# Set console to support Unicode characters +$PSDefaultParameterValues['*:Encoding'] = 'utf8' +if ($PSVersionTable.PSVersion.Major -ge 6) { + $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8' +} + +# ================================ +# Color Output Functions +# ================================ +function Write-Header { + param([string]$Text) + Write-Host "" + Write-Host "============================================" -ForegroundColor Magenta + Write-Host $Text -ForegroundColor White + Write-Host "============================================" -ForegroundColor Magenta + Write-Host "" +} + +function Write-Status { + param([string]$Message) + Write-Host "" + Write-Host "[*] $Message" -ForegroundColor Blue +} + +function Write-Success { + param([string]$Message) + Write-Host "" + Write-Host "[+] $Message" -ForegroundColor Green +} + +function Write-Error { + param([string]$Message) + Write-Host "" + Write-Host "[!] ERROR: $Message" -ForegroundColor Red +} + +function Write-Warning { + param([string]$Message) + Write-Host "" + Write-Host "[!] WARNING: $Message" -ForegroundColor Yellow +} + +function Write-Info { + param([string]$Message) + Write-Host "" + Write-Host "[i] $Message" -ForegroundColor Cyan +} + +# ================================ +# System Requirements Check +# ================================ +function Test-Requirements { + Write-Status "Checking system requirements..." + + # Check administrator privileges + $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal($currentUser) + + if (-NOT $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Error "This script requires administrator privileges." + Write-Info "Please restart using one of these methods:" + Write-Info "1. Windows Key + X -> 'Windows PowerShell (Admin)'" + Write-Info "2. Right-click PowerShell -> 'Run as Administrator'" + Write-Host "" + Read-Host "Press any key to exit..." + exit 1 + } + + # Check Windows version + $osVersion = [System.Environment]::OSVersion.Version + if ($osVersion.Major -lt 10) { + Write-Warning "Windows 10 or later recommended. Current: Windows $($osVersion.Major).$($osVersion.Minor)" + } + + # Check internet connection + try { + Test-Connection "8.8.8.8" -Count 1 -Quiet | Out-Null + } + catch { + Write-Warning "Please check your internet connection." + } + + Write-Success "System requirements check completed" +} + +# ================================ +# Install Tailscale +# ================================ +function Install-Tailscale { + Write-Status "Checking Tailscale installation..." + + # Check existing installation + $tailscalePath = Get-Command "tailscale" -ErrorAction SilentlyContinue + if ($tailscalePath) { + $version = & tailscale version 2>$null | Select-Object -First 1 + Write-Info "Tailscale is already installed." + Write-Info "Current version: $version" + return + } + + Write-Info "Installing Tailscale for Windows..." + + # Temporary download path + $tempPath = "$env:TEMP\tailscale-setup.exe" + $downloadUrl = "https://pkgs.tailscale.com/stable/tailscale-setup.exe" + + try { + Write-Status "Downloading Tailscale..." + Invoke-WebRequest -Uri $downloadUrl -OutFile $tempPath -UseBasicParsing + + Write-Status "Installing Tailscale... (please wait)" + Start-Process -FilePath $tempPath -ArgumentList "/S" -Wait + + # Refresh PATH environment variable + $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") + + # Verify installation + Start-Sleep -Seconds 3 + $tailscaleInstalled = Get-Command "tailscale" -ErrorAction SilentlyContinue + if (-not $tailscaleInstalled) { + # Try direct path + $tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe" + if (Test-Path $tailscaleExe) { + # Add Tailscale to PATH + $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") + if ($currentPath -notlike "*Tailscale*") { + [Environment]::SetEnvironmentVariable("Path", "$currentPath;C:\Program Files\Tailscale", "Machine") + $env:Path = "$env:Path;C:\Program Files\Tailscale" + } + } + } + + Remove-Item $tempPath -Force -ErrorAction SilentlyContinue + Write-Success "Tailscale installation completed" + + } + catch { + Write-Error "Tailscale installation failed: $($_.Exception.Message)" + throw + } +} + +# ================================ +# Start Tailscale Service +# ================================ +function Start-TailscaleService { + Write-Status "Starting Tailscale service..." + + try { + # Start Tailscale service + $service = Get-Service -Name "Tailscale" -ErrorAction SilentlyContinue + if ($service) { + if ($service.Status -ne "Running") { + Start-Service -Name "Tailscale" + Start-Sleep -Seconds 3 + } + Write-Success "Tailscale service is running." + } else { + Write-Warning "Tailscale service not found. Attempting manual start..." + } + } + catch { + Write-Warning "Failed to start service: $($_.Exception.Message)" + } +} + +# ================================ +# Register with Headscale +# ================================ +function Register-Headscale { + Write-Status "Registering with Headscale server..." + + # Find Tailscale executable path + $tailscaleCmd = Get-Command "tailscale" -ErrorAction SilentlyContinue + if (-not $tailscaleCmd) { + $tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe" + if (Test-Path $tailscaleExe) { + $tailscaleCmd = @{Source = $tailscaleExe} + } else { + Write-Error "Tailscale executable not found." + return $false + } + } + + $tailscalePath = $tailscaleCmd.Source + + # Check existing connection + try { + $status = & $tailscalePath status 2>$null + if ($LASTEXITCODE -eq 0 -and $status) { + Write-Warning "Already connected to Tailscale/Headscale network." + + # Show current connection status + Write-Info "Current connection status:" + $status | Select-Object -First 5 | ForEach-Object { Write-Host " $_" -ForegroundColor Gray } + + # Check force registration option + if ($Force) { + Write-Warning "Force registration option is enabled." + Write-Info "Disconnecting existing connection and re-registering..." + } else { + $response = Read-Host "Disconnect existing connection and register with FARMQ Headscale? (Y/n)" + if ($response -eq "" -or $response -match "^[Yy]") { + Write-Info "Disconnecting existing connection..." + } else { + Write-Info "Skipping registration." + return $true + } + } + + # Disconnect existing connection + try { + & $tailscalePath logout 2>$null + Start-Sleep -Seconds 3 + Write-Success "Existing connection disconnected." + } + catch { + Write-Warning "Error during disconnection, but continuing..." + } + } + } + catch { + # Not connected (normal) + } + + Write-Info "Headscale Server: $HeadscaleServer" + Write-Info "Pre-auth Key: $($PreAuthKey.Substring(0,8))***************" + + # Attempt Headscale registration + Write-Status "Executing registration command..." + + try { + $arguments = @( + "up", + "--login-server=$HeadscaleServer", + "--authkey=$PreAuthKey", + "--accept-routes", + "--accept-dns=false" + ) + + & $tailscalePath $arguments + + if ($LASTEXITCODE -eq 0) { + Write-Success "Headscale registration successful!" + return $true + } else { + Write-Error "Automatic registration failed." + Write-Info "Manual registration command:" + Write-Host "tailscale up --login-server=`"$HeadscaleServer`" --authkey=`"$PreAuthKey`"" + return $false + } + } + catch { + Write-Error "Registration error: $($_.Exception.Message)" + return $false + } +} + +# ================================ +# Configure Firewall +# ================================ +function Configure-Firewall { + Write-Status "Configuring firewall settings..." + + try { + # Add Windows Defender firewall exception + $ruleName = "Tailscale-FARMQ" + $existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue + + if (-not $existingRule) { + New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Protocol UDP -LocalPort 41641 -Action Allow -Profile Any | Out-Null + New-NetFirewallRule -DisplayName "$ruleName-Outbound" -Direction Outbound -Protocol UDP -LocalPort 41641 -Action Allow -Profile Any | Out-Null + Write-Info "Windows Defender firewall exceptions added." + } + + Write-Success "Firewall configuration completed" + } + catch { + Write-Warning "Firewall configuration error: $($_.Exception.Message)" + Write-Info "Please manually allow Tailscale through Windows firewall." + } +} + +# ================================ +# Verify Connection +# ================================ +function Test-Connection { + Write-Status "Verifying network connection..." + + Start-Sleep -Seconds 5 + + # Find Tailscale executable path + $tailscaleCmd = Get-Command "tailscale" -ErrorAction SilentlyContinue + if (-not $tailscaleCmd) { + $tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe" + if (Test-Path $tailscaleExe) { + $tailscaleCmd = @{Source = $tailscaleExe} + } else { + Write-Error "Tailscale executable not found." + return + } + } + + $tailscalePath = $tailscaleCmd.Source + + try { + $status = & $tailscalePath status 2>$null + if ($LASTEXITCODE -ne 0 -or -not $status) { + Write-Error "Tailscale connection issue detected." + return + } + + # Get IP addresses + $ipv4 = & $tailscalePath ip -4 2>$null + $ipv6 = & $tailscalePath ip -6 2>$null + + Write-Success "Headscale network connection completed!" + Write-Info "Assigned IPv4: $(if($ipv4){$ipv4}else{'N/A'})" + Write-Info "Assigned IPv6: $(if($ipv6){$ipv6}else{'N/A'})" + + # Network connectivity test + Write-Status "Testing network connectivity..." + + try { + Test-Connection "100.64.0.1" -Count 2 -Quiet | Out-Null + Write-Success "FARMQ network ($FarmqNetwork) connection successful!" + } + catch { + Write-Warning "Network test failed. Please check firewall settings." + } + + # Show connected nodes + Write-Info "Network status:" + $status | Select-Object -First 10 | ForEach-Object { + Write-Host " $_" -ForegroundColor Gray + } + } + catch { + Write-Error "Connection verification failed: $($_.Exception.Message)" + } +} + +# ================================ +# Cleanup +# ================================ +function Complete-Installation { + Write-Status "Completing installation..." + + # Clean temporary files + Get-ChildItem "$env:TEMP\tailscale*" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue + + Write-Success "Cleanup completed" +} + +# ================================ +# Show Final Information +# ================================ +function Show-FinalInfo { + Write-Header "FARMQ Headscale Windows Installation Complete!" + + # System information + $computerName = $env:COMPUTERNAME + $tailscaleCmd = Get-Command "tailscale" -ErrorAction SilentlyContinue + if (-not $tailscaleCmd) { + $tailscaleExe = "C:\Program Files\Tailscale\tailscale.exe" + if (Test-Path $tailscaleExe) { + $tailscaleCmd = @{Source = $tailscaleExe} + } + } + + if ($tailscaleCmd) { + $tailscaleIP = & $tailscaleCmd.Source ip -4 2>$null + } + + $osVersion = [System.Environment]::OSVersion.Version + + Write-Host "Installation completed successfully!" -ForegroundColor Green + Write-Host "" + + Write-Host "System Information:" -ForegroundColor Cyan + Write-Host " Computer Name: $computerName" + Write-Host " Tailscale IP: $(if($tailscaleIP){$tailscaleIP}else{'N/A'})" + Write-Host " OS: Windows $($osVersion.Major).$($osVersion.Minor)" + Write-Host " Headscale Server: $HeadscaleServer" + + Write-Host "" + Write-Host "Useful Commands:" -ForegroundColor Yellow + Write-Host " tailscale status # Check connection status" + Write-Host " tailscale ip # Show assigned IP" + Write-Host " tailscale ping # Test connection to other nodes" + Write-Host " tailscale logout # Disconnect from network" + + Write-Host "" + Write-Host "FARMQ Management Pages:" -ForegroundColor Magenta + Write-Host " http://192.168.0.151:5002" + Write-Host " http://192.168.0.151:5002/vms (VM Management)" + + Write-Host "" + Write-Host "If you encounter issues, check:" -ForegroundColor White + Write-Host " 1. Windows Firewall settings" + Write-Host " 2. Antivirus software exceptions" + Write-Host " 3. Corporate network policies" + + Write-Header "Installation Complete - You can now use FARMQ network!" +} + +# ================================ +# Main Function +# ================================ +function Main { + # Stop on errors + $ErrorActionPreference = "Stop" + + Write-Header "FARMQ Headscale Windows One-Click Installation" + + try { + # Installation process + Test-Requirements + Install-Tailscale + Start-TailscaleService + $registerSuccess = Register-Headscale + + if ($registerSuccess) { + Configure-Firewall + Test-Connection + Complete-Installation + Show-FinalInfo + } else { + Write-Warning "Registration failed but Tailscale is installed." + Write-Info "Please complete registration manually." + } + + } + catch { + Write-Error "Installation error occurred: $($_.Exception.Message)" + Write-Info "If the problem persists, please contact administrator." + Write-Host "" + Read-Host "Press any key to exit..." + exit 1 + } +} + +# ================================ +# Script Execution +# ================================ + +# Handle parameters +if ($args -contains "--help" -or $args -contains "-h") { + Write-Host "FARMQ Headscale Windows Installation Script" + Write-Host "" + Write-Host "Usage:" + Write-Host " iex ((New-Object System.Net.WebClient).DownloadString('https://git.0bin.in/.../farmq-install-en.ps1'))" + Write-Host "" + Write-Host "Options:" + Write-Host " -Force Force disconnect existing connection and re-register" + Write-Host " -HeadscaleServer Server address (default: https://head.0bin.in)" + Write-Host "" + Write-Host "Examples:" + Write-Host " # Force re-registration" + Write-Host " `$Force = `$true; iex ((New-Object System.Net.WebClient).DownloadString('https://git.0bin.in/.../farmq-install-en.ps1'))" + exit 0 +} + +# Handle Force parameter from URL or variable +if ($MyInvocation.MyCommand.Path -like "*force=1*" -or (Get-Variable -Name "ForceInstall" -ErrorAction SilentlyContinue)) { + $Force = $true +} + +# Execute main function +Main \ No newline at end of file