# ========================================== # 🖥️ SYSTEM UTILS # ========================================== # --- СИСТЕМНАЯ ИНФОРМАЦИЯ --- function Get-SystemInfo { return @{ os = "windows" version = [System.Environment]::OSVersion.Version.Major.ToString() } } function Ensure-VCRedist { Write-Info "Проверка Visual C++ Redistributable..." # Check registry for VC++ 2015-2022 (x64) # Key: HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64 $regPath = "HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" if (Test-Path $regPath) { $installed = (Get-ItemProperty -Path $regPath).Installed if ($installed -eq 1) { Write-Success "Visual C++ Redistributable уже установлен." return } } Write-Warning "Visual C++ Redistributable не найден. Устанавливаю..." $vcUrl = "https://aka.ms/vs/17/release/vc_redist.x64.exe" $vcFile = "$env:TEMP\vc_redist.x64.exe" if (Download-File -Url $vcUrl -Destination $vcFile) { Write-Step "Установка библиотек Visual C++..." $process = Start-Process -FilePath $vcFile -ArgumentList "/install", "/quiet", "/norestart" -PassThru -Wait if ($process.ExitCode -eq 0 -or $process.ExitCode -eq 3010) { # 3010 = reboot required (usually works without immediate reboot) Write-Success "Библиотеки установлены!" } else { Write-Error "Ошибка установки VC++ (Код: $($process.ExitCode))" Write-Host " Попробуйте установить вручную: https://aka.ms/vs/17/release/vc_redist.x64.exe" } Remove-Item $vcFile -Force -ErrorAction SilentlyContinue } } # --- DOCKER --- function Test-Docker { $status = @{ Installed = $false Running = $false Compose = $false } try { $ver = docker --version 2>&1 if ($LASTEXITCODE -eq 0) { $status.Installed = $true } } catch {} if ($status.Installed) { try { $info = docker info 2>&1 if ($LASTEXITCODE -eq 0) { $status.Running = $true } } catch {} } if ($status.Running) { try { $comp = docker compose version 2>&1 if ($LASTEXITCODE -eq 0) { $status.Compose = $true } } catch { # Check legacy try { $comp = docker-compose --version 2>&1 if ($LASTEXITCODE -eq 0) { $status.Compose = $true } } catch {} } } return $status } # --- СЛУЖБЫ И ЗАДАЧИ --- function Manage-ScheduledTask { param( [string]$Name, [string]$ExePath, [string]$Arguments, [string]$WorkDir, [string]$Action = "Install" # Install, Uninstall, Start, Stop ) switch ($Action) { "Install" { # Удаляем старую Unregister-ScheduledTask -TaskName $Name -Confirm:$false -ErrorAction SilentlyContinue $act = New-ScheduledTaskAction -Execute "$ExePath" -Argument "$Arguments" -WorkingDirectory $WorkDir $trig = New-ScheduledTaskTrigger -AtStartup $princ = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest $sett = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) Register-ScheduledTask -TaskName $Name -Action $act -Trigger $trig -Principal $princ -Settings $sett -Force | Out-Null return $true } "Uninstall" { Unregister-ScheduledTask -TaskName $Name -Confirm:$false -ErrorAction SilentlyContinue } "Start" { Start-ScheduledTask -TaskName $Name -ErrorAction SilentlyContinue } "Stop" { Stop-ScheduledTask -TaskName $Name -ErrorAction SilentlyContinue # Пытаемся убить процесс по имени exe if ($ExePath) { $procName = [System.IO.Path]::GetFileNameWithoutExtension($ExePath) if ($procName) { Stop-Process -Name $procName -Force -ErrorAction SilentlyContinue } } } } } function Get-TaskStatus { param([string]$Name) $task = Get-ScheduledTask -TaskName $Name -ErrorAction SilentlyContinue if ($task) { # Если задача в статусе Running — возвращаем Running if ($task.State -eq "Running") { return "Running" } # Если задача Ready — проверяем, работает ли процесс sing-box # (scheduled task может быть Ready даже когда процесс работает) $process = Get-Process -Name "sing-box" -ErrorAction SilentlyContinue if ($process) { return "Running" } return $task.State } return $null } function Ensure-FirewallPort { param( [int]$Port, [string]$Name, [string]$Protocol = "TCP" ) $rule = Get-NetFirewallRule -DisplayName $Name -ErrorAction SilentlyContinue if (-not $rule) { New-NetFirewallRule -DisplayName $Name -Direction Inbound -LocalPort $Port -Protocol $Protocol -Action Allow -Profile Any | Out-Null return $true } return $false } function Get-LocalIPs { return (Get-NetIPAddress -AddressFamily IPv4 -InterfaceAlias * | Where-Object { $_.IPAddress -notmatch "^127\." -and $_.IPAddress -notmatch "^169\.254\." }).IPAddress }