LumaTunnel
【WinUI】LumaTunnel 是一个面向个人多设备的 Windows 代理系统。客户端在本机提供 HTTP/HTTPS CONNECT 与 SOCKS5 TCP 代理,并通过一个受信任 TLS 证书保护的 WSS 会话,将多个 TCP 流复用到自建 Windows Server 节点。
关注
0
Fork
0
Star
0
using System.Text.Json;
using LumaTunnel.Shared.Models;
using LumaTunnel.Shared.Protocol;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace LumaTunnel.Client.Views;
public sealed partial class RoutingPage : Page
{
private List<RoutingRule> _rules = [];
private bool _loaded;
public RoutingPage() => InitializeComponent();
private void OnLoaded(object sender, RoutedEventArgs e)
{
_rules = AppServices.Profile.Rules.OrderBy(static rule => rule.Priority).ToList();
ModeBox.SelectedIndex = AppServices.Profile.Mode switch { ProxyMode.Rule => 0, ProxyMode.Global => 1, _ => 2 };
_loaded = true;
Refresh();
}
private async void OnModeChanged(object sender, SelectionChangedEventArgs e)
{
if (!_loaded) return;
await AppServices.SetModeAsync(ModeBox.SelectedIndex switch { 0 => ProxyMode.Rule, 1 => ProxyMode.Global, _ => ProxyMode.Direct });
}
private async void OnAdd(object sender, RoutedEventArgs e)
{
try
{
var rule = new RoutingRule
{
Kind = (RuleKind)KindBox.SelectedIndex,
Value = ValueBox.Text.Trim(),
Action = ActionBox.SelectedIndex == 0 ? RouteAction.Tunnel : RouteAction.Direct,
Priority = _rules.Count
};
var candidate = _rules.Append(rule).ToList();
AppServices.Routing.SetRules(candidate);
_rules = candidate;
await SaveAsync();
ValueBox.Text = string.Empty;
}
catch (Exception exception) { Show(exception.Message, InfoBarSeverity.Error); }
}
private async void OnDelete(object sender, RoutedEventArgs e)
{
if (RuleList.SelectedIndex < 0) return;
_rules.RemoveAt(RuleList.SelectedIndex);
await SaveAsync();
}
private async void OnMoveUp(object sender, RoutedEventArgs e) => await MoveAsync(-1);
private async void OnMoveDown(object sender, RoutedEventArgs e) => await MoveAsync(1);
private async Task MoveAsync(int delta)
{
var source = RuleList.SelectedIndex;
var target = source + delta;
if (source < 0 || target < 0 || target >= _rules.Count) return;
(_rules[source], _rules[target]) = (_rules[target], _rules[source]);
await SaveAsync();
RuleList.SelectedIndex = target;
}
private async void OnExport(object sender, RoutedEventArgs e)
{
var box = new TextBox { Text = JsonSerializer.Serialize(_rules, TunnelMessageSerializer.Options), AcceptsReturn = true, TextWrapping = TextWrapping.Wrap, MinWidth = 620, MinHeight = 300, IsReadOnly = true };
await new ContentDialog { Title = "规则 JSON", Content = box, CloseButtonText = "关闭", XamlRoot = XamlRoot }.ShowAsync();
}
private async void OnImport(object sender, RoutedEventArgs e)
{
var box = new TextBox { AcceptsReturn = true, TextWrapping = TextWrapping.Wrap, MinWidth = 620, MinHeight = 300, PlaceholderText = "粘贴规则 JSON 数组" };
var dialog = new ContentDialog { Title = "导入规则", Content = box, PrimaryButtonText = "导入", CloseButtonText = "取消", XamlRoot = XamlRoot };
if (await dialog.ShowAsync() != ContentDialogResult.Primary) return;
try
{
_rules = JsonSerializer.Deserialize<List<RoutingRule>>(box.Text, TunnelMessageSerializer.Options) ?? [];
AppServices.Routing.SetRules(_rules);
await SaveAsync();
}
catch (Exception exception) { Show(exception.Message, InfoBarSeverity.Error); }
}
private async Task SaveAsync()
{
_rules = _rules.Select((rule, index) => rule with { Priority = index }).ToList();
await AppServices.SetRulesAsync(_rules);
Refresh();
}
private void Refresh() => RuleList.ItemsSource = _rules.Select(rule => $"{rule.Priority + 1}. {rule.Kind} {rule.Value} → {rule.Action}").ToArray();
private void Show(string message, InfoBarSeverity severity) { MessageBar.Message = message; MessageBar.Severity = severity; MessageBar.IsOpen = true; }
}
using System.Text.Json;
using LumaTunnel.Shared.Models;
using LumaTunnel.Shared.Protocol;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace LumaTunnel.Client.Views;
public sealed partial class RoutingPage : Page
{
private List<RoutingRule> _rules = [];
private bool _loaded;
public RoutingPage() => InitializeComponent();
private void OnLoaded(object sender, RoutedEventArgs e)
{
_rules = AppServices.Profile.Rules.OrderBy(static rule => rule.Priority).ToList();
ModeBox.SelectedIndex = AppServices.Profile.Mode switch { ProxyMode.Rule => 0, ProxyMode.Global => 1, _ => 2 };
_loaded = true;
Refresh();
}
private async void OnModeChanged(object sender, SelectionChangedEventArgs e)
{
if (!_loaded) return;
await AppServices.SetModeAsync(ModeBox.SelectedIndex switch { 0 => ProxyMode.Rule, 1 => ProxyMode.Global, _ => ProxyMode.Direct });
}
private async void OnAdd(object sender, RoutedEventArgs e)
{
try
{
var rule = new RoutingRule
{
Kind = (RuleKind)KindBox.SelectedIndex,
Value = ValueBox.Text.Trim(),
Action = ActionBox.SelectedIndex == 0 ? RouteAction.Tunnel : RouteAction.Direct,
Priority = _rules.Count
};
var candidate = _rules.Append(rule).ToList();
AppServices.Routing.SetRules(candidate);
_rules = candidate;
await SaveAsync();
ValueBox.Text = string.Empty;
}
catch (Exception exception) { Show(exception.Message, InfoBarSeverity.Error); }
}
private async void OnDelete(object sender, RoutedEventArgs e)
{
if (RuleList.SelectedIndex < 0) return;
_rules.RemoveAt(RuleList.SelectedIndex);
await SaveAsync();
}
private async void OnMoveUp(object sender, RoutedEventArgs e) => await MoveAsync(-1);
private async void OnMoveDown(object sender, RoutedEventArgs e) => await MoveAsync(1);
private async Task MoveAsync(int delta)
{
var source = RuleList.SelectedIndex;
var target = source + delta;
if (source < 0 || target < 0 || target >= _rules.Count) return;
(_rules[source], _rules[target]) = (_rules[target], _rules[source]);
await SaveAsync();
RuleList.SelectedIndex = target;
}
private async void OnExport(object sender, RoutedEventArgs e)
{
var box = new TextBox { Text = JsonSerializer.Serialize(_rules, TunnelMessageSerializer.Options), AcceptsReturn = true, TextWrapping = TextWrapping.Wrap, MinWidth = 620, MinHeight = 300, IsReadOnly = true };
await new ContentDialog { Title = "规则 JSON", Content = box, CloseButtonText = "关闭", XamlRoot = XamlRoot }.ShowAsync();
}
private async void OnImport(object sender, RoutedEventArgs e)
{
var box = new TextBox { AcceptsReturn = true, TextWrapping = TextWrapping.Wrap, MinWidth = 620, MinHeight = 300, PlaceholderText = "粘贴规则 JSON 数组" };
var dialog = new ContentDialog { Title = "导入规则", Content = box, PrimaryButtonText = "导入", CloseButtonText = "取消", XamlRoot = XamlRoot };
if (await dialog.ShowAsync() != ContentDialogResult.Primary) return;
try
{
_rules = JsonSerializer.Deserialize<List<RoutingRule>>(box.Text, TunnelMessageSerializer.Options) ?? [];
AppServices.Routing.SetRules(_rules);
await SaveAsync();
}
catch (Exception exception) { Show(exception.Message, InfoBarSeverity.Error); }
}
private async Task SaveAsync()
{
_rules = _rules.Select((rule, index) => rule with { Priority = index }).ToList();
await AppServices.SetRulesAsync(_rules);
Refresh();
}
private void Refresh() => RuleList.ItemsSource = _rules.Select(rule => $"{rule.Priority + 1}. {rule.Kind} {rule.Value} → {rule.Action}").ToArray();
private void Show(string message, InfoBarSeverity severity) { MessageBar.Message = message; MessageBar.Severity = severity; MessageBar.IsOpen = true; }
}