Files
Go-VPN-Client/internal/vpn/vpn.go
arkonsadter 20d24a3639 feat(cli): add settings menu and VLESS log viewer with core selection
- 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
2026-04-06 20:06:35 +06:00

155 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}