Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions FluxRoute.Core/Services/ProfileBatLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,32 @@ public static bool TryCreateLaunchPlan(
.Where(a => !string.IsNullOrWhiteSpace(a) && a != "^")
.ToList();

// ═══ ИНЪЕКЦИЯ ПОЛЬЗОВАТЕЛЬСКИХ ДОМЕНОВ ИЗ МЕНЕДЖЕРА ═══
// Автоматически добавляем list-general-user.txt в аргументы winws.exe,
// чтобы домены из UI FluxRoute работали без ручного редактирования BAT-файлов.
var userHostlistPath = Path.Combine(engineDir, "lists", "list-general-user.txt");
if (File.Exists(userHostlistPath) && new FileInfo(userHostlistPath).Length > 10)
{
bool hasUserList = args.Any(a => a.Contains("list-general-user", StringComparison.OrdinalIgnoreCase));
if (!hasUserList)
{
args.Add("--hostlist");
args.Add(userHostlistPath); // Передаем абсолютный путь
}
}

var userExcludePath = Path.Combine(engineDir, "lists", "list-exclude-user.txt");
if (File.Exists(userExcludePath) && new FileInfo(userExcludePath).Length > 10)
{
bool hasExclude = args.Any(a => a.Contains("list-exclude-user", StringComparison.OrdinalIgnoreCase));
if (!hasExclude)
{
args.Add("--hostlist-exclude");
args.Add(userExcludePath);
}
}
// ═══════════════════════════════════════════════════════

if (args.Count == 0)
{
lastCandidateError = "В BAT найден winws.exe, но не найдены аргументы запуска.";
Expand Down
12 changes: 12 additions & 0 deletions FluxRoute.Core/Services/SettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ public sealed class AppSettings
// Пользовательские сайты для проверки
public List<string> UserSites { get; set; } = new();

// ═══ НОВЫЕ СВОЙСТВА ДЛЯ МЕНЕДЖЕРА ДОМЕНОВ ═══
/// <summary>
/// Список доменов для проверки (включения).
/// </summary>
public List<string> CustomTargetDomains { get; set; } = new();

/// <summary>
/// Список доменов для исключения из проверки.
/// </summary>
public List<string> CustomExcludeDomains { get; set; } = new();
// ═══════════════════════════════════════════

// Рейтинг профилей
public List<ProfileRatingEntry> ProfileRatings { get; set; } = new();

Expand Down
28 changes: 24 additions & 4 deletions FluxRoute/ViewModels/MainViewModel.Orchestrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,31 @@ private void UpdateOrchestratorEnabledSites()
_orchestrator.EnabledSites = sites;
_aiOrchestrator.EnabledSites = sites;

var userTargets = UserCustomSitesText
var userTargets = new List<TargetEntry>();

// Берем домены из менеджера, ИСКЛЮЧАЯ те, что во вкладке "Исключения"
foreach (var domain in CustomTargetDomains)
{
if (!string.IsNullOrWhiteSpace(domain) &&
!CustomExcludeDomains.Contains(domain, StringComparer.OrdinalIgnoreCase))
{
userTargets.Add(BuildUserTargetEntry(domain));
}
}

// Fallback на старый TextBox
var legacyTargets = UserCustomSitesText
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Where(s => !string.IsNullOrWhiteSpace(s))
.Select(s => BuildUserTargetEntry(s))
.ToList();
.Where(s => !string.IsNullOrWhiteSpace(s) && !s.StartsWith("!") &&
!CustomExcludeDomains.Contains(s, StringComparer.OrdinalIgnoreCase))
.Select(s => BuildUserTargetEntry(s));

foreach (var t in legacyTargets)
{
if (!userTargets.Any(x => x.Key == t.Key))
userTargets.Add(t);
}

_orchestrator.UserSiteTargets = userTargets;
_aiOrchestrator.UserSiteTargets = userTargets;
}
Expand Down
1 change: 1 addition & 0 deletions FluxRoute/ViewModels/MainViewModel.Process.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ private void Start()

try
{
SyncCustomHostlist();
ProfileBatLauncher.PrepareRuntime(EngineDir);

if (ProfileBatLauncher.TryCreateLaunchPlan(SelectedProfile.FullPath, EngineDir, out var plan, out var parseError) && plan is not null)
Expand Down
190 changes: 179 additions & 11 deletions FluxRoute/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Windows;
using System.Windows.Threading;
using Application = System.Windows.Application;
using FluxRoute.AI.Services;
using FluxRoute.Core.Models;
using FluxRoute.Core.Services;
using FluxRoute.AI.Services;
using FluxRoute.Services;
using FluxRoute.Updater.Services;
using FluxRoute.Views;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using Application = System.Windows.Application;

namespace FluxRoute.ViewModels;

Expand All @@ -29,10 +30,19 @@ public partial class MainViewModel : ObservableObject
public ObservableCollection<string> UpdateLogs => Updates.UpdateLogs;
public ObservableCollection<string> ServiceLogs => Service.ServiceLogs;

// Коллекции для менеджера доменов (UI)

// ── Пресеты конфигурации ──
public ObservableCollection<ConfigPreset> Presets { get; } = new();
public ObservableCollection<string> RunningProcesses { get; } = new();

// ── Менеджер доменов ──
[ObservableProperty] private string selectedTabMode = "Domains";
[ObservableProperty] private string newSiteInput = "";

public ObservableCollection<string> CustomTargetDomains { get; } = new();
public ObservableCollection<string> CustomExcludeDomains { get; } = new();

[ObservableProperty] private string newPresetName = "";
[ObservableProperty] private string newPresetTrigger = "";

Expand Down Expand Up @@ -125,6 +135,79 @@ private void RemovePreset(ConfigPreset preset)
SaveSettings();
}

// ── Команды менеджера доменов ──
[RelayCommand]
private void AddCustomDomain()
{
var input = NewSiteInput.Trim();
if (string.IsNullOrEmpty(input)) return;

// Нормализация: убираем протоколы и www
input = input.Replace("http://", "").Replace("https://", "").Replace("www.", "").TrimEnd('/');
if (string.IsNullOrEmpty(input)) return;

var list = SelectedTabMode == "Exclusions" ? CustomExcludeDomains : CustomTargetDomains;

if (!list.Contains(input, StringComparer.OrdinalIgnoreCase))
{
list.Add(input);
NewSiteInput = "";
SaveSettings();
SyncCustomHostlist();
AddToRecentLogs($"✅ Добавлен домен: {input} ({(SelectedTabMode == "Exclusions" ? "исключение" : "проверка")})");
}
else
{
NewSiteInput = "";
}
}

[RelayCommand]
private void RemoveCustomDomain(string domain)
{
if (string.IsNullOrEmpty(domain)) return;
var list = SelectedTabMode == "Exclusions" ? CustomExcludeDomains : CustomTargetDomains;
if (list.Contains(domain))
{
list.Remove(domain);
SaveSettings();
SyncCustomHostlist();
AddToRecentLogs($"🗑 Удалён домен: {domain}");
}
}

[RelayCommand]
private void SetDomainsMode()
{
SelectedTabMode = "Domains";
NewSiteInput = "";
}

[RelayCommand]
private void SetExclusionsMode()
{
SelectedTabMode = "Exclusions";
NewSiteInput = "";
}

[RelayCommand]
private void ClearDomainList()
{
var list = SelectedTabMode == "Exclusions" ? CustomExcludeDomains : CustomTargetDomains;
if (list.Count == 0) return;
var modeName = SelectedTabMode == "Exclusions" ? "исключений" : "доменов";
if (CustomDialog.Show(
"🗑 Очистить список",
$"Удалить все {list.Count} записей из списка {modeName}?",
"Очистить", "Отмена", isDanger: true))
{
list.Clear();
SaveSettings();
SyncCustomHostlist();
AddToRecentLogs($"🗑 Список {modeName} очищен");
}
}

// ── События ──
public event EventHandler? OpenSettingsRequested;
public event EventHandler? OpenAboutRequested;
Expand Down Expand Up @@ -203,6 +286,8 @@ partial void OnSelectedTabIndexChanged(int value)
// ── Боковая панель ──
[ObservableProperty] private bool isSidebarExpanded = true;



[RelayCommand]
private void ToggleSidebar() => IsSidebarExpanded = !IsSidebarExpanded;

Expand Down Expand Up @@ -516,6 +601,29 @@ private void ApplySettings(AppSettings settings)
SiteInstagram = settings.SiteInstagram;
SiteTelegram = settings.SiteTelegram;
UserCustomSitesText = string.Join("\n", settings.UserSites ?? new());

// Миграция старых данных в новые списки менеджера доменов
CustomTargetDomains.Clear();
CustomExcludeDomains.Clear();

if (settings.CustomTargetDomains?.Count > 0)
{
foreach (var s in settings.CustomTargetDomains) CustomTargetDomains.Add(s);
}
else if (settings.UserSites?.Count > 0)
{
foreach (var s in settings.UserSites.Where(x => !x.StartsWith("!"))) CustomTargetDomains.Add(s);
}

if (settings.CustomExcludeDomains?.Count > 0)
{
foreach (var s in settings.CustomExcludeDomains) CustomExcludeDomains.Add(s);
}
else if (settings.UserSites?.Count > 0)
{
foreach (var s in settings.UserSites.Where(x => x.StartsWith("!"))) CustomExcludeDomains.Add(s.TrimStart('!'));
}

AutoUpdateEnabled = settings.AutoUpdateEnabled;
AutoStartEnabled = settings.AutoStartEnabled;
MinimizeToTray = settings.MinimizeToTray;
Expand Down Expand Up @@ -565,9 +673,11 @@ public void SaveSettings()
SiteInstagram = SiteInstagram,
SiteTelegram = SiteTelegram,
UserSites = UserCustomSitesText
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Where(s => !string.IsNullOrWhiteSpace(s))
.ToList(),
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Where(s => !string.IsNullOrWhiteSpace(s))
.ToList(),
CustomTargetDomains = CustomTargetDomains.ToList(),
CustomExcludeDomains = CustomExcludeDomains.ToList(),
AutoUpdateEnabled = AutoUpdateEnabled,
AutoStartEnabled = AutoStartEnabled,
MinimizeToTray = MinimizeToTray,
Expand Down Expand Up @@ -641,6 +751,7 @@ private void MainAction()
else Start();
}


private void AddToRecentLogs(string message)
{
RecentLogs.Add(message);
Expand All @@ -649,6 +760,63 @@ private void AddToRecentLogs(string message)
LastStatusMessage = message;
}

// ── Синхронизация пользовательских доменов с движком (winws.exe) ──
// ── Синхронизация пользовательских доменов с движком (winws.exe) ──
private void SyncCustomHostlist()
{
try
{
var listsDir = Path.Combine(EngineDir, "lists");
Directory.CreateDirectory(listsDir);
var userHostlistPath = Path.Combine(listsDir, "list-general-user.txt");

var domains = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

// 1. Берем домены из нового Менеджера доменов (вкладка "Домены")
foreach (var d in CustomTargetDomains)
{
if (!string.IsNullOrWhiteSpace(d))
domains.Add(d.Trim());
}

// 2. Подхватываем из старого TextBox (для обратной совместимости)
if (!string.IsNullOrWhiteSpace(UserCustomSitesText))
{
var legacy = UserCustomSitesText
.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Where(s => !s.StartsWith("!"));
foreach (var d in legacy)
domains.Add(d.Trim());
}

// Записываем в файл
if (domains.Count > 0)
{
// Используем явное удаление + запись, чтобы избежать кэширования
if (File.Exists(userHostlistPath))
{
try { File.SetAttributes(userHostlistPath, FileAttributes.Normal); } catch { }
}
File.WriteAllLines(userHostlistPath, domains.OrderBy(x => x), new UTF8Encoding(false));
Logs.Add($"[Sync] Записано {domains.Count} доменов в list-general-user.txt");
}
else
{
// Пустой список — удаляем файл или пишем комментарий
if (File.Exists(userHostlistPath))
File.Delete(userHostlistPath);
else
File.WriteAllText(userHostlistPath, "# custom domains empty\n", new UTF8Encoding(false));
Logs.Add("[Sync] list-general-user.txt очищен");
}
}
catch (Exception ex)
{
Logs.Add($"❌ Ошибка записи list-general-user.txt: {ex.Message}");
Logs.Add($" Stack: {ex.StackTrace?.Split('\n')[0]}");
}
}

// ── Cleanup ──

public void Cleanup()
Expand Down
Loading
Loading