- Add settings menu to switch between Xray and V2Ray cores for VLESS connections - Implement core type persistence in configuration with LoadSettings/SaveSettings - Add VLESS error and access log viewer showing last 30 and 20 lines respectively - Display current core type and system time in main menu - Update VLESS connection to use selected core dynamically - Refactor monitor.go to accept 'q' key input for graceful exit instead of signal handling - Add proxy platform-specific implementations (proxy_unix.go, proxy_windows.go) - Add downloader module for managing binary resources - Include V2Ray and Xray configuration files and geodata (geoip.dat, geosite.dat) - Update CLI imports to include path/filepath and time packages - Improve user experience with core selection visibility and log diagnostics
155 lines
4.6 KiB
Go
155 lines
4.6 KiB
Go
package vpn
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"runtime"
|
||
"syscall"
|
||
"time"
|
||
|
||
"vpn-client/internal/config"
|
||
"vpn-client/internal/logger"
|
||
"vpn-client/internal/proxy"
|
||
"vpn-client/internal/wireguard"
|
||
)
|
||
|
||
// Disconnect отключает VPN
|
||
func Disconnect(logsDir string) error {
|
||
// Загружаем состояние
|
||
state, err := config.LoadState()
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка загрузки состояния: %w", err)
|
||
}
|
||
|
||
if !state.Connected {
|
||
return fmt.Errorf("VPN не подключен")
|
||
}
|
||
|
||
fmt.Printf("Отключение от '%s'...\n", state.ConfigName)
|
||
|
||
// Логируем отключение
|
||
var logFile string
|
||
if state.ConfigType == "wireguard" {
|
||
logFile = logger.GetLogPath(logsDir, "wireguard")
|
||
} else if state.ConfigType == "vless" {
|
||
logFile = logger.GetLogPath(logsDir, "vless")
|
||
}
|
||
|
||
if logFile != "" {
|
||
logger.LogMessage(logFile, fmt.Sprintf("Начало отключения от '%s'", state.ConfigName))
|
||
}
|
||
|
||
// Останавливаем процесс в зависимости от типа
|
||
if state.ConfigType == "wireguard" {
|
||
// Отключаем WireGuard
|
||
if err := wireguard.Disconnect(state.Interface, logsDir); err != nil {
|
||
fmt.Printf("%s Ошибка отключения WireGuard: %v\n", "⚠", err)
|
||
}
|
||
} else if state.ProcessPID > 0 {
|
||
// Останавливаем процесс VLESS
|
||
process, err := os.FindProcess(state.ProcessPID)
|
||
if err == nil {
|
||
if runtime.GOOS == "windows" {
|
||
// На Windows используем taskkill
|
||
process.Kill()
|
||
} else {
|
||
// На Unix используем SIGTERM
|
||
process.Signal(syscall.SIGTERM)
|
||
}
|
||
|
||
// Ждем завершения процесса
|
||
time.Sleep(1 * time.Second)
|
||
|
||
if logFile != "" {
|
||
logger.LogMessage(logFile, fmt.Sprintf("Отключено от '%s' (PID: %d)", state.ConfigName, state.ProcessPID))
|
||
}
|
||
|
||
if state.LogFile != "" && logFile != "" {
|
||
logger.LogMessage(logFile, fmt.Sprintf("Лог трафика сохранен: %s", state.LogFile))
|
||
}
|
||
}
|
||
}
|
||
|
||
// Сбрасываем состояние
|
||
newState := &config.ConnectionState{
|
||
Connected: false,
|
||
ConfigName: "",
|
||
ConfigType: "",
|
||
StartTime: "",
|
||
Interface: "",
|
||
ProcessPID: 0,
|
||
LogFile: "",
|
||
}
|
||
|
||
if err := config.SaveState(newState); err != nil {
|
||
return fmt.Errorf("ошибка сохранения состояния: %w", err)
|
||
}
|
||
|
||
// Отключаем системный прокси если был VLESS
|
||
if state.ConfigType == "vless" {
|
||
if err := proxy.DisableSystemProxy(); err != nil {
|
||
fmt.Printf("%s Не удалось отключить системный прокси: %v\n", "⚠", err)
|
||
fmt.Println("Отключите его вручную в настройках Windows")
|
||
} else {
|
||
fmt.Println("✓ Системный прокси отключен")
|
||
}
|
||
}
|
||
|
||
fmt.Println("✓ Отключено от VPN")
|
||
return nil
|
||
}
|
||
|
||
// GetStatus возвращает текущий статус подключения
|
||
func GetStatus() (*config.ConnectionState, error) {
|
||
return config.LoadState()
|
||
}
|
||
|
||
// ShowStatus отображает детальный статус подключения
|
||
func ShowStatus() error {
|
||
state, err := GetStatus()
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка получения статуса: %w", err)
|
||
}
|
||
|
||
if !state.Connected {
|
||
fmt.Println("\n❌ VPN не подключен")
|
||
return nil
|
||
}
|
||
|
||
fmt.Println("\n" + "==================================================")
|
||
fmt.Println("📊 Статус VPN")
|
||
fmt.Println("==================================================")
|
||
fmt.Printf("Статус: ✓ Подключено\n")
|
||
fmt.Printf("Конфиг: %s\n", state.ConfigName)
|
||
fmt.Printf("Тип: %s\n", state.ConfigType)
|
||
|
||
if state.StartTime != "" {
|
||
startTime, err := time.Parse(time.RFC3339, state.StartTime)
|
||
if err == nil {
|
||
duration := time.Since(startTime)
|
||
hours := int(duration.Hours())
|
||
minutes := int(duration.Minutes()) % 60
|
||
seconds := int(duration.Seconds()) % 60
|
||
fmt.Printf("Время подключения: %02d:%02d:%02d\n", hours, minutes, seconds)
|
||
}
|
||
}
|
||
|
||
if state.ConfigType == "vless" {
|
||
fmt.Printf("Прокси: 127.0.0.1:10808\n")
|
||
if state.LogFile != "" {
|
||
fmt.Printf("Лог трафика: %s\n", state.LogFile)
|
||
}
|
||
} else if state.ConfigType == "wireguard" {
|
||
// Получаем статистику WireGuard
|
||
stats, err := wireguard.GetStats(state.Interface)
|
||
if err == nil {
|
||
fmt.Printf("\nСтатистика трафика:\n")
|
||
fmt.Printf(" Получено: %s\n", stats["rx"])
|
||
fmt.Printf(" Отправлено: %s\n", stats["tx"])
|
||
}
|
||
}
|
||
|
||
fmt.Println("==================================================")
|
||
return nil
|
||
}
|