package subscription import ( "encoding/base64" "fmt" "io" "net/http" "net/url" "strings" "time" "vpn-client/internal/config" "vpn-client/internal/logger" ) // FetchConfigs загружает конфигурации из подписки func FetchConfigs(subscriptionURL, logsDir string) ([]config.VLESSConfig, error) { logFile := logger.GetLogPath(logsDir, "subscription") logger.LogMessage(logFile, fmt.Sprintf("Начало загрузки подписки: %s", subscriptionURL)) // Создаем HTTP клиент с таймаутом client := &http.Client{ Timeout: 10 * time.Second, } resp, err := client.Get(subscriptionURL) if err != nil { logger.LogMessage(logFile, fmt.Sprintf("Ошибка загрузки подписки: %v", err)) return nil, fmt.Errorf("ошибка загрузки подписки: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("ошибка HTTP: %d", resp.StatusCode) } body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("ошибка чтения ответа: %w", err) } // Пытаемся декодировать base64 content := string(body) if decoded, err := base64.StdEncoding.DecodeString(content); err == nil { content = string(decoded) } // Парсим конфигурации var configs []config.VLESSConfig lines := strings.Split(content, "\n") for _, line := range lines { line = strings.TrimSpace(line) if line == "" { continue } var protocol string var name string var configURL string // VLESS конфиги if strings.HasPrefix(line, "vless://") { protocol = "VLESS" configURL = line if parsed, err := url.Parse(line); err == nil && parsed.Fragment != "" { name, _ = url.QueryUnescape(parsed.Fragment) } else { name = fmt.Sprintf("VLESS_%d", len(configs)+1) } configs = append(configs, config.VLESSConfig{ Name: name, URL: configURL, Protocol: protocol, }) } else if strings.HasPrefix(line, "vmess://") { protocol = "VMess" configURL = line if parsed, err := url.Parse(line); err == nil && parsed.Fragment != "" { name, _ = url.QueryUnescape(parsed.Fragment) } else { name = fmt.Sprintf("VMess_%d", len(configs)+1) } configs = append(configs, config.VLESSConfig{ Name: name, URL: configURL, Protocol: protocol, }) } else if strings.HasPrefix(line, "trojan://") { protocol = "Trojan" configURL = line if parsed, err := url.Parse(line); err == nil && parsed.Fragment != "" { name, _ = url.QueryUnescape(parsed.Fragment) } else { name = fmt.Sprintf("Trojan_%d", len(configs)+1) } configs = append(configs, config.VLESSConfig{ Name: name, URL: configURL, Protocol: protocol, }) } else if strings.HasPrefix(line, "ss://") { protocol = "Shadowsocks" configURL = line if parsed, err := url.Parse(line); err == nil && parsed.Fragment != "" { name, _ = url.QueryUnescape(parsed.Fragment) } else { name = fmt.Sprintf("SS_%d", len(configs)+1) } configs = append(configs, config.VLESSConfig{ Name: name, URL: configURL, Protocol: protocol, }) } } logger.LogMessage(logFile, fmt.Sprintf("Успешно загружено %d конфигов", len(configs))) return configs, nil } // UpdateSubscription обновляет конфигурации из подписки func UpdateSubscription(subscriptionName, logsDir string) error { // Загружаем подписки subs, err := config.LoadSubscriptions() if err != nil { return fmt.Errorf("ошибка загрузки подписок: %w", err) } // Ищем подписку var sub *config.Subscription for i := range subs.Subscriptions { if subs.Subscriptions[i].Name == subscriptionName { sub = &subs.Subscriptions[i] break } } if sub == nil { return fmt.Errorf("подписка '%s' не найдена", subscriptionName) } fmt.Printf("Загрузка конфигов из '%s'...\n", subscriptionName) // Загружаем конфиги newConfigs, err := FetchConfigs(sub.URL, logsDir) if err != nil { return fmt.Errorf("ошибка загрузки конфигов: %w", err) } if len(newConfigs) == 0 { return fmt.Errorf("не удалось загрузить конфиги") } // Загружаем текущие конфигурации allConfigs, err := config.LoadConfigs() if err != nil { return fmt.Errorf("ошибка загрузки конфигураций: %w", err) } // Удаляем старые конфиги из этой подписки var filteredConfigs []config.VLESSConfig for _, cfg := range allConfigs.VLESS { if cfg.Subscription != subscriptionName { filteredConfigs = append(filteredConfigs, cfg) } } allConfigs.VLESS = filteredConfigs // Добавляем новые конфиги с префиксом подписки for _, cfg := range newConfigs { cfg.Name = fmt.Sprintf("[%s] %s", subscriptionName, cfg.Name) cfg.Subscription = subscriptionName allConfigs.VLESS = append(allConfigs.VLESS, cfg) } // Сохраняем if err := config.SaveConfigs(allConfigs); err != nil { return fmt.Errorf("ошибка сохранения конфигураций: %w", err) } fmt.Printf("✓ Обновлено %d конфигов из подписки\n", len(newConfigs)) return nil }