feat: Реализована нативная установка sing-box с системными утилитами и веб-сервером, заменяя устаревшие скрипты.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
data
|
data
|
||||||
|
_legacy
|
||||||
|
|||||||
@@ -1,419 +0,0 @@
|
|||||||
# ==========================================
|
|
||||||
# <20>️ PROXIFYRE INSTALLER ДЛЯ DISCORD/VESKTOP
|
|
||||||
# ==========================================
|
|
||||||
param(
|
|
||||||
[switch]$Force, # Принудительная переустановка
|
|
||||||
[string]$Proxy = "", # Адрес прокси (для неинтерактивного режима), например "127.0.0.1:8080"
|
|
||||||
[string[]]$Apps = @(), # Приложения (для неинтерактивного режима), например @("Discord", "Vesktop")
|
|
||||||
[switch]$Silent # Тихий режим (без лишнего вывода, для вызова из других скриптов)
|
|
||||||
)
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# 🛠️ ПРЯМЫЕ ССЫЛКИ (Hardcoded)
|
|
||||||
# ==========================================
|
|
||||||
$InstallPath = "C:\Tools\ProxiFyre"
|
|
||||||
$DriverUrl = "https://github.com/wiresock/ndisapi/releases/download/v3.6.2/Windows.Packet.Filter.3.6.2.1.x64.msi"
|
|
||||||
$AppUrl = "https://github.com/wiresock/proxifyre/releases/download/v2.1.4/ProxiFyre-v2.1.4-x64-signed.zip"
|
|
||||||
# ==========================================
|
|
||||||
|
|
||||||
# 0. Проверка прав
|
|
||||||
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
|
||||||
Write-Host "⛔ Запусти от имени АДМИНИСТРАТОРА!" -ForegroundColor Red
|
|
||||||
Start-Sleep -s 3; Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear-Host
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host " 🚀 PROXIFYRE SERVICE INSTALLER " -ForegroundColor Cyan
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ПРОВЕРКА СУЩЕСТВУЮЩЕЙ УСТАНОВКИ
|
|
||||||
# ==========================================
|
|
||||||
$proxifyreExists = Test-Path "$InstallPath\ProxiFyre.exe"
|
|
||||||
$proxifyreService = Get-Service -Name "ProxiFyreService" -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($proxifyreExists -and -not $Force) {
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host " ✅ PROXIFYRE УЖЕ УСТАНОВЛЕН! " -ForegroundColor Green
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Статус службы
|
|
||||||
Write-Host "📊 Статус службы: " -NoNewline -ForegroundColor Yellow
|
|
||||||
if ($proxifyreService -and $proxifyreService.Status -eq 'Running') {
|
|
||||||
Write-Host "РАБОТАЕТ" -ForegroundColor Green
|
|
||||||
}
|
|
||||||
elseif ($proxifyreService) {
|
|
||||||
Write-Host "$($proxifyreService.Status)" -ForegroundColor Red
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "НЕ ЗАРЕГИСТРИРОВАНА" -ForegroundColor Red
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📁 Расположение:" -ForegroundColor Yellow
|
|
||||||
Write-Host " Путь: $InstallPath" -ForegroundColor White
|
|
||||||
Write-Host " Конфиг: $InstallPath\app-config.json" -ForegroundColor White
|
|
||||||
Write-Host " Логи: $InstallPath\logs\" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Показываем текущий конфиг
|
|
||||||
$configPath = "$InstallPath\app-config.json"
|
|
||||||
if (Test-Path $configPath) {
|
|
||||||
try {
|
|
||||||
$config = Get-Content $configPath -Raw | ConvertFrom-Json
|
|
||||||
$apps = $config.proxies[0].appNames -join ", "
|
|
||||||
$proxy = $config.proxies[0].socks5ProxyEndpoint
|
|
||||||
Write-Host "⚙️ Текущие настройки:" -ForegroundColor Yellow
|
|
||||||
Write-Host " Прокси: $proxy" -ForegroundColor White
|
|
||||||
Write-Host " Приложения: $apps" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Host " ⚠️ Не удалось прочитать конфиг" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Что вы хотите сделать?" -ForegroundColor Yellow
|
|
||||||
Write-Host " [1] Запустить службу" -ForegroundColor White
|
|
||||||
Write-Host " [2] Остановить службу" -ForegroundColor White
|
|
||||||
Write-Host " [3] Перезапустить службу" -ForegroundColor White
|
|
||||||
Write-Host " [4] Изменить настройки (прокси/приложения)" -ForegroundColor White
|
|
||||||
Write-Host " [5] Показать логи" -ForegroundColor White
|
|
||||||
Write-Host " [6] Показать конфиг" -ForegroundColor White
|
|
||||||
Write-Host " [7] Переустановить (полная переустановка)" -ForegroundColor White
|
|
||||||
Write-Host " [q] Выход" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
$choice = Read-Host "👉 Выбор (1-7/q)"
|
|
||||||
|
|
||||||
switch ($choice) {
|
|
||||||
"1" {
|
|
||||||
Write-Host "`n🚀 Запуск службы..." -ForegroundColor Cyan
|
|
||||||
Start-Process -FilePath "$InstallPath\ProxiFyre.exe" -ArgumentList "start" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 2
|
|
||||||
$svc = Get-Service -Name "ProxiFyreService" -ErrorAction SilentlyContinue
|
|
||||||
Write-Host " Статус: $($svc.Status)" -ForegroundColor Green
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"2" {
|
|
||||||
Write-Host "`n⏹️ Остановка службы..." -ForegroundColor Cyan
|
|
||||||
Start-Process -FilePath "$InstallPath\ProxiFyre.exe" -ArgumentList "stop" -Wait -NoNewWindow
|
|
||||||
Write-Host " ✅ Служба остановлена" -ForegroundColor Green
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"3" {
|
|
||||||
Write-Host "`n🔄 Перезапуск службы..." -ForegroundColor Cyan
|
|
||||||
Start-Process -FilePath "$InstallPath\ProxiFyre.exe" -ArgumentList "stop" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 1
|
|
||||||
Start-Process -FilePath "$InstallPath\ProxiFyre.exe" -ArgumentList "start" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 2
|
|
||||||
$svc = Get-Service -Name "ProxiFyreService" -ErrorAction SilentlyContinue
|
|
||||||
Write-Host " Статус: $($svc.Status)" -ForegroundColor Green
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"4" {
|
|
||||||
Write-Host "`n⚙️ Изменение настроек..." -ForegroundColor Cyan
|
|
||||||
|
|
||||||
# Выбор режима
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📡 Где работает VPN-прокси?" -ForegroundColor Yellow
|
|
||||||
Write-Host " [1] Удалённый сервер (Docker на VPS/другом ПК)" -ForegroundColor White
|
|
||||||
Write-Host " [2] Локальный Docker Desktop (на этом ПК)" -ForegroundColor White
|
|
||||||
$modeChoice = Read-Host "👉 Выбор (1-2)"
|
|
||||||
|
|
||||||
if ($modeChoice -eq "2") {
|
|
||||||
# Локальный Docker — нужен sing-box
|
|
||||||
$singboxTask = Get-ScheduledTask -TaskName "SingBoxProxy" -ErrorAction SilentlyContinue
|
|
||||||
if ($singboxTask -and $singboxTask.State -eq "Running") {
|
|
||||||
Write-Host "`n✅ Нативный sing-box работает!" -ForegroundColor Green
|
|
||||||
$NewProxy = "127.0.0.1:1080"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "`n⚠️ Нативный sing-box не запущен!" -ForegroundColor Yellow
|
|
||||||
Write-Host " Запустите: .\setup-singbox-native.ps1" -ForegroundColor Cyan
|
|
||||||
$runSetup = Read-Host " Запустить сейчас? (y/n)"
|
|
||||||
if ($runSetup -eq "y") {
|
|
||||||
$setupScript = Join-Path $PSScriptRoot "setup-singbox-native.ps1"
|
|
||||||
if (Test-Path $setupScript) { & $setupScript }
|
|
||||||
}
|
|
||||||
$NewProxy = "127.0.0.1:1080"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Удалённый прокси
|
|
||||||
Write-Host "`n🌐 SOCKS5 Прокси (IP:PORT):" -ForegroundColor Yellow
|
|
||||||
$NewProxy = Read-Host " [Enter] для 127.0.0.1:8080"
|
|
||||||
if ([string]::IsNullOrWhiteSpace($NewProxy)) { $NewProxy = "127.0.0.1:8080" }
|
|
||||||
}
|
|
||||||
|
|
||||||
# Запрос приложений
|
|
||||||
Write-Host "`n🎮 Что проксируем?" -ForegroundColor Yellow
|
|
||||||
Write-Host " [1] Vesktop"
|
|
||||||
Write-Host " [2] Discord"
|
|
||||||
Write-Host " [3] Vesktop + Discord"
|
|
||||||
$AppChoice = Read-Host "👉 Выбор (1-3)"
|
|
||||||
|
|
||||||
switch ($AppChoice) {
|
|
||||||
"2" { $NewApps = @("Discord") }
|
|
||||||
"3" { $NewApps = @("Vesktop", "Discord") }
|
|
||||||
Default { $NewApps = @("Vesktop") }
|
|
||||||
}
|
|
||||||
|
|
||||||
# Генерируем новый конфиг
|
|
||||||
Write-Host "`n📝 Обновляем конфиг..." -ForegroundColor Cyan
|
|
||||||
$ConfigData = @{
|
|
||||||
logLevel = "Info"
|
|
||||||
proxies = @(
|
|
||||||
@{
|
|
||||||
appNames = $NewApps
|
|
||||||
socks5ProxyEndpoint = $NewProxy
|
|
||||||
username = ""
|
|
||||||
password = ""
|
|
||||||
supportedProtocols = @("TCP", "UDP")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
excludes = @()
|
|
||||||
} | ConvertTo-Json -Depth 4
|
|
||||||
Set-Content -Path $configPath -Value $ConfigData -Encoding UTF8
|
|
||||||
|
|
||||||
Write-Host " ✅ Конфиг обновлён:" -ForegroundColor Green
|
|
||||||
Write-Host " Прокси: $NewProxy" -ForegroundColor White
|
|
||||||
Write-Host " Приложения: $($NewApps -join ', ')" -ForegroundColor White
|
|
||||||
|
|
||||||
# Перезапуск службы
|
|
||||||
Write-Host "`n🔄 Перезапуск службы для применения..." -ForegroundColor Cyan
|
|
||||||
Start-Process -FilePath "$InstallPath\ProxiFyre.exe" -ArgumentList "stop" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 1
|
|
||||||
Start-Process -FilePath "$InstallPath\ProxiFyre.exe" -ArgumentList "start" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 2
|
|
||||||
$svc = Get-Service -Name "ProxiFyreService" -ErrorAction SilentlyContinue
|
|
||||||
Write-Host " Статус: $($svc.Status)" -ForegroundColor Green
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"5" {
|
|
||||||
Write-Host "`n📋 Логи ProxiFyre:" -ForegroundColor Cyan
|
|
||||||
$logFiles = Get-ChildItem "$InstallPath\logs\*.log" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
|
||||||
if ($logFiles) {
|
|
||||||
Get-Content $logFiles.FullName -Tail 50
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host " Лог-файлы не найдены" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"6" {
|
|
||||||
Write-Host "`n📄 Конфиг ProxiFyre:" -ForegroundColor Cyan
|
|
||||||
if (Test-Path $configPath) {
|
|
||||||
Get-Content $configPath
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host " Конфиг не найден" -ForegroundColor Yellow
|
|
||||||
}
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"7" {
|
|
||||||
Write-Host "`n🔧 Переустановка..." -ForegroundColor Cyan
|
|
||||||
$Force = $true
|
|
||||||
# Продолжаем выполнение скрипта для полной переустановки
|
|
||||||
}
|
|
||||||
default {
|
|
||||||
Write-Host "`n ℹ️ Выход" -ForegroundColor Gray
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# --- ШАГ 1: КОНФИГУРАЦИЯ ---
|
|
||||||
# Если параметры переданы — используем их (неинтерактивный режим)
|
|
||||||
if ($Proxy -and $Apps.Count -gt 0) {
|
|
||||||
$UserProxy = $Proxy
|
|
||||||
$TargetApps = $Apps
|
|
||||||
if (-not $Silent) {
|
|
||||||
Write-Host "`n⚙️ Неинтерактивный режим:" -ForegroundColor Cyan
|
|
||||||
Write-Host " Прокси: $UserProxy" -ForegroundColor White
|
|
||||||
Write-Host " Приложения: $($TargetApps -join ', ')" -ForegroundColor White
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Интерактивный режим — сначала выбор режима
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host " 📡 ГДЕ РАБОТАЕТ ВАШ VPN-ПРОКСИ? " -ForegroundColor Cyan
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host " [1] 🌐 УДАЛЁННЫЙ СЕРВЕР" -ForegroundColor White
|
|
||||||
Write-Host " Docker на VPS или другом ПК в сети" -ForegroundColor Gray
|
|
||||||
Write-Host " (UDP работает, просто укажите адрес)" -ForegroundColor Gray
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host " [2] 💻 ЛОКАЛЬНЫЙ DOCKER НА WINDOWS" -ForegroundColor White
|
|
||||||
Write-Host " Docker Desktop на этом компьютере" -ForegroundColor Gray
|
|
||||||
Write-Host " (UDP НЕ работает, нужен нативный sing-box)" -ForegroundColor Gray
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
$modeChoice = Read-Host "👉 Выбор (1-2)"
|
|
||||||
|
|
||||||
if ($modeChoice -eq "2") {
|
|
||||||
# Локальный Docker — нужен нативный sing-box
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Yellow
|
|
||||||
Write-Host " ⚠️ ЛОКАЛЬНЫЙ DOCKER DESKTOP " -ForegroundColor Yellow
|
|
||||||
Write-Host "==========================================" -ForegroundColor Yellow
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Docker Desktop на Windows НЕ поддерживает UDP!" -ForegroundColor Red
|
|
||||||
Write-Host "Для голосовых звонков Discord нужен нативный sing-box." -ForegroundColor Red
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Проверяем, установлен ли уже нативный sing-box
|
|
||||||
$singboxInstalled = Test-Path "C:\Tools\sing-box\sing-box.exe"
|
|
||||||
$singboxTask = Get-ScheduledTask -TaskName "SingBoxProxy" -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($singboxInstalled -and $singboxTask -and $singboxTask.State -eq "Running") {
|
|
||||||
Write-Host "✅ Нативный sing-box уже установлен и работает!" -ForegroundColor Green
|
|
||||||
Write-Host " Прокси: 127.0.0.1:1080" -ForegroundColor White
|
|
||||||
$UserProxy = "127.0.0.1:1080"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "Для установки нативного sing-box запустите:" -ForegroundColor Yellow
|
|
||||||
Write-Host " .\setup-singbox-native.ps1" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
$runSetup = Read-Host "Запустить setup-singbox-native.ps1 сейчас? (y/n)"
|
|
||||||
if ($runSetup -eq "y") {
|
|
||||||
$setupScript = Join-Path $PSScriptRoot "setup-singbox-native.ps1"
|
|
||||||
if (Test-Path $setupScript) {
|
|
||||||
& $setupScript
|
|
||||||
# После установки используем порт 1080
|
|
||||||
$UserProxy = "127.0.0.1:1080"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "❌ Скрипт setup-singbox-native.ps1 не найден!" -ForegroundColor Red
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Установите sing-box вручную и повторите." -ForegroundColor Yellow
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Удалённый прокси — просто спрашиваем адрес
|
|
||||||
Write-Host "`n🌐 SOCKS5 Прокси (IP:PORT):" -ForegroundColor Yellow
|
|
||||||
Write-Host " Примеры: 192.168.1.100:8080, myserver.com:8080" -ForegroundColor Gray
|
|
||||||
$UserProxy = Read-Host " [Enter] для 127.0.0.1:8080"
|
|
||||||
if ([string]::IsNullOrWhiteSpace($UserProxy)) { $UserProxy = "127.0.0.1:8080" }
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`n🎮 Что проксируем?" -ForegroundColor Yellow
|
|
||||||
Write-Host " [1] Vesktop"
|
|
||||||
Write-Host " [2] Discord"
|
|
||||||
Write-Host " [3] Vesktop + Discord"
|
|
||||||
$AppChoice = Read-Host "👉 Выбор (1-3)"
|
|
||||||
|
|
||||||
switch ($AppChoice) {
|
|
||||||
"2" { $TargetApps = @("Discord") }
|
|
||||||
"3" { $TargetApps = @("Vesktop", "Discord") }
|
|
||||||
Default { $TargetApps = @("Vesktop") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
||||||
|
|
||||||
# --- ШАГ 2: ПОДГОТОВКА (ОЧИСТКА) ---
|
|
||||||
Write-Host "`n🧹 [0/3] Очистка процессов..." -ForegroundColor Magenta
|
|
||||||
# Убиваем любые старые процессы, чтобы они не держали драйвер
|
|
||||||
Stop-Process -Name "ProxiFyre" -Force -ErrorAction SilentlyContinue
|
|
||||||
Start-Sleep -s 1
|
|
||||||
|
|
||||||
# --- ШАГ 3: ДРАЙВЕР ---
|
|
||||||
Write-Host "⚙️ [1/3] Ставим драйвер (Packet Filter)..." -ForegroundColor Magenta
|
|
||||||
$MsiFile = "$env:TEMP\WinpkFilter.msi"
|
|
||||||
|
|
||||||
try {
|
|
||||||
Invoke-WebRequest -Uri $DriverUrl -OutFile $MsiFile -ErrorAction Stop
|
|
||||||
$Proc = Start-Process -FilePath "msiexec.exe" -ArgumentList "/i `"$MsiFile`" /qn /norestart" -Wait -PassThru
|
|
||||||
if ($Proc.ExitCode -eq 0) { Write-Host " ✅ Успешно." -ForegroundColor Green }
|
|
||||||
elseif ($Proc.ExitCode -eq 1603) { Write-Host " ⚠️ Уже установлен." -ForegroundColor Yellow }
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "❌ Ошибка скачивания драйвера: $_"; Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- ШАГ 4: PROXIFYRE ---
|
|
||||||
Write-Host "`n⬇️ [2/3] Ставим ProxiFyre..." -ForegroundColor Magenta
|
|
||||||
|
|
||||||
# Если папка есть, чистим всё, кроме конфига (если вдруг хотим сохранить), но проще пересоздать
|
|
||||||
if (Test-Path $InstallPath) { Remove-Item $InstallPath -Recurse -Force -ErrorAction SilentlyContinue }
|
|
||||||
New-Item -ItemType Directory -Force -Path $InstallPath | Out-Null
|
|
||||||
|
|
||||||
try {
|
|
||||||
$ZipFile = "$env:TEMP\ProxiFyre.zip"
|
|
||||||
Invoke-WebRequest -Uri $AppUrl -OutFile $ZipFile -ErrorAction Stop
|
|
||||||
Expand-Archive -Path $ZipFile -DestinationPath $InstallPath -Force
|
|
||||||
Write-Host " ✅ Распаковано." -ForegroundColor Green
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "❌ Ошибка скачивания ProxiFyre: $_"; Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
# Вытаскиваем из подпапки если надо
|
|
||||||
$ExeItem = Get-ChildItem -Path $InstallPath -Filter "ProxiFyre.exe" -Recurse | Select-Object -First 1
|
|
||||||
if ($ExeItem.DirectoryName -ne $InstallPath) {
|
|
||||||
Copy-Item -Path "$($ExeItem.DirectoryName)\*" -Destination $InstallPath -Recurse -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- ШАГ 5: КОНФИГ ---
|
|
||||||
Write-Host "`n📝 [3/3] Генерируем конфиг..." -ForegroundColor Cyan
|
|
||||||
$ConfigPath = Join-Path -Path $InstallPath -ChildPath "app-config.json"
|
|
||||||
$ConfigData = @{
|
|
||||||
logLevel = "Info"
|
|
||||||
proxies = @(
|
|
||||||
@{
|
|
||||||
appNames = $TargetApps
|
|
||||||
socks5ProxyEndpoint = $UserProxy
|
|
||||||
username = ""
|
|
||||||
password = ""
|
|
||||||
supportedProtocols = @("TCP", "UDP")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
excludes = @()
|
|
||||||
} | ConvertTo-Json -Depth 4
|
|
||||||
Set-Content -Path $ConfigPath -Value $ConfigData -Encoding UTF8
|
|
||||||
|
|
||||||
# --- ШАГ 6: УСТАНОВКА СЛУЖБЫ ---
|
|
||||||
Write-Host "`n🛡️ [4/3] Регистрация службы Windows..." -ForegroundColor Cyan
|
|
||||||
$ExePath = Join-Path -Path $InstallPath -ChildPath "ProxiFyre.exe"
|
|
||||||
|
|
||||||
# 1. Удаляем старую службу если была
|
|
||||||
Start-Process -FilePath $ExePath -ArgumentList "uninstall" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 1
|
|
||||||
|
|
||||||
# 2. Инсталлируем службу
|
|
||||||
Start-Process -FilePath $ExePath -ArgumentList "install" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 1
|
|
||||||
|
|
||||||
# 3. Запускаем службу
|
|
||||||
Write-Host " 🚀 Запускаем службу..." -ForegroundColor Yellow
|
|
||||||
Start-Process -FilePath $ExePath -ArgumentList "start" -Wait -NoNewWindow
|
|
||||||
Start-Sleep -s 3
|
|
||||||
|
|
||||||
# 4. Проверка
|
|
||||||
# Важно: имя службы в системе именно "ProxiFyreService" (судя по твоим логам)
|
|
||||||
$Service = Get-Service -Name "ProxiFyreService" -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($Service -and $Service.Status -eq 'Running') {
|
|
||||||
Write-Host "`n==========================================" -ForegroundColor Green
|
|
||||||
Write-Host "🎉 УСПЕХ! Служба работает в фоне." -ForegroundColor Green
|
|
||||||
Write-Host "📁 Логи тут: $InstallPath\logs"
|
|
||||||
Write-Host "=========================================="
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "`n⚠️ Служба установлена, но статус: $($Service.Status). Проверь логи." -ForegroundColor Red
|
|
||||||
Write-Host "Попробуй вручную: net start ProxiFyreService"
|
|
||||||
}
|
|
||||||
@@ -1,460 +0,0 @@
|
|||||||
# ==========================================
|
|
||||||
# 🐳 VPN-PROXY DOCKER INSTALLER ДЛЯ WINDOWS
|
|
||||||
# ==========================================
|
|
||||||
# Устанавливает VPN-прокси через Docker Compose
|
|
||||||
# и опционально настраивает Discord/Vesktop
|
|
||||||
# ==========================================
|
|
||||||
|
|
||||||
param(
|
|
||||||
[switch]$SkipDockerCheck,
|
|
||||||
[switch]$SkipDiscord,
|
|
||||||
[switch]$Force # Принудительная переустановка
|
|
||||||
)
|
|
||||||
|
|
||||||
# Цвета для красивого вывода
|
|
||||||
function Write-Step { param($msg) Write-Host "`n📦 $msg" -ForegroundColor Cyan }
|
|
||||||
function Write-Success { param($msg) Write-Host " ✅ $msg" -ForegroundColor Green }
|
|
||||||
function Write-Warning { param($msg) Write-Host " ⚠️ $msg" -ForegroundColor Yellow }
|
|
||||||
function Write-Error { param($msg) Write-Host " ❌ $msg" -ForegroundColor Red }
|
|
||||||
function Write-Info { param($msg) Write-Host " ℹ️ $msg" -ForegroundColor Gray }
|
|
||||||
|
|
||||||
Clear-Host
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host " 🐳 VPN-PROXY DOCKER INSTALLER " -ForegroundColor Cyan
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
|
|
||||||
# Определяем директорию скрипта
|
|
||||||
$ScriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
|
|
||||||
if (-not $ScriptDir) { $ScriptDir = Get-Location }
|
|
||||||
|
|
||||||
Write-Info "Директория проекта: $ScriptDir"
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ШАГ 0: ПРОВЕРКА СУЩЕСТВУЮЩЕЙ УСТАНОВКИ
|
|
||||||
# ==========================================
|
|
||||||
Write-Step "Проверка существующей установки..."
|
|
||||||
|
|
||||||
$existingContainer = docker ps -a --filter "name=sing-proxy" --format "{{.Names}}" 2>$null
|
|
||||||
$containerRunning = docker ps --filter "name=sing-proxy" --format "{{.Status}}" 2>$null
|
|
||||||
|
|
||||||
if ($existingContainer -and -not $Force) {
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host " ✅ VPN-PROXY УЖЕ УСТАНОВЛЕН! " -ForegroundColor Green
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Статус контейнера
|
|
||||||
if ($containerRunning -match "Up") {
|
|
||||||
Write-Host "📊 Статус: " -NoNewline -ForegroundColor Yellow
|
|
||||||
Write-Host "РАБОТАЕТ" -ForegroundColor Green
|
|
||||||
Write-Host " Контейнер: sing-proxy" -ForegroundColor Gray
|
|
||||||
Write-Host " Uptime: $containerRunning" -ForegroundColor Gray
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "📊 Статус: " -NoNewline -ForegroundColor Yellow
|
|
||||||
Write-Host "ОСТАНОВЛЕН" -ForegroundColor Red
|
|
||||||
$containerStatus = docker ps -a --filter "name=sing-proxy" --format "{{.Status}}" 2>$null
|
|
||||||
Write-Host " Контейнер: sing-proxy ($containerStatus)" -ForegroundColor Gray
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📡 Адреса подключения:" -ForegroundColor Yellow
|
|
||||||
Write-Host " Веб-интерфейс: http://localhost:3456" -ForegroundColor White
|
|
||||||
Write-Host " HTTP/SOCKS Прокси: 127.0.0.1:8080" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📁 Расположение:" -ForegroundColor Yellow
|
|
||||||
Write-Host " Проект: $ScriptDir" -ForegroundColor White
|
|
||||||
Write-Host " Данные: $ScriptDir\data" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# Проверяем ProxiFyre
|
|
||||||
$proxifyreInstalled = Test-Path "C:\Tools\ProxiFyre\ProxiFyre.exe"
|
|
||||||
if ($proxifyreInstalled) {
|
|
||||||
$proxifyreService = Get-Service -Name "ProxiFyreService" -ErrorAction SilentlyContinue
|
|
||||||
Write-Host "🛡️ ProxiFyre:" -ForegroundColor Yellow
|
|
||||||
Write-Host " Путь: C:\Tools\ProxiFyre" -ForegroundColor White
|
|
||||||
Write-Host " Статус: $($proxifyreService.Status)" -ForegroundColor White
|
|
||||||
|
|
||||||
# Показываем настроенные приложения из конфига
|
|
||||||
$configPath = "C:\Tools\ProxiFyre\app-config.json"
|
|
||||||
if (Test-Path $configPath) {
|
|
||||||
try {
|
|
||||||
$config = Get-Content $configPath -Raw | ConvertFrom-Json
|
|
||||||
$apps = $config.proxies[0].appNames -join ", "
|
|
||||||
$proxy = $config.proxies[0].socks5ProxyEndpoint
|
|
||||||
Write-Host " Прокси: $proxy" -ForegroundColor White
|
|
||||||
Write-Host " Приложения: $apps" -ForegroundColor White
|
|
||||||
}
|
|
||||||
catch {}
|
|
||||||
}
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Что вы хотите сделать?" -ForegroundColor Yellow
|
|
||||||
Write-Host " [1] Запустить контейнер (если остановлен)" -ForegroundColor White
|
|
||||||
Write-Host " [2] Перезапустить контейнер" -ForegroundColor White
|
|
||||||
Write-Host " [3] Переустановить (пересобрать)" -ForegroundColor White
|
|
||||||
Write-Host " [4] Показать логи" -ForegroundColor White
|
|
||||||
Write-Host " [5] Открыть веб-интерфейс" -ForegroundColor White
|
|
||||||
Write-Host " [q] Выход" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
$choice = Read-Host "👉 Выбор (1-5/q)"
|
|
||||||
|
|
||||||
switch ($choice) {
|
|
||||||
"1" {
|
|
||||||
Write-Step "Запуск контейнера..."
|
|
||||||
Push-Location $ScriptDir
|
|
||||||
docker compose start
|
|
||||||
Pop-Location
|
|
||||||
Write-Success "Контейнер запущен!"
|
|
||||||
Write-Host " Веб-интерфейс: http://localhost:3456" -ForegroundColor Green
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"2" {
|
|
||||||
Write-Step "Перезапуск контейнера..."
|
|
||||||
Push-Location $ScriptDir
|
|
||||||
docker compose restart
|
|
||||||
Pop-Location
|
|
||||||
Write-Success "Контейнер перезапущен!"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"3" {
|
|
||||||
Write-Info "Переустановка..."
|
|
||||||
# Продолжаем выполнение скрипта
|
|
||||||
}
|
|
||||||
"4" {
|
|
||||||
Write-Step "Логи контейнера (последние 50 строк):"
|
|
||||||
docker logs --tail 50 sing-proxy
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"5" {
|
|
||||||
Start-Process "http://localhost:3456"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
default {
|
|
||||||
Write-Info "Выход"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ШАГ 1: ПРОВЕРКА DOCKER
|
|
||||||
# ==========================================
|
|
||||||
Write-Step "Проверка Docker..."
|
|
||||||
|
|
||||||
$DockerInstalled = $false
|
|
||||||
$DockerRunning = $false
|
|
||||||
$DockerComposeAvailable = $false
|
|
||||||
|
|
||||||
# Проверяем, установлен ли Docker
|
|
||||||
try {
|
|
||||||
$dockerVersion = docker --version 2>&1
|
|
||||||
if ($LASTEXITCODE -eq 0 -and $dockerVersion -match "Docker version") {
|
|
||||||
Write-Success "Docker установлен: $dockerVersion"
|
|
||||||
$DockerInstalled = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
$DockerInstalled = $false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $DockerInstalled -and -not $SkipDockerCheck) {
|
|
||||||
Write-Error "Docker не найден!"
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📥 Установите Docker Desktop:" -ForegroundColor Yellow
|
|
||||||
Write-Host " https://www.docker.com/products/docker-desktop/" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "После установки:" -ForegroundColor Yellow
|
|
||||||
Write-Host " 1. Запустите Docker Desktop" -ForegroundColor White
|
|
||||||
Write-Host " 2. Дождитесь, пока появится зелёная иконка 🐳 в трее" -ForegroundColor White
|
|
||||||
Write-Host " 3. Запустите этот скрипт заново" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
$openUrl = Read-Host "Открыть страницу загрузки Docker? (y/n)"
|
|
||||||
if ($openUrl -eq "y" -or $openUrl -eq "Y") {
|
|
||||||
Start-Process "https://www.docker.com/products/docker-desktop/"
|
|
||||||
}
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Проверяем, запущен ли Docker
|
|
||||||
if ($DockerInstalled) {
|
|
||||||
try {
|
|
||||||
$dockerInfo = docker info 2>&1
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
Write-Success "Docker daemon запущен"
|
|
||||||
$DockerRunning = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
$DockerRunning = $false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $DockerRunning -and -not $SkipDockerCheck) {
|
|
||||||
Write-Warning "Docker установлен, но не запущен!"
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "🚀 Запустите Docker Desktop и дождитесь, пока:" -ForegroundColor Yellow
|
|
||||||
Write-Host " - Появится зелёная иконка 🐳 в трее" -ForegroundColor White
|
|
||||||
Write-Host " - Или надпись 'Docker Desktop is running'" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
$waitForDocker = Read-Host "Ожидать запуска Docker? (y/n)"
|
|
||||||
if ($waitForDocker -eq "y" -or $waitForDocker -eq "Y") {
|
|
||||||
Write-Info "Ожидание запуска Docker..."
|
|
||||||
$attempts = 0
|
|
||||||
$maxAttempts = 60 # 5 минут макс
|
|
||||||
|
|
||||||
while (-not $DockerRunning -and $attempts -lt $maxAttempts) {
|
|
||||||
Start-Sleep -Seconds 5
|
|
||||||
$attempts++
|
|
||||||
Write-Host "." -NoNewline
|
|
||||||
|
|
||||||
try {
|
|
||||||
$dockerInfo = docker info 2>&1
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
$DockerRunning = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {}
|
|
||||||
}
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
if ($DockerRunning) {
|
|
||||||
Write-Success "Docker запустился!"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Error "Таймаут ожидания Docker. Запустите Docker Desktop вручную."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Проверяем Docker Compose
|
|
||||||
if ($DockerRunning) {
|
|
||||||
try {
|
|
||||||
$composeVersion = docker compose version 2>&1
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
Write-Success "Docker Compose доступен: $composeVersion"
|
|
||||||
$DockerComposeAvailable = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {}
|
|
||||||
|
|
||||||
if (-not $DockerComposeAvailable) {
|
|
||||||
# Пробуем старый формат docker-compose
|
|
||||||
try {
|
|
||||||
$composeVersion = docker-compose --version 2>&1
|
|
||||||
if ($LASTEXITCODE -eq 0) {
|
|
||||||
Write-Success "Docker Compose (legacy) доступен: $composeVersion"
|
|
||||||
$DockerComposeAvailable = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $DockerComposeAvailable -and -not $SkipDockerCheck) {
|
|
||||||
Write-Error "Docker Compose не найден!"
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Docker Compose обычно входит в Docker Desktop." -ForegroundColor Yellow
|
|
||||||
Write-Host "Попробуйте переустановить Docker Desktop." -ForegroundColor Yellow
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ШАГ 2: ПРОВЕРКА ФАЙЛОВ ПРОЕКТА
|
|
||||||
# ==========================================
|
|
||||||
Write-Step "Проверка файлов проекта..."
|
|
||||||
|
|
||||||
$dockerComposeFile = Join-Path $ScriptDir "docker-compose.yml"
|
|
||||||
$dockerDir = Join-Path $ScriptDir "docker"
|
|
||||||
$dockerFile = Join-Path $dockerDir "Dockerfile.singbox"
|
|
||||||
|
|
||||||
if (-not (Test-Path $dockerComposeFile)) {
|
|
||||||
Write-Error "Не найден docker-compose.yml в $ScriptDir"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-Success "docker-compose.yml найден"
|
|
||||||
|
|
||||||
if (-not (Test-Path $dockerFile)) {
|
|
||||||
Write-Error "Не найден docker/Dockerfile.singbox"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-Success "Dockerfile.singbox найден"
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ШАГ 3: СБОРКА И ЗАПУСК
|
|
||||||
# ==========================================
|
|
||||||
Write-Step "Сборка Docker-контейнера..."
|
|
||||||
|
|
||||||
# Переходим в директорию проекта
|
|
||||||
Push-Location $ScriptDir
|
|
||||||
|
|
||||||
try {
|
|
||||||
# Останавливаем старый контейнер если есть
|
|
||||||
Write-Info "Останавливаем старый контейнер (если есть)..."
|
|
||||||
docker compose down 2>&1 | Out-Null
|
|
||||||
|
|
||||||
# Собираем образ
|
|
||||||
Write-Info "Сборка образа (может занять 1-2 минуты)..."
|
|
||||||
$buildOutput = docker compose build 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "Ошибка сборки Docker образа!"
|
|
||||||
Write-Host $buildOutput -ForegroundColor Red
|
|
||||||
Pop-Location
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-Success "Образ собран"
|
|
||||||
|
|
||||||
# Запускаем контейнер
|
|
||||||
Write-Info "Запуск контейнера..."
|
|
||||||
$upOutput = docker compose up -d 2>&1
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "Ошибка запуска контейнера!"
|
|
||||||
Write-Host $upOutput -ForegroundColor Red
|
|
||||||
Pop-Location
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-Success "Контейнер запущен"
|
|
||||||
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Pop-Location
|
|
||||||
}
|
|
||||||
|
|
||||||
# Ждём немного, чтобы контейнер инициализировался
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
|
|
||||||
# Проверяем статус
|
|
||||||
Write-Step "Проверка статуса..."
|
|
||||||
$containerStatus = docker ps --filter "name=sing-proxy" --format "{{.Status}}" 2>&1
|
|
||||||
if ($containerStatus -match "Up") {
|
|
||||||
Write-Success "Контейнер sing-proxy работает: $containerStatus"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Warning "Контейнер может ещё запускаться. Проверьте через: docker ps"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ШАГ 4: ИНФОРМАЦИЯ О ПОДКЛЮЧЕНИИ
|
|
||||||
# ==========================================
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host " 🎉 УСТАНОВКА ЗАВЕРШЕНА! " -ForegroundColor Green
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📡 Адреса подключения:" -ForegroundColor Yellow
|
|
||||||
Write-Host " Веб-интерфейс: http://localhost:3456" -ForegroundColor White
|
|
||||||
Write-Host " HTTP/SOCKS Прокси: 127.0.0.1:8080" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📋 Следующие шаги:" -ForegroundColor Yellow
|
|
||||||
Write-Host " 1. Откройте http://localhost:3456 в браузере" -ForegroundColor White
|
|
||||||
Write-Host " 2. Вставьте VLESS ссылку или URL подписки" -ForegroundColor White
|
|
||||||
Write-Host " 3. Нажмите 'Применить'" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ШАГ 5: УСТАНОВКА СКРИПТА ДЛЯ DISCORD
|
|
||||||
# ==========================================
|
|
||||||
if (-not $SkipDiscord) {
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "🎮 Хотите установить ProxiFyre для Discord/Vesktop?" -ForegroundColor Yellow
|
|
||||||
Write-Host " Это позволит направить трафик Discord через прокси" -ForegroundColor Gray
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host " [1] Да, установить для Discord" -ForegroundColor White
|
|
||||||
Write-Host " [2] Да, установить для Vesktop" -ForegroundColor White
|
|
||||||
Write-Host " [3] Да, установить для Discord + Vesktop" -ForegroundColor White
|
|
||||||
Write-Host " [n] Нет, пропустить" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
$discordChoice = Read-Host "👉 Выбор (1-3/n)"
|
|
||||||
|
|
||||||
if ($discordChoice -match "^[123]$") {
|
|
||||||
# Проверяем права администратора
|
|
||||||
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
|
|
||||||
|
|
||||||
if (-not $isAdmin) {
|
|
||||||
Write-Warning "Для установки ProxiFyre нужны права администратора!"
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Запустите этот скрипт от имени администратора," -ForegroundColor Yellow
|
|
||||||
Write-Host "или запустите discord-windows-hack.ps1 отдельно:" -ForegroundColor Yellow
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host " 1. Откройте PowerShell от имени Администратора" -ForegroundColor White
|
|
||||||
Write-Host " 2. Выполните:" -ForegroundColor White
|
|
||||||
Write-Host " cd `"$ScriptDir`"" -ForegroundColor Cyan
|
|
||||||
Write-Host " .\discord-windows-hack.ps1" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Запускаем скрипт с параметрами для неинтерактивного режима
|
|
||||||
$discordScript = Join-Path $ScriptDir "discord-windows-hack.ps1"
|
|
||||||
|
|
||||||
if (Test-Path $discordScript) {
|
|
||||||
Write-Step "Запуск установки ProxiFyre для Discord..."
|
|
||||||
|
|
||||||
# Определяем целевые приложения
|
|
||||||
$targetApps = switch ($discordChoice) {
|
|
||||||
"1" { @("Discord") }
|
|
||||||
"2" { @("Vesktop") }
|
|
||||||
"3" { @("Vesktop", "Discord") }
|
|
||||||
}
|
|
||||||
|
|
||||||
# Вызываем discord-windows-hack.ps1 с параметрами (без дублирования кода)
|
|
||||||
& $discordScript -Proxy "127.0.0.1:8080" -Apps $targetApps -Force
|
|
||||||
|
|
||||||
# Предупреждение о UDP
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Yellow
|
|
||||||
Write-Host " ⚠️ ВАЖНО: ГОЛОСОВЫЕ ЗВОНКИ DISCORD " -ForegroundColor Yellow
|
|
||||||
Write-Host "==========================================" -ForegroundColor Yellow
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Docker Desktop на Windows НЕ ПОДДЕРЖИВАЕТ UDP!" -ForegroundColor Red
|
|
||||||
Write-Host "Голосовые звонки Discord могут не работать." -ForegroundColor Red
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Для полной поддержки голоса запустите:" -ForegroundColor Cyan
|
|
||||||
Write-Host " .\setup-singbox-native.ps1" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Этот скрипт установит нативный sing-box," -ForegroundColor Gray
|
|
||||||
Write-Host "который поддерживает UDP трафик." -ForegroundColor Gray
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Warning "Скрипт discord-windows-hack.ps1 не найден"
|
|
||||||
Write-Info "Скачайте его или создайте вручную"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Info "Установка Discord прокси пропущена"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ФИНАЛЬНЫЕ ИНСТРУКЦИИ
|
|
||||||
# ==========================================
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host " 📋 ПОЛЕЗНЫЕ КОМАНДЫ " -ForegroundColor Cyan
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "🐳 Docker:" -ForegroundColor Yellow
|
|
||||||
Write-Host " docker ps - статус контейнера" -ForegroundColor Gray
|
|
||||||
Write-Host " docker logs --tail 50 sing-proxy - логи VPN-прокси" -ForegroundColor Gray
|
|
||||||
Write-Host " docker compose restart - перезапуск" -ForegroundColor Gray
|
|
||||||
Write-Host " docker compose down - остановка" -ForegroundColor Gray
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "🛡️ ProxiFyre (если установлен):" -ForegroundColor Yellow
|
|
||||||
Write-Host " Get-Content C:\Tools\ProxiFyre\app-config.json - конфиг" -ForegroundColor Gray
|
|
||||||
Write-Host " Get-ChildItem C:\Tools\ProxiFyre\logs - список логов" -ForegroundColor Gray
|
|
||||||
Write-Host " Get-Content C:\Tools\ProxiFyre\logs\*.log -Tail 50 - последние логи" -ForegroundColor Gray
|
|
||||||
Write-Host " Get-Service ProxiFyreService - статус службы" -ForegroundColor Gray
|
|
||||||
Write-Host " Restart-Service ProxiFyreService - перезапуск службы" -ForegroundColor Gray
|
|
||||||
Write-Host ""
|
|
||||||
65
manage.ps1
Normal file
65
manage.ps1
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# ==========================================
|
||||||
|
# 🚀 VPN PROXY CONTROL CENTER (WINDOWS)
|
||||||
|
# ==========================================
|
||||||
|
# Главный скрипт управления. Запускать от имени Администратора.
|
||||||
|
|
||||||
|
$ScriptDir = if ($PSScriptRoot) { $PSScriptRoot } else { Split-Path -Parent $MyInvocation.MyCommand.Path }
|
||||||
|
$LibDir = "$ScriptDir\scripts\lib"
|
||||||
|
|
||||||
|
# Проверка библиотек
|
||||||
|
if (!(Test-Path "$LibDir\Common.ps1")) {
|
||||||
|
Write-Host "❌ Ошибка: Не найдены библиотеки в $LibDir" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
. "$LibDir\Common.ps1"
|
||||||
|
. "$LibDir\System.ps1"
|
||||||
|
|
||||||
|
Ensure-Admin
|
||||||
|
|
||||||
|
while ($true) {
|
||||||
|
Write-Header "VPN PROXY CONTROL CENTER"
|
||||||
|
|
||||||
|
# --- СБОР СТАТУСОВ ---
|
||||||
|
|
||||||
|
# 1. Native Sing-box
|
||||||
|
$sbStatus = Get-TaskStatus -Name "SingBoxProxy"
|
||||||
|
$sbStr = if ($sbStatus -eq "Running") { "РАБОТАЕТ" } else { "ОСТАНОВЛЕН" }
|
||||||
|
$sbColor = if ($sbStatus -eq "Running") { "Green" } else { "Yellow" }
|
||||||
|
if (!$sbStatus) { $sbStr = "НЕ УСТАНОВЛЕН"; $sbColor = "Gray" }
|
||||||
|
|
||||||
|
# 2. Discord Proxy
|
||||||
|
$discSvc = Get-Service -Name "ProxiFyreService" -ErrorAction SilentlyContinue
|
||||||
|
$discStr = if ($discSvc.Status -eq 'Running') { "АКТИВЕН" } else { "НЕ АКТИВЕН" }
|
||||||
|
$discColor = if ($discSvc.Status -eq 'Running') { "Green" } else { "Gray" }
|
||||||
|
|
||||||
|
# --- ОТРИСОВКА МЕНЮ ---
|
||||||
|
|
||||||
|
Write-Host " [1] 📦 VPN Клиент (Sing-box)" -NoNewline -ForegroundColor White
|
||||||
|
Write-Host " [$sbStr]" -ForegroundColor $sbColor
|
||||||
|
Write-Host " Основной способ. Поддерживает UDP и игры." -ForegroundColor Gray
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host " [2] 🎮 Настройка Discord/Vesktop" -NoNewline -ForegroundColor White
|
||||||
|
Write-Host " [$discStr]" -ForegroundColor $discColor
|
||||||
|
Write-Host " Маршрутизация приложений через прокси." -ForegroundColor Gray
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host " ---------------------------------------" -ForegroundColor DarkGray
|
||||||
|
|
||||||
|
|
||||||
|
Write-Host " [3] 🔄 Обновить статус" -ForegroundColor White
|
||||||
|
Write-Host " [U] ❌ Удалить всё (Uninstall)" -ForegroundColor Red
|
||||||
|
Write-Host " [q] Выход" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$choice = Read-Host "👉 Ваш выбор"
|
||||||
|
|
||||||
|
switch ($choice) {
|
||||||
|
"1" { & "$ScriptDir\scripts\setup-singbox.ps1" }
|
||||||
|
"2" { & "$ScriptDir\scripts\setup-discord.ps1" }
|
||||||
|
"3" { continue }
|
||||||
|
"u" { & "$ScriptDir\scripts\uninstall-all.ps1" }
|
||||||
|
"q" { exit }
|
||||||
|
}
|
||||||
|
}
|
||||||
59
scripts/lib/Common.ps1
Normal file
59
scripts/lib/Common.ps1
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# ==========================================
|
||||||
|
# 🛠️ COMMON UTILS
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
# --- ЦВЕТА И ВЫВОД ---
|
||||||
|
|
||||||
|
function Write-Step { param($msg) Write-Host "`n📦 $msg" -ForegroundColor Cyan }
|
||||||
|
function Write-Success { param($msg) Write-Host " ✅ $msg" -ForegroundColor Green }
|
||||||
|
function Write-Warning { param($msg) Write-Host " ⚠️ $msg" -ForegroundColor Yellow }
|
||||||
|
function Write-Error { param($msg) Write-Host " ❌ $msg" -ForegroundColor Red }
|
||||||
|
function Write-Info { param($msg) Write-Host " ℹ️ $msg" -ForegroundColor Gray }
|
||||||
|
|
||||||
|
function Write-Header {
|
||||||
|
param($Title)
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "==========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host " $Title" -ForegroundColor Cyan
|
||||||
|
Write-Host "==========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- ПОЛЕЗНЫЕ ФУНКЦИИ ---
|
||||||
|
|
||||||
|
function Get-ScriptDirectory {
|
||||||
|
if ($PSScriptRoot) { return $PSScriptRoot }
|
||||||
|
return Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
function Ensure-Admin {
|
||||||
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
|
||||||
|
if (-not $isAdmin) {
|
||||||
|
Write-Host "⛔ Требуются права АДМИНИСТРАТОРА!" -ForegroundColor Red
|
||||||
|
Write-Host " Пожалуйста, запустите скрипт от имени администратора." -ForegroundColor Gray
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Show-Menu {
|
||||||
|
param(
|
||||||
|
[string]$Title,
|
||||||
|
[System.Collections.Specialized.OrderedDictionary]$Options,
|
||||||
|
[string]$Prompt = "👉 Ваш выбор"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($Title) {
|
||||||
|
Write-Host "`n$Title" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = $Options.Keys
|
||||||
|
foreach ($key in $keys) {
|
||||||
|
Write-Host " [$key] $($Options[$key])" -ForegroundColor White
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
return Read-Host "$Prompt"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
140
scripts/lib/Net.ps1
Normal file
140
scripts/lib/Net.ps1
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# ==========================================
|
||||||
|
# 🌐 NET UTILS
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
# --- CONFIG ---
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
|
|
||||||
|
# --- ФУНКЦИИ ---
|
||||||
|
|
||||||
|
$script:HwidFile = "C:\Tools\sing-box\hwid"
|
||||||
|
$script:AppName = "VPN-Proxy-Control by Dokril"
|
||||||
|
|
||||||
|
function Get-HWID {
|
||||||
|
# Генерация или чтение HWID из файла
|
||||||
|
if (Test-Path $script:HwidFile) {
|
||||||
|
return (Get-Content $script:HwidFile -Raw).Trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Генерируем новый HWID
|
||||||
|
$hwid = [Guid]::NewGuid().ToString("N").Substring(0, 16)
|
||||||
|
|
||||||
|
# Сохраняем
|
||||||
|
$dir = Split-Path $script:HwidFile -Parent
|
||||||
|
if (!(Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
||||||
|
Set-Content -Path $script:HwidFile -Value $hwid
|
||||||
|
|
||||||
|
return $hwid
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SubscriptionHeaders {
|
||||||
|
# Формируем заголовки как в server.py
|
||||||
|
$osName = "windows"
|
||||||
|
$osVersion = [Environment]::OSVersion.Version.ToString()
|
||||||
|
|
||||||
|
return @{
|
||||||
|
"User-Agent" = "singbox"
|
||||||
|
"x-hwid" = (Get-HWID)
|
||||||
|
"x-device-os" = $osName
|
||||||
|
"x-ver-os" = $osVersion
|
||||||
|
"x-device-model" = $script:AppName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Download-File {
|
||||||
|
param(
|
||||||
|
[string]$Url,
|
||||||
|
[string]$Destination,
|
||||||
|
[string]$UserAgent = "VPN-Proxy-Installer"
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$req = [System.Net.HttpWebRequest]::Create($Url)
|
||||||
|
$req.UserAgent = $UserAgent
|
||||||
|
$resp = $req.GetResponse()
|
||||||
|
|
||||||
|
$stream = $resp.GetResponseStream()
|
||||||
|
$fs = [System.IO.File]::Create($Destination)
|
||||||
|
$msgLen = $resp.ContentLength
|
||||||
|
|
||||||
|
$buffer = New-Object byte[] 10240
|
||||||
|
$count = 0
|
||||||
|
$total = 0
|
||||||
|
|
||||||
|
do {
|
||||||
|
$count = $stream.Read($buffer, 0, $buffer.Length)
|
||||||
|
$fs.Write($buffer, 0, $count)
|
||||||
|
$total += $count
|
||||||
|
# Можно добавить прогресс бар, но пока просто качаем
|
||||||
|
} while ($count -gt 0)
|
||||||
|
|
||||||
|
$fs.Close()
|
||||||
|
$stream.Close()
|
||||||
|
$resp.Close()
|
||||||
|
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Ошибка скачивания: $_"
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SubscriptionData {
|
||||||
|
param(
|
||||||
|
[string]$Url,
|
||||||
|
[string]$UserAgent = "singbox",
|
||||||
|
$Headers = @{}
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Info "Загружаю подписку..."
|
||||||
|
|
||||||
|
$rawContent = $null
|
||||||
|
$userInfo = @{}
|
||||||
|
|
||||||
|
# 1. Получаем ответ
|
||||||
|
try {
|
||||||
|
$response = Invoke-WebRequest -Uri $Url -Headers $Headers -TimeoutSec 15 -UseBasicParsing
|
||||||
|
$rawContent = $response.Content
|
||||||
|
|
||||||
|
# Парсим subscription-userinfo header
|
||||||
|
$userInfoHeader = $response.Headers["subscription-userinfo"]
|
||||||
|
if ($userInfoHeader) {
|
||||||
|
$parts = $userInfoHeader -split ";"
|
||||||
|
foreach ($part in $parts) {
|
||||||
|
if ($part -match "(\w+)=(\d+)") {
|
||||||
|
$userInfo[$matches[1]] = [int64]$matches[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return @{
|
||||||
|
success = $false
|
||||||
|
error = "Ошибка загрузки: $($_.Exception.Message)"
|
||||||
|
rawContent = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Пробуем парсить как JSON
|
||||||
|
try {
|
||||||
|
$config = $rawContent | ConvertFrom-Json
|
||||||
|
return @{
|
||||||
|
success = $true
|
||||||
|
config = $config
|
||||||
|
rawContent = $rawContent
|
||||||
|
userInfo = $userInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
# JSON не распарсился — возвращаем rawContent для дальнейшей обработки
|
||||||
|
return @{
|
||||||
|
success = $false
|
||||||
|
error = "Ответ не в формате JSON (возможно Base64 или список ссылок)"
|
||||||
|
rawContent = $rawContent
|
||||||
|
userInfo = $userInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
156
scripts/lib/System.ps1
Normal file
156
scripts/lib/System.ps1
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
# ==========================================
|
||||||
|
# 🖥️ SYSTEM UTILS
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
# --- СИСТЕМНАЯ ИНФОРМАЦИЯ ---
|
||||||
|
|
||||||
|
function Get-HWID {
|
||||||
|
param([string]$StoreDir)
|
||||||
|
|
||||||
|
$hwidFile = "$StoreDir\hwid"
|
||||||
|
if (Test-Path $hwidFile) {
|
||||||
|
return (Get-Content $hwidFile -Raw).Trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
$hwid = [System.Guid]::NewGuid().ToString("N").Substring(0, 16)
|
||||||
|
|
||||||
|
if (!(Test-Path $StoreDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $StoreDir -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Content -Path $hwidFile -Value $hwid
|
||||||
|
return $hwid
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-SystemInfo {
|
||||||
|
return @{
|
||||||
|
os = "windows"
|
||||||
|
version = [System.Environment]::OSVersion.Version.Major.ToString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- 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
|
||||||
|
}
|
||||||
100
scripts/setup-discord.ps1
Normal file
100
scripts/setup-discord.ps1
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# ==========================================
|
||||||
|
# 🎮 DISCORD PROXY SETUP
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
param([switch]$Force)
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
. "$ScriptDir\lib\Common.ps1"
|
||||||
|
. "$ScriptDir\lib\Net.ps1"
|
||||||
|
. "$ScriptDir\lib\System.ps1"
|
||||||
|
|
||||||
|
Write-Header "НАСТРОЙКА DISCORD / VESKTOP"
|
||||||
|
|
||||||
|
Ensure-Admin
|
||||||
|
|
||||||
|
$InstallPath = "C:\Tools\ProxiFyre"
|
||||||
|
$DriverUrl = "https://github.com/wiresock/ndisapi/releases/download/v3.6.2/Windows.Packet.Filter.3.6.2.1.x64.msi"
|
||||||
|
$AppUrl = "https://github.com/wiresock/proxifyre/releases/download/v2.1.4/ProxiFyre-v2.1.4-x64-signed.zip"
|
||||||
|
|
||||||
|
# Проверка Sing-box
|
||||||
|
$singboxStatus = Get-TaskStatus -Name "SingBoxProxy"
|
||||||
|
$localProxy = "127.0.0.1:1080"
|
||||||
|
$useLocal = $false
|
||||||
|
|
||||||
|
if ($singboxStatus -eq "Running") {
|
||||||
|
Write-Info "Обнаружен работающий Native Sing-box."
|
||||||
|
$useLocal = $true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Warning "Native Sing-box не запущен!"
|
||||||
|
Write-Host " Для работы голосовых звонков он необходим." -ForegroundColor Gray
|
||||||
|
Write-Host " Вы можете вернуться в главное меню и настроить его." -ForegroundColor Gray
|
||||||
|
if ((Read-Host " Продолжить настройку БЕЗ него (голос может не работать)? (y/n)") -ne 'y') { exit }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Меню выбора приложений
|
||||||
|
Write-Host "`n🎮 Какие приложения проксировать?" -ForegroundColor Yellow
|
||||||
|
$appOpts = [Ordered]@{
|
||||||
|
"1" = "Discord"
|
||||||
|
"2" = "Vesktop"
|
||||||
|
"3" = "Discord + Vesktop"
|
||||||
|
}
|
||||||
|
$appChoice = Show-Menu -Options $appOpts
|
||||||
|
$targetApps = switch ($appChoice) {
|
||||||
|
"1" { @("Discord") }
|
||||||
|
"2" { @("Vesktop") }
|
||||||
|
"3" { @("Vesktop", "Discord") }
|
||||||
|
default { @("Discord") }
|
||||||
|
}
|
||||||
|
|
||||||
|
$proxyAddr = $localProxy
|
||||||
|
if (!$useLocal) {
|
||||||
|
$proxyAddr = Read-Host "Введите адрес прокси (хост:порт) [Enter для $localProxy]"
|
||||||
|
if ([string]::IsNullOrWhiteSpace($proxyAddr)) { $proxyAddr = $localProxy }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Установка драйвера
|
||||||
|
Write-Step "Установка драйвера..."
|
||||||
|
$msi = "$env:TEMP\WinpkFilter.msi"
|
||||||
|
if (Download-File -Url $DriverUrl -Destination $msi) {
|
||||||
|
Start-Process msiexec.exe -ArgumentList "/i `"$msi`" /qn /norestart" -Wait
|
||||||
|
Write-Success "Драйвер готов"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Установка ProxiFyre
|
||||||
|
Write-Step "Установка ProxiFyre..."
|
||||||
|
if (!(Test-Path $InstallPath) -or $Force) {
|
||||||
|
New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null
|
||||||
|
$zip = "$env:TEMP\ProxiFyre.zip"
|
||||||
|
if (Download-File -Url $AppUrl -Destination $zip) {
|
||||||
|
Expand-Archive -Path $zip -DestinationPath $InstallPath -Force
|
||||||
|
# Handle update folder structure if needed, simplified here assuming flat or check generic
|
||||||
|
$exe = Get-ChildItem $InstallPath -Recurse -Filter "ProxiFyre.exe" | Select -First 1
|
||||||
|
if ($exe.DirectoryName -ne $InstallPath) {
|
||||||
|
Copy-Item "$($exe.DirectoryName)\*" $InstallPath -Recurse -Force
|
||||||
|
}
|
||||||
|
Write-Success "Распаковано"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Конфиг
|
||||||
|
$cfg = @{
|
||||||
|
logLevel = "Info"
|
||||||
|
proxies = @(@{
|
||||||
|
appNames = $targetApps
|
||||||
|
socks5ProxyEndpoint = $proxyAddr
|
||||||
|
supportedProtocols = @("TCP", "UDP")
|
||||||
|
})
|
||||||
|
excludes = @()
|
||||||
|
}
|
||||||
|
$cfg | ConvertTo-Json -Depth 5 | Set-Content "$InstallPath\app-config.json" -Encoding UTF8
|
||||||
|
|
||||||
|
# Служба
|
||||||
|
Write-Step "Перезапуск службы..."
|
||||||
|
Start-Process "$InstallPath\ProxiFyre.exe" -ArgumentList "stop" -Wait -NoNewWindow
|
||||||
|
Start-Process "$InstallPath\ProxiFyre.exe" -ArgumentList "install" -Wait -NoNewWindow
|
||||||
|
Start-Process "$InstallPath\ProxiFyre.exe" -ArgumentList "start" -Wait -NoNewWindow
|
||||||
|
|
||||||
|
Write-Success "Готово! Discord должен работать через прокси."
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
414
scripts/setup-singbox.ps1
Normal file
414
scripts/setup-singbox.ps1
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
# ==========================================
|
||||||
|
# 📦 SING-BOX NATIVE INSTALLER
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$Force,
|
||||||
|
[string]$SubscriptionUrl = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
. "$ScriptDir\lib\Common.ps1"
|
||||||
|
. "$ScriptDir\lib\Net.ps1"
|
||||||
|
. "$ScriptDir\lib\System.ps1"
|
||||||
|
|
||||||
|
# --- CONFIG ---
|
||||||
|
$SingboxVersion = "1.11.4"
|
||||||
|
$InstallDir = "C:\Tools\sing-box"
|
||||||
|
$LocalProxyPort = 1080
|
||||||
|
$SingboxUrl = "https://github.com/SagerNet/sing-box/releases/download/v$SingboxVersion/sing-box-$SingboxVersion-windows-amd64.zip"
|
||||||
|
$TaskName = "SingBoxProxy"
|
||||||
|
|
||||||
|
Ensure-Admin
|
||||||
|
|
||||||
|
# --- LOGIC ---
|
||||||
|
|
||||||
|
function Select-Server {
|
||||||
|
param($Config)
|
||||||
|
|
||||||
|
$outbounds = $Config.outbounds
|
||||||
|
$servers = @()
|
||||||
|
|
||||||
|
foreach ($outbound in $outbounds) {
|
||||||
|
if ($outbound.type -in @("vless", "vmess", "trojan", "shadowsocks", "hysteria2")) {
|
||||||
|
$servers += @{
|
||||||
|
tag = $outbound.tag
|
||||||
|
type = $outbound.type
|
||||||
|
server = $outbound.server
|
||||||
|
server_port = $outbound.server_port
|
||||||
|
outbound = $outbound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($servers.Count -eq 0) {
|
||||||
|
Write-Error "Серверы не найдены в подписке!"
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = [Ordered]@{}
|
||||||
|
for ($i = 0; $i -lt $servers.Count; $i++) {
|
||||||
|
$s = $servers[$i]
|
||||||
|
$options["$($i+1)"] = "$($s.tag) ($($s.server):$($s.server_port))"
|
||||||
|
}
|
||||||
|
|
||||||
|
$choice = Show-Menu -Title "🌐 Доступные серверы" -Options $options -Prompt "👉 Выберите сервер (номер)"
|
||||||
|
$index = [int]$choice - 1
|
||||||
|
|
||||||
|
if ($index -lt 0 -or $index -ge $servers.Count) {
|
||||||
|
Write-Error "Неверный выбор!"
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
return $servers[$index]
|
||||||
|
}
|
||||||
|
|
||||||
|
function New-SingboxConfig {
|
||||||
|
param($Outbound, $Port)
|
||||||
|
|
||||||
|
return @{
|
||||||
|
log = @{ level = "info"; timestamp = $true }
|
||||||
|
dns = @{ independent_cache = $true }
|
||||||
|
inbounds = @(
|
||||||
|
@{
|
||||||
|
type = "socks"
|
||||||
|
tag = "socks-in"
|
||||||
|
listen = "0.0.0.0"
|
||||||
|
listen_port = $Port
|
||||||
|
}
|
||||||
|
)
|
||||||
|
outbounds = @(
|
||||||
|
$Outbound,
|
||||||
|
@{ type = "direct"; tag = "direct" }
|
||||||
|
)
|
||||||
|
route = @{
|
||||||
|
final = $Outbound.tag
|
||||||
|
auto_detect_interface = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Parse-VlessUrl {
|
||||||
|
param([string]$Url)
|
||||||
|
|
||||||
|
if (-not $Url.StartsWith("vless://")) { throw "URL должен начинаться с vless://" }
|
||||||
|
|
||||||
|
# Remove scheme
|
||||||
|
$raw = $Url.Substring(8)
|
||||||
|
|
||||||
|
# Split fragment
|
||||||
|
$tag = "reality"
|
||||||
|
if ($raw -match "#(.*)$") {
|
||||||
|
$tag = [System.Web.HttpUtility]::UrlDecode($matches[1])
|
||||||
|
$raw = $raw -replace "#.*$", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Split query
|
||||||
|
$queryStr = ""
|
||||||
|
if ($raw -match "\?(.*)$") {
|
||||||
|
$queryStr = $matches[1]
|
||||||
|
$raw = $raw -replace "\?.*$", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse UUID@HOST:PORT
|
||||||
|
if ($raw -notmatch "([^@]+)@([^:]+):(\d+)") { throw "Неверный формат vless (ожидается uuid@host:port)" }
|
||||||
|
$uuid = $matches[1][0]
|
||||||
|
$serverHost = $matches[2][0]
|
||||||
|
$port = [int]$matches[3][0] # Fix for regex object access in PS
|
||||||
|
|
||||||
|
if (-not $uuid) {
|
||||||
|
# Fallback if regex returns match info differently in different PS versions
|
||||||
|
$uuid = $matches[1]
|
||||||
|
$serverHost = $matches[2]
|
||||||
|
$port = [int]$matches[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Parse Query
|
||||||
|
$params = @{}
|
||||||
|
if ($queryStr) {
|
||||||
|
$parts = $queryStr -split "&"
|
||||||
|
foreach ($p in $parts) {
|
||||||
|
$kv = $p -split "="
|
||||||
|
if ($kv.Count -eq 2) {
|
||||||
|
$params[[System.Web.HttpUtility]::UrlDecode($kv[0])] = [System.Web.HttpUtility]::UrlDecode($kv[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract
|
||||||
|
$pbk = if ($params["pbk"]) { $params["pbk"] } else { throw "Отсутствует параметр pbk (Public Key)" }
|
||||||
|
$sid = if ($params["sid"]) { $params["sid"] } else { throw "Отсутствует параметр sid (Short ID)" }
|
||||||
|
$sni = if ($params["sni"]) { $params["sni"] } else { $serverHost }
|
||||||
|
$fp = if ($params["fp"]) { $params["fp"] } else { "chrome" }
|
||||||
|
$flow = if ($params["flow"]) { $params["flow"] } else { "" }
|
||||||
|
|
||||||
|
return @{
|
||||||
|
uuid = $uuid
|
||||||
|
server = $serverHost
|
||||||
|
server_port = $port
|
||||||
|
tag = $tag
|
||||||
|
public_key = $pbk
|
||||||
|
short_id = $sid
|
||||||
|
server_name = $sni
|
||||||
|
fingerprint = $fp
|
||||||
|
flow = $flow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- MAIN ---
|
||||||
|
|
||||||
|
Write-Header "NATIVE SING-BOX (UDP ПОДДЕРЖКА)"
|
||||||
|
|
||||||
|
$taskStatus = Get-TaskStatus -Name $TaskName
|
||||||
|
|
||||||
|
if ($taskStatus -and -not $Force) {
|
||||||
|
Write-Info "Sing-box уже установлен."
|
||||||
|
Write-Host " Статус: $taskStatus" -ForegroundColor ($taskStatus -eq "Running" ? "Green" : "Red")
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$opts = [Ordered]@{
|
||||||
|
"1" = "Сменить сервер (из подписки)"
|
||||||
|
"2" = "Ввести новую ссылку на подписку"
|
||||||
|
"3" = "Перезапустить службу"
|
||||||
|
"4" = "Остановить службу"
|
||||||
|
"5" = "Показать конфиг"
|
||||||
|
"6" = "Переустановить"
|
||||||
|
"b" = "Назад"
|
||||||
|
}
|
||||||
|
|
||||||
|
$act = Show-Menu -Options $opts
|
||||||
|
|
||||||
|
switch ($act) {
|
||||||
|
"1" {
|
||||||
|
# Reload existing sub logic could be added here, currently just re-runs install flow partially
|
||||||
|
# Simplification: treat as new setup but try to load saved sub url
|
||||||
|
$Force = $true
|
||||||
|
}
|
||||||
|
"2" { $SubscriptionUrl = ""; $Force = $true }
|
||||||
|
"3" { Manage-ScheduledTask -Name $TaskName -Action "Start"; Write-Success "Запущено!"; exit }
|
||||||
|
"4" { Manage-ScheduledTask -Name $TaskName -Action "Stop"; Write-Success "Остановлено!"; exit }
|
||||||
|
"5" { Get-Content "$InstallDir\config.json"; exit }
|
||||||
|
"6" { $Force = $true }
|
||||||
|
"b" { exit }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Force -or -not $taskStatus) {
|
||||||
|
# 1. Загрузка
|
||||||
|
Write-Step "Установка Sing-box..."
|
||||||
|
if (!(Test-Path "$InstallDir\sing-box.exe")) {
|
||||||
|
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||||||
|
$zipCtx = "$env:TEMP\sing-box.zip"
|
||||||
|
if (Download-File -Url $SingboxUrl -Destination $zipCtx) {
|
||||||
|
Expand-Archive -Path $zipCtx -DestinationPath $env:TEMP -Force
|
||||||
|
$extracted = Get-ChildItem "$env:TEMP\sing-box-*" -Directory | Select -First 1
|
||||||
|
Copy-Item "$($extracted.FullName)\sing-box.exe" "$InstallDir\sing-box.exe" -Force
|
||||||
|
Remove-Item $zipCtx; Remove-Item $extracted.FullName -Recurse -Force
|
||||||
|
Write-Success "Sing-box скачан"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Read-Host "Нажмите Enter для выхода..."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Подписка
|
||||||
|
if ([string]::IsNullOrWhiteSpace($SubscriptionUrl)) {
|
||||||
|
# Try load saved
|
||||||
|
$savedSub = "$InstallDir\sub_info.json"
|
||||||
|
if (Test-Path $savedSub) {
|
||||||
|
try {
|
||||||
|
$json = Get-Content $savedSub -Raw | ConvertFrom-Json
|
||||||
|
if ($json.url) {
|
||||||
|
Write-Info "Найдена сохраненная подписка: $($json.url)"
|
||||||
|
if ((Read-Host "Использовать? (y/n)") -eq 'y') { $SubscriptionUrl = $json.url }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($SubscriptionUrl)) {
|
||||||
|
$SubscriptionUrl = Read-Host "`n🔗 Введите URL подписки (VLESS)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($SubscriptionUrl)) {
|
||||||
|
Write-Error "Url не указан"
|
||||||
|
Read-Host "Нажмите Enter для выхода..."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- PARSING ---
|
||||||
|
$data = @{ success = $false; config = $null; error = "" }
|
||||||
|
|
||||||
|
if ($SubscriptionUrl.StartsWith("vless://")) {
|
||||||
|
try {
|
||||||
|
$p = Parse-VlessUrl -Url $SubscriptionUrl
|
||||||
|
$outbound = [Ordered]@{
|
||||||
|
type = "vless"
|
||||||
|
tag = $p.tag
|
||||||
|
server = $p.server
|
||||||
|
server_port = $p.server_port
|
||||||
|
uuid = $p.uuid
|
||||||
|
flow = $p.flow
|
||||||
|
tls = @{
|
||||||
|
enabled = $true
|
||||||
|
server_name = $p.server_name
|
||||||
|
utls = @{ enabled = $true; fingerprint = $p.fingerprint }
|
||||||
|
reality = @{
|
||||||
|
enabled = $true
|
||||||
|
public_key = $p.public_key
|
||||||
|
short_id = $p.short_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packet_encoding = "xudp"
|
||||||
|
}
|
||||||
|
$data.success = $true
|
||||||
|
$data.config = @{ outbounds = @($outbound) }
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$data.error = $_.Exception.Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$data = Get-SubscriptionData -Url $SubscriptionUrl -Headers (Get-SubscriptionHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# --- PARSING LOGIC ENHANCEMENT ---
|
||||||
|
if (-not $data.success) {
|
||||||
|
# Fallback: Try to handle non-JSON body (Base64 or Plain Text)
|
||||||
|
try {
|
||||||
|
Write-Info "JSON парсинг не удался, пробую как список ссылок..."
|
||||||
|
$content = $data.rawContent
|
||||||
|
|
||||||
|
# Base64 decode if needed
|
||||||
|
if ($content -match "^[A-Za-z0-9+/=]+$") {
|
||||||
|
try {
|
||||||
|
$bytes = [System.Convert]::FromBase64String($content)
|
||||||
|
$content = [System.Text.Encoding]::UTF8.GetString($bytes)
|
||||||
|
}
|
||||||
|
catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try to find vless:// links
|
||||||
|
$links = $content -split "[\r\n]+" | Where-Object { $_ -match "^vless://" }
|
||||||
|
|
||||||
|
if ($links.Count -gt 0) {
|
||||||
|
Write-Success "Найдено ссылок: $($links.Count)"
|
||||||
|
|
||||||
|
# Mock a config object with these links as "outbounds"
|
||||||
|
# Note: We can't fully parsing VLESS query params in pure PS easily without a lot of regex
|
||||||
|
# So we will try a simpler approach: Let sing-box do it? No, sing-box needs config.
|
||||||
|
|
||||||
|
# WORKAROUND: Create a minimal outbound for each link
|
||||||
|
# Parsing `vless://UUID@HOST:PORT?security=reality&...#NAME`
|
||||||
|
$parsedOutbounds = @()
|
||||||
|
|
||||||
|
foreach ($link in $links) {
|
||||||
|
if ($link -match "vless://([^@]+)@([^:]+):(\d+)(\?.*)?(#.*)?") {
|
||||||
|
$uuid = $matches[1]
|
||||||
|
$server = $matches[2]
|
||||||
|
$port = [int]$matches[3]
|
||||||
|
$query = $matches[4]
|
||||||
|
$hash = $matches[5]
|
||||||
|
|
||||||
|
$tag = if ($hash) { $hash.Substring(1) } else { "${server}:${port}" }
|
||||||
|
$tag = [System.Web.HttpUtility]::UrlDecode($tag)
|
||||||
|
|
||||||
|
# Parse Query Params
|
||||||
|
$flow = ""; $fp = ""; $pbk = ""; $sid = ""; $sni = ""; $serviceName = ""
|
||||||
|
|
||||||
|
if ($query) {
|
||||||
|
if ($query -match "flow=([^&]+)") { $flow = $matches[1] }
|
||||||
|
if ($query -match "fp=([^&]+)") { $fp = $matches[1] }
|
||||||
|
if ($query -match "pbk=([^&]+)") { $pbk = $matches[1] }
|
||||||
|
if ($query -match "sid=([^&]+)") { $sid = $matches[1] }
|
||||||
|
if ($query -match "sni=([^&]+)") { $sni = $matches[1] }
|
||||||
|
if ($query -match "serviceName=([^&]+)") { $serviceName = $matches[1] }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Construct Sing-box outbound (REALITY based assumption for modern vless)
|
||||||
|
$out = [Ordered]@{
|
||||||
|
type = "vless"
|
||||||
|
tag = $tag
|
||||||
|
server = $server
|
||||||
|
server_port = $port
|
||||||
|
uuid = $uuid
|
||||||
|
flow = $flow
|
||||||
|
tls = @{
|
||||||
|
enabled = $true
|
||||||
|
server_name = $sni
|
||||||
|
utls = @{ enabled = $true; fingerprint = $fp }
|
||||||
|
reality = @{
|
||||||
|
enabled = $true
|
||||||
|
public_key = $pbk
|
||||||
|
short_id = $sid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
packet_encoding = "xudp"
|
||||||
|
}
|
||||||
|
$parsedOutbounds += $out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parsedOutbounds.Count -gt 0) {
|
||||||
|
$data.success = $true
|
||||||
|
$data.config = @{ outbounds = $parsedOutbounds }
|
||||||
|
$data.error = $null
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw "Не удалось распарсить VLESS ссылки"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw $data.error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Ошибка обработки подписки: $_"
|
||||||
|
Write-Host " Скрипт поддерживает: SIP008 (JSON) или список VLESS+Reality ссылок." -ForegroundColor Yellow
|
||||||
|
Read-Host "Нажмите Enter для выхода..."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Save sub info
|
||||||
|
@{ url = $SubscriptionUrl } | ConvertTo-Json | Set-Content "$InstallDir\sub_info.json"
|
||||||
|
|
||||||
|
# 3. Выбор сервера
|
||||||
|
$server = Select-Server -Config $data.config
|
||||||
|
if (!$server) {
|
||||||
|
Read-Host "Нажмите Enter для выхода..."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Конфиг
|
||||||
|
$cfg = New-SingboxConfig -Outbound $server.outbound -Port $LocalProxyPort
|
||||||
|
$cfg | ConvertTo-Json -Depth 10 | Set-Content "$InstallDir\config.json" -Encoding UTF8
|
||||||
|
|
||||||
|
# 5. Задача
|
||||||
|
Manage-ScheduledTask -Name $TaskName -ExePath "$InstallDir\sing-box.exe" -Arguments "run -c `"$InstallDir\config.json`"" -WorkDir $InstallDir -Action "Install"
|
||||||
|
Manage-ScheduledTask -Name $TaskName -Action "Start"
|
||||||
|
|
||||||
|
# 6. Firewall
|
||||||
|
if (Ensure-FirewallPort -Port $LocalProxyPort -Name "SingBox-Proxy-Port") {
|
||||||
|
Write-Success "Правило Firewall создано (порт $LocalProxyPort)"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Success "Успешно установлено и запущено!"
|
||||||
|
Write-Info "Локальный прокси: 127.0.0.1:$LocalProxyPort"
|
||||||
|
|
||||||
|
$ips = Get-LocalIPs
|
||||||
|
if ($ips) {
|
||||||
|
Write-Info "Доступно из сети по адресам:"
|
||||||
|
foreach ($ip in $ips) {
|
||||||
|
Write-Host " ${ip}:$LocalProxyPort" -ForegroundColor Gray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
|
}
|
||||||
48
scripts/uninstall-all.ps1
Normal file
48
scripts/uninstall-all.ps1
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# ==========================================
|
||||||
|
# 🗑️ UNINSTALL ALL (CLEANUP)
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
. "$ScriptDir\lib\Common.ps1"
|
||||||
|
. "$ScriptDir\lib\System.ps1"
|
||||||
|
|
||||||
|
Write-Header "ПОЛНОЕ УДАЛЕНИЕ"
|
||||||
|
|
||||||
|
Ensure-Admin
|
||||||
|
|
||||||
|
Write-Warning "Это действие удалит весь установленный софт:"
|
||||||
|
Write-Host " - Sing-box (Служба и файлы)" -ForegroundColor Gray
|
||||||
|
Write-Host " - ProxiFyre (Служба и файлы)" -ForegroundColor Gray
|
||||||
|
Write-Host " - Драйвер WinPacketFilter" -ForegroundColor Gray
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
if ((Read-Host "Вы уверены? (y/n)") -ne 'y') { exit }
|
||||||
|
|
||||||
|
Write-Step "Удаление Sing-box..."
|
||||||
|
Manage-ScheduledTask -Name "SingBoxProxy" -Action "Stop"
|
||||||
|
Manage-ScheduledTask -Name "SingBoxProxy" -Action "Uninstall"
|
||||||
|
|
||||||
|
if (Test-Path "C:\Tools\sing-box") {
|
||||||
|
Remove-Item "C:\Tools\sing-box" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
Write-Success "Файлы удалены"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Step "Удаление Discrod Proxy (ProxiFyre)..."
|
||||||
|
$pfDir = "C:\Tools\ProxiFyre"
|
||||||
|
if (Test-Path "$pfDir\ProxiFyre.exe") {
|
||||||
|
Start-Process "$pfDir\ProxiFyre.exe" -ArgumentList "uninstall" -Wait -NoNewWindow
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path $pfDir) {
|
||||||
|
Remove-Item $pfDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
Write-Success "Файлы удалены"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Step "Удаление драйвера..."
|
||||||
|
# Тут сложно удалить MSI тихо без GUID, но попробуем через known path или пропустим, т.к. драйвер может быть нужен другим
|
||||||
|
Write-Info "Драйвер WinPacketFilter оставлен (он может использоваться другим ПО)."
|
||||||
|
Write-Info "Если нужно, удалите его через 'Установка и удаление программ'."
|
||||||
|
|
||||||
|
Write-Success "Очистка завершена!"
|
||||||
|
Start-Sleep -Seconds 3
|
||||||
@@ -1,439 +0,0 @@
|
|||||||
# ==========================================
|
|
||||||
# 🎯 SING-BOX NATIVE INSTALLER FOR WINDOWS
|
|
||||||
# ==========================================
|
|
||||||
# Устанавливает нативный sing-box для поддержки UDP (Discord голосовые)
|
|
||||||
# Используется когда Docker запущен ЛОКАЛЬНО на Windows
|
|
||||||
# (Docker Desktop не поддерживает UDP)
|
|
||||||
# ==========================================
|
|
||||||
|
|
||||||
param(
|
|
||||||
[switch]$Force, # Принудительная переустановка
|
|
||||||
[string]$SubscriptionUrl = "" # URL подписки (для неинтерактивного режима)
|
|
||||||
)
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# КОНФИГУРАЦИЯ
|
|
||||||
# ==========================================
|
|
||||||
$SingboxVersion = "1.11.4"
|
|
||||||
$InstallDir = "C:\Tools\sing-box"
|
|
||||||
$LocalProxyPort = 1080
|
|
||||||
|
|
||||||
# Ссылки для скачивания
|
|
||||||
$SingboxUrl = "https://github.com/SagerNet/sing-box/releases/download/v$SingboxVersion/sing-box-$SingboxVersion-windows-amd64.zip"
|
|
||||||
|
|
||||||
# App Info (для заголовков подписки)
|
|
||||||
$AppName = "VPN-Proxy-Control by Dokril"
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ЦВЕТА И ВЫВОД
|
|
||||||
# ==========================================
|
|
||||||
function Write-Step { param($msg) Write-Host "`n📦 $msg" -ForegroundColor Cyan }
|
|
||||||
function Write-Success { param($msg) Write-Host " ✅ $msg" -ForegroundColor Green }
|
|
||||||
function Write-Warning { param($msg) Write-Host " ⚠️ $msg" -ForegroundColor Yellow }
|
|
||||||
function Write-Error { param($msg) Write-Host " ❌ $msg" -ForegroundColor Red }
|
|
||||||
function Write-Info { param($msg) Write-Host " ℹ️ $msg" -ForegroundColor Gray }
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
|
|
||||||
# ==========================================
|
|
||||||
|
|
||||||
function Get-HWID {
|
|
||||||
$hwidFile = "$InstallDir\hwid"
|
|
||||||
if (Test-Path $hwidFile) {
|
|
||||||
return (Get-Content $hwidFile -Raw).Trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
$hwid = [System.Guid]::NewGuid().ToString("N").Substring(0, 16)
|
|
||||||
|
|
||||||
if (!(Test-Path $InstallDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Set-Content -Path $hwidFile -Value $hwid
|
|
||||||
return $hwid
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-SystemInfo {
|
|
||||||
return @{
|
|
||||||
os = "windows"
|
|
||||||
version = [System.Environment]::OSVersion.Version.Major.ToString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-SubscriptionData {
|
|
||||||
param([string]$Url)
|
|
||||||
|
|
||||||
Write-Info "Загружаю подписку..."
|
|
||||||
|
|
||||||
$hwid = Get-HWID
|
|
||||||
$sysInfo = Get-SystemInfo
|
|
||||||
|
|
||||||
$headers = @{
|
|
||||||
"User-Agent" = "singbox"
|
|
||||||
"x-hwid" = $hwid
|
|
||||||
"x-device-os" = $sysInfo.os
|
|
||||||
"x-ver-os" = $sysInfo.version
|
|
||||||
"x-device-model" = $AppName
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$response = Invoke-WebRequest -Uri $Url -Headers $headers -TimeoutSec 15 -UseBasicParsing
|
|
||||||
$config = $response.Content | ConvertFrom-Json
|
|
||||||
|
|
||||||
$userInfo = @{}
|
|
||||||
$userInfoHeader = $response.Headers["subscription-userinfo"]
|
|
||||||
if ($userInfoHeader) {
|
|
||||||
$parts = $userInfoHeader -split ";"
|
|
||||||
foreach ($part in $parts) {
|
|
||||||
if ($part -match "(\w+)=(\d+)") {
|
|
||||||
$userInfo[$matches[1]] = [int64]$matches[2]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return @{
|
|
||||||
success = $true
|
|
||||||
config = $config
|
|
||||||
userInfo = $userInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return @{
|
|
||||||
success = $false
|
|
||||||
error = $_.Exception.Message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Select-Server {
|
|
||||||
param($Config)
|
|
||||||
|
|
||||||
$outbounds = $Config.outbounds
|
|
||||||
$servers = @()
|
|
||||||
|
|
||||||
foreach ($outbound in $outbounds) {
|
|
||||||
if ($outbound.type -in @("vless", "vmess", "trojan", "shadowsocks", "hysteria2")) {
|
|
||||||
$servers += @{
|
|
||||||
tag = $outbound.tag
|
|
||||||
type = $outbound.type
|
|
||||||
server = $outbound.server
|
|
||||||
server_port = $outbound.server_port
|
|
||||||
outbound = $outbound
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($servers.Count -eq 0) {
|
|
||||||
Write-Error "Серверы не найдены в подписке!"
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`n🌐 Доступные серверы:" -ForegroundColor Yellow
|
|
||||||
for ($i = 0; $i -lt $servers.Count; $i++) {
|
|
||||||
$s = $servers[$i]
|
|
||||||
Write-Host " [$($i+1)] $($s.tag) ($($s.type)) - $($s.server):$($s.server_port)"
|
|
||||||
}
|
|
||||||
|
|
||||||
$choice = Read-Host "`n👉 Выберите сервер (1-$($servers.Count))"
|
|
||||||
$index = [int]$choice - 1
|
|
||||||
|
|
||||||
if ($index -lt 0 -or $index -ge $servers.Count) {
|
|
||||||
Write-Error "Неверный выбор!"
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
|
|
||||||
return $servers[$index]
|
|
||||||
}
|
|
||||||
|
|
||||||
function New-SingboxConfig {
|
|
||||||
param($Outbound, $Port)
|
|
||||||
|
|
||||||
$config = @{
|
|
||||||
log = @{
|
|
||||||
level = "info"
|
|
||||||
timestamp = $true
|
|
||||||
}
|
|
||||||
dns = @{
|
|
||||||
independent_cache = $true
|
|
||||||
}
|
|
||||||
inbounds = @(
|
|
||||||
@{
|
|
||||||
type = "socks"
|
|
||||||
tag = "socks-in"
|
|
||||||
listen = "127.0.0.1"
|
|
||||||
listen_port = $Port
|
|
||||||
}
|
|
||||||
)
|
|
||||||
outbounds = @(
|
|
||||||
$Outbound,
|
|
||||||
@{
|
|
||||||
type = "direct"
|
|
||||||
tag = "direct"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
route = @{
|
|
||||||
final = $Outbound.tag
|
|
||||||
auto_detect_interface = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $config
|
|
||||||
}
|
|
||||||
|
|
||||||
function Install-SingboxTask {
|
|
||||||
Write-Step "Установка sing-box через Task Scheduler..."
|
|
||||||
|
|
||||||
$exePath = "$InstallDir\sing-box.exe"
|
|
||||||
$configPath = "$InstallDir\config.json"
|
|
||||||
$taskName = "SingBoxProxy"
|
|
||||||
|
|
||||||
# Удаляем старую задачу
|
|
||||||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
# Создаём новую
|
|
||||||
$action = New-ScheduledTaskAction -Execute "$exePath" -Argument "run -c `"$configPath`"" -WorkingDirectory $InstallDir
|
|
||||||
$trigger = New-ScheduledTaskTrigger -AtStartup
|
|
||||||
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
|
||||||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1)
|
|
||||||
|
|
||||||
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
|
|
||||||
|
|
||||||
# Запускаем сразу
|
|
||||||
Start-ScheduledTask -TaskName $taskName
|
|
||||||
|
|
||||||
Write-Success "Задача создана и запущена!"
|
|
||||||
return $true
|
|
||||||
}
|
|
||||||
|
|
||||||
function Stop-SingboxTask {
|
|
||||||
Stop-ScheduledTask -TaskName "SingBoxProxy" -ErrorAction SilentlyContinue
|
|
||||||
Stop-Process -Name "sing-box" -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ПРОВЕРКА ПРАВ АДМИНИСТРАТОРА
|
|
||||||
# ==========================================
|
|
||||||
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
|
||||||
Write-Host "⛔ Запустите скрипт от имени АДМИНИСТРАТОРА!" -ForegroundColor Red
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
Exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Clear-Host
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host " 🎯 SING-BOX NATIVE INSTALLER " -ForegroundColor Cyan
|
|
||||||
Write-Host " для Discord UDP поддержки " -ForegroundColor Cyan
|
|
||||||
Write-Host "==========================================" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Этот скрипт нужен когда Docker запущен" -ForegroundColor Gray
|
|
||||||
Write-Host "ЛОКАЛЬНО на Windows (Docker Desktop)." -ForegroundColor Gray
|
|
||||||
Write-Host "Docker Desktop не поддерживает UDP." -ForegroundColor Gray
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# ПРОВЕРКА СУЩЕСТВУЮЩЕЙ УСТАНОВКИ
|
|
||||||
# ==========================================
|
|
||||||
$singboxExists = Test-Path "$InstallDir\sing-box.exe"
|
|
||||||
$taskExists = Get-ScheduledTask -TaskName "SingBoxProxy" -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($singboxExists -and $taskExists -and -not $Force) {
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host " ✅ SING-BOX УЖЕ УСТАНОВЛЕН! " -ForegroundColor Green
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
$taskInfo = Get-ScheduledTask -TaskName "SingBoxProxy"
|
|
||||||
$taskState = $taskInfo.State
|
|
||||||
|
|
||||||
Write-Host "📊 Статус: " -NoNewline -ForegroundColor Yellow
|
|
||||||
if ($taskState -eq "Running") {
|
|
||||||
Write-Host "РАБОТАЕТ" -ForegroundColor Green
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Host "$taskState" -ForegroundColor Red
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📁 Расположение: $InstallDir" -ForegroundColor Yellow
|
|
||||||
Write-Host "🔌 Локальный SOCKS5: 127.0.0.1:$LocalProxyPort" -ForegroundColor Yellow
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
Write-Host "Что вы хотите сделать?" -ForegroundColor Yellow
|
|
||||||
Write-Host " [1] Запустить" -ForegroundColor White
|
|
||||||
Write-Host " [2] Остановить" -ForegroundColor White
|
|
||||||
Write-Host " [3] Показать конфиг" -ForegroundColor White
|
|
||||||
Write-Host " [4] Переустановить" -ForegroundColor White
|
|
||||||
Write-Host " [q] Выход" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
|
|
||||||
$choice = Read-Host "👉 Выбор (1-4/q)"
|
|
||||||
|
|
||||||
switch ($choice) {
|
|
||||||
"1" {
|
|
||||||
Start-ScheduledTask -TaskName "SingBoxProxy"
|
|
||||||
Start-Sleep -Seconds 2
|
|
||||||
Write-Success "Запущено!"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"2" {
|
|
||||||
Stop-SingboxTask
|
|
||||||
Write-Success "Остановлено!"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"3" {
|
|
||||||
Write-Host "`n📄 Конфигурация:" -ForegroundColor Cyan
|
|
||||||
Get-Content "$InstallDir\config.json" | ConvertFrom-Json | ConvertTo-Json -Depth 10
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
"4" {
|
|
||||||
Write-Info "Переустановка..."
|
|
||||||
}
|
|
||||||
default {
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ==========================================
|
|
||||||
# УСТАНОВКА
|
|
||||||
# ==========================================
|
|
||||||
|
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
||||||
|
|
||||||
# ШАГ 1: Скачиваем sing-box
|
|
||||||
Write-Step "Скачиваю sing-box v$SingboxVersion..."
|
|
||||||
|
|
||||||
if (!(Test-Path $InstallDir)) {
|
|
||||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
$zipFile = "$env:TEMP\sing-box.zip"
|
|
||||||
|
|
||||||
try {
|
|
||||||
Invoke-WebRequest -Uri $SingboxUrl -OutFile $zipFile -UseBasicParsing
|
|
||||||
Write-Success "Скачано!"
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "Ошибка скачивания: $_"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Info "Распаковываю..."
|
|
||||||
Expand-Archive -Path $zipFile -DestinationPath $env:TEMP -Force
|
|
||||||
|
|
||||||
$extractedDir = Get-ChildItem "$env:TEMP\sing-box-*" -Directory | Select-Object -First 1
|
|
||||||
Copy-Item "$($extractedDir.FullName)\sing-box.exe" "$InstallDir\sing-box.exe" -Force
|
|
||||||
|
|
||||||
Remove-Item $zipFile -Force -ErrorAction SilentlyContinue
|
|
||||||
Remove-Item $extractedDir.FullName -Recurse -Force -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
Write-Success "sing-box установлен в $InstallDir"
|
|
||||||
|
|
||||||
# ШАГ 2: Получаем подписку
|
|
||||||
Write-Step "Настройка подписки..."
|
|
||||||
|
|
||||||
$savedSubFile = Join-Path $PSScriptRoot "data\subscription.json"
|
|
||||||
$subUrl = $SubscriptionUrl
|
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($subUrl) -and (Test-Path $savedSubFile)) {
|
|
||||||
try {
|
|
||||||
$savedSub = Get-Content $savedSubFile -Raw | ConvertFrom-Json
|
|
||||||
if ($savedSub.url) {
|
|
||||||
Write-Info "Найдена сохранённая подписка"
|
|
||||||
Write-Host " URL: $($savedSub.url)" -ForegroundColor Gray
|
|
||||||
$useSaved = Read-Host " Использовать? (y/n)"
|
|
||||||
if ($useSaved -eq "y") {
|
|
||||||
$subUrl = $savedSub.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($subUrl)) {
|
|
||||||
Write-Host "`n🔗 Введите URL подписки:" -ForegroundColor Yellow
|
|
||||||
$subUrl = Read-Host "👉"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($subUrl)) {
|
|
||||||
Write-Error "URL подписки не указан!"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$subResult = Get-SubscriptionData -Url $subUrl
|
|
||||||
|
|
||||||
if (-not $subResult.success) {
|
|
||||||
Write-Error "Ошибка загрузки подписки: $($subResult.error)"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Success "Подписка загружена!"
|
|
||||||
|
|
||||||
if ($subResult.userInfo.Count -gt 0) {
|
|
||||||
$ui = $subResult.userInfo
|
|
||||||
if ($ui.upload -and $ui.download -and $ui.total) {
|
|
||||||
$usedGB = [math]::Round(($ui.upload + $ui.download) / 1GB, 2)
|
|
||||||
$totalGB = [math]::Round($ui.total / 1GB, 2)
|
|
||||||
Write-Info "Трафик: $usedGB / $totalGB GB"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ШАГ 3: Выбираем сервер
|
|
||||||
$selectedServer = Select-Server -Config $subResult.config
|
|
||||||
|
|
||||||
if (-not $selectedServer) {
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Success "Выбран сервер: $($selectedServer.tag)"
|
|
||||||
|
|
||||||
# ШАГ 4: Генерируем конфиг
|
|
||||||
Write-Step "Генерирую конфигурацию..."
|
|
||||||
|
|
||||||
$config = New-SingboxConfig -Outbound $selectedServer.outbound -Port $LocalProxyPort
|
|
||||||
$configJson = $config | ConvertTo-Json -Depth 10
|
|
||||||
|
|
||||||
Set-Content -Path "$InstallDir\config.json" -Value $configJson -Encoding UTF8
|
|
||||||
Write-Success "Конфиг сохранён: $InstallDir\config.json"
|
|
||||||
|
|
||||||
# ШАГ 5: Запускаем
|
|
||||||
Stop-SingboxTask
|
|
||||||
Start-Sleep -Seconds 1
|
|
||||||
|
|
||||||
$installed = Install-SingboxTask
|
|
||||||
|
|
||||||
if (-not $installed) {
|
|
||||||
Write-Error "Не удалось установить задачу!"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Start-Sleep -Seconds 3
|
|
||||||
|
|
||||||
# Проверяем порт
|
|
||||||
Write-Info "Проверяю доступность порта..."
|
|
||||||
$portCheck = Test-NetConnection -ComputerName 127.0.0.1 -Port $LocalProxyPort -WarningAction SilentlyContinue
|
|
||||||
|
|
||||||
if ($portCheck.TcpTestSucceeded) {
|
|
||||||
Write-Success "Порт $LocalProxyPort доступен!"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Warning "Порт $LocalProxyPort пока недоступен. Подождите несколько секунд."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Финал
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host " 🎉 SING-BOX УСТАНОВЛЕН! " -ForegroundColor Green
|
|
||||||
Write-Host "==========================================" -ForegroundColor Green
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📡 Локальный SOCKS5: 127.0.0.1:$LocalProxyPort" -ForegroundColor Yellow
|
|
||||||
Write-Host " Сервер: $($selectedServer.tag)" -ForegroundColor White
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "Теперь запустите discord-windows-hack.ps1" -ForegroundColor Cyan
|
|
||||||
Write-Host "и укажите прокси: 127.0.0.1:$LocalProxyPort" -ForegroundColor Cyan
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "📋 Полезные команды:" -ForegroundColor Gray
|
|
||||||
Write-Host " Статус: Get-ScheduledTask -TaskName SingBoxProxy" -ForegroundColor Gray
|
|
||||||
Write-Host " Запустить: Start-ScheduledTask -TaskName SingBoxProxy" -ForegroundColor Gray
|
|
||||||
Write-Host " Остановить: Stop-ScheduledTask -TaskName SingBoxProxy" -ForegroundColor Gray
|
|
||||||
Write-Host ""
|
|
||||||
@@ -516,14 +516,16 @@ class ProxyControlHandler(http.server.BaseHTTPRequestHandler):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
config = None
|
||||||
|
config_text = ""
|
||||||
|
user_info = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(req, timeout=15) as response:
|
with urllib.request.urlopen(req, timeout=15) as response:
|
||||||
config_text = response.read().decode("utf-8")
|
config_text = response.read().decode("utf-8")
|
||||||
config = json.loads(config_text)
|
|
||||||
|
|
||||||
# Parse User Info header
|
# Parse User Info header
|
||||||
user_info_header = response.headers.get("subscription-userinfo", "")
|
user_info_header = response.headers.get("subscription-userinfo", "")
|
||||||
user_info = {}
|
|
||||||
if user_info_header:
|
if user_info_header:
|
||||||
parts = user_info_header.split(';')
|
parts = user_info_header.split(';')
|
||||||
for part in parts:
|
for part in parts:
|
||||||
@@ -541,6 +543,68 @@ class ProxyControlHandler(http.server.BaseHTTPRequestHandler):
|
|||||||
self.send_json({"success": False, "error": f"Ошибка подключения: {e.reason}"}, 400)
|
self.send_json({"success": False, "error": f"Ошибка подключения: {e.reason}"}, 400)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Try to parse as JSON first
|
||||||
|
try:
|
||||||
|
config = json.loads(config_text)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
# Not JSON - try Base64 decode or plain text VLESS links
|
||||||
|
content = config_text.strip()
|
||||||
|
|
||||||
|
# Try Base64 decode
|
||||||
|
import base64
|
||||||
|
import re
|
||||||
|
try:
|
||||||
|
# Check if it looks like Base64
|
||||||
|
if re.match(r'^[A-Za-z0-9+/=\s]+$', content):
|
||||||
|
decoded = base64.b64decode(content).decode('utf-8')
|
||||||
|
content = decoded
|
||||||
|
except Exception:
|
||||||
|
pass # Not Base64, continue with original content
|
||||||
|
|
||||||
|
# Parse VLESS links
|
||||||
|
lines = content.strip().split('\n')
|
||||||
|
vless_links = [line.strip() for line in lines if line.strip().startswith('vless://')]
|
||||||
|
|
||||||
|
if not vless_links:
|
||||||
|
self.send_json({"success": False, "error": "Не найдены VLESS ссылки в ответе"}, 400)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Parse each VLESS link and create outbounds
|
||||||
|
outbounds = []
|
||||||
|
for link in vless_links:
|
||||||
|
try:
|
||||||
|
params = parse_vless_url(link)
|
||||||
|
outbound = {
|
||||||
|
"type": "vless",
|
||||||
|
"tag": params['tag'],
|
||||||
|
"server": params['server'],
|
||||||
|
"server_port": params['server_port'],
|
||||||
|
"uuid": params['uuid'],
|
||||||
|
"flow": params['flow'],
|
||||||
|
"tls": {
|
||||||
|
"enabled": True,
|
||||||
|
"server_name": params['server_name'],
|
||||||
|
"utls": {"enabled": True, "fingerprint": params['fingerprint']},
|
||||||
|
"reality": {
|
||||||
|
"enabled": True,
|
||||||
|
"public_key": params['public_key'],
|
||||||
|
"short_id": params['short_id']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packet_encoding": "xudp"
|
||||||
|
}
|
||||||
|
outbounds.append(outbound)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WebUI] Failed to parse VLESS link: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not outbounds:
|
||||||
|
self.send_json({"success": False, "error": "Не удалось распарсить VLESS ссылки"}, 400)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create a mock config with parsed outbounds
|
||||||
|
config = {"outbounds": outbounds}
|
||||||
|
|
||||||
# Extract outbound servers
|
# Extract outbound servers
|
||||||
outbounds = config.get("outbounds", [])
|
outbounds = config.get("outbounds", [])
|
||||||
servers = []
|
servers = []
|
||||||
|
|||||||
Reference in New Issue
Block a user