- Add GUI (Test) module with Fyne-based interface (internal/gui/gui.go, internal/gui/server.go) - Add CLI monitoring capability (internal/cli/monitor.go) - Add main_cli.go entry point for CLI-only builds - Add comprehensive documentation suite covering setup, build, quick start, and changelog - Add admin manifest (admin.manifest) for Windows UAC elevation support - Add rsrc.syso.json configuration for resource embedding - Update .gitignore to exclude build scripts (*.bat, *.sh) - Update main.go and cli.go to support dual GUI (Test)/CLI modes - Update README.md with new project information - Enables users to build and run both GUI (Test)and CLI versions with proper admin privileges on Windows
189 lines
5.6 KiB
Go
189 lines
5.6 KiB
Go
package cli
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"strings"
|
||
"time"
|
||
|
||
"vpn-client/internal/config"
|
||
"vpn-client/internal/wireguard"
|
||
)
|
||
|
||
// MonitorConnection показывает статус подключения в реальном времени
|
||
func MonitorConnection() error {
|
||
// Проверяем, что есть активное подключение
|
||
state, err := config.LoadState()
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка загрузки состояния: %w", err)
|
||
}
|
||
|
||
if !state.Connected {
|
||
fmt.Println("VPN не подключен")
|
||
return nil
|
||
}
|
||
|
||
fmt.Println("Нажмите Ctrl+C для выхода из мониторинга\n")
|
||
time.Sleep(1 * time.Second)
|
||
|
||
// Запускаем мониторинг
|
||
ticker := time.NewTicker(1 * time.Second)
|
||
defer ticker.Stop()
|
||
|
||
for {
|
||
select {
|
||
case <-ticker.C:
|
||
clearScreen()
|
||
displayRealtimeStatus(state)
|
||
}
|
||
}
|
||
}
|
||
|
||
func displayRealtimeStatus(state *config.ConnectionState) {
|
||
// Перезагружаем состояние для актуальных данных
|
||
currentState, err := config.LoadState()
|
||
if err != nil || !currentState.Connected {
|
||
fmt.Println("❌ Подключение потеряно")
|
||
return
|
||
}
|
||
|
||
// Заголовок
|
||
fmt.Println(strings.Repeat("═", 70))
|
||
fmt.Println(bold("📊 VPN МОНИТОРИНГ В РЕАЛЬНОМ ВРЕМЕНИ"))
|
||
fmt.Println(strings.Repeat("═", 70))
|
||
|
||
// Статус подключения
|
||
fmt.Printf("\n%s %s\n", green("●"), bold("ПОДКЛЮЧЕНО"))
|
||
fmt.Printf("Конфигурация: %s\n", cyan(currentState.ConfigName))
|
||
fmt.Printf("Тип: %s\n", currentState.ConfigType)
|
||
|
||
// Время подключения
|
||
if currentState.StartTime != "" {
|
||
startTime, err := time.Parse(time.RFC3339, currentState.StartTime)
|
||
if err == nil {
|
||
duration := time.Since(startTime)
|
||
hours := int(duration.Hours())
|
||
minutes := int(duration.Minutes()) % 60
|
||
seconds := int(duration.Seconds()) % 60
|
||
fmt.Printf("Время подключения: %s\n",
|
||
yellow(fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)))
|
||
}
|
||
}
|
||
|
||
// Текущее время
|
||
fmt.Printf("Текущее время: %s\n", time.Now().Format("15:04:05"))
|
||
|
||
fmt.Println(strings.Repeat("─", 70))
|
||
|
||
// Специфичная информация по типу подключения
|
||
if currentState.ConfigType == "vless" {
|
||
displayVLESSStats(currentState)
|
||
} else if currentState.ConfigType == "wireguard" {
|
||
displayWireGuardStats(currentState)
|
||
}
|
||
|
||
fmt.Println(strings.Repeat("═", 70))
|
||
fmt.Printf("\n%s Обновление каждую секунду | Нажмите Ctrl+C для выхода\n",
|
||
cyan("ℹ"))
|
||
}
|
||
|
||
func displayVLESSStats(state *config.ConnectionState) {
|
||
fmt.Println("\n" + bold("VLESS/Xray Прокси"))
|
||
fmt.Printf("Адрес прокси: %s\n", green("127.0.0.1:10808"))
|
||
fmt.Printf("Протокол: SOCKS5\n")
|
||
fmt.Printf("PID процесса: %d\n", state.ProcessPID)
|
||
|
||
if state.LogFile != "" {
|
||
fmt.Printf("\n%s Логи\n", bold("📝"))
|
||
fmt.Printf(" Трафик: %s\n", state.LogFile)
|
||
|
||
// Показываем размер лог-файла
|
||
if info, err := os.Stat(state.LogFile); err == nil {
|
||
size := info.Size()
|
||
sizeStr := formatBytes(size)
|
||
fmt.Printf(" Размер лога: %s\n", sizeStr)
|
||
}
|
||
}
|
||
|
||
// Проверяем, что процесс еще работает
|
||
if state.ProcessPID > 0 {
|
||
process, err := os.FindProcess(state.ProcessPID)
|
||
if err == nil {
|
||
if err := process.Signal(os.Signal(nil)); err != nil {
|
||
fmt.Printf("\n%s Процесс Xray не отвечает!\n", red("⚠"))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func displayWireGuardStats(state *config.ConnectionState) {
|
||
fmt.Println("\n" + bold("WireGuard Туннель"))
|
||
fmt.Printf("Интерфейс: %s\n", state.Interface)
|
||
|
||
// Получаем статистику WireGuard
|
||
stats, err := wireguard.GetStats(state.Interface)
|
||
if err != nil {
|
||
fmt.Printf("\n%s Ошибка получения статистики: %v\n", red("⚠"), err)
|
||
return
|
||
}
|
||
|
||
fmt.Printf("\n%s Статистика трафика\n", bold("📊"))
|
||
|
||
// Парсим и форматируем данные
|
||
if rx, ok := stats["rx"]; ok {
|
||
fmt.Printf(" %s Получено: %s\n", green("↓"), rx)
|
||
}
|
||
|
||
if tx, ok := stats["tx"]; ok {
|
||
fmt.Printf(" %s Отправлено: %s\n", yellow("↑"), tx)
|
||
}
|
||
|
||
// Дополнительная информация
|
||
if endpoint, ok := stats["endpoint"]; ok && endpoint != "" {
|
||
fmt.Printf("\n%s Сервер\n", bold("🌐"))
|
||
fmt.Printf(" Endpoint: %s\n", endpoint)
|
||
}
|
||
|
||
if handshake, ok := stats["latest_handshake"]; ok && handshake != "" {
|
||
fmt.Printf(" Последний handshake: %s\n", handshake)
|
||
}
|
||
}
|
||
|
||
func formatBytes(bytes int64) string {
|
||
const unit = 1024
|
||
if bytes < unit {
|
||
return fmt.Sprintf("%d B", bytes)
|
||
}
|
||
div, exp := int64(unit), 0
|
||
for n := bytes / unit; n >= unit; n /= unit {
|
||
div *= unit
|
||
exp++
|
||
}
|
||
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
|
||
}
|
||
|
||
// ShowQuickStatus показывает краткий статус (для главного меню)
|
||
func ShowQuickStatus() string {
|
||
state, err := config.LoadState()
|
||
if err != nil || !state.Connected {
|
||
return fmt.Sprintf("%s Не подключено", red("○"))
|
||
}
|
||
|
||
// Вычисляем время подключения
|
||
var timeStr string
|
||
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
|
||
timeStr = fmt.Sprintf(" [%02d:%02d]", hours, minutes)
|
||
}
|
||
}
|
||
|
||
return fmt.Sprintf("%s Подключено: %s%s",
|
||
green("●"),
|
||
cyan(state.ConfigName),
|
||
yellow(timeStr))
|
||
}
|