2025-04-02 03:44:23 +00:00
|
|
|
namespace ServiceLib.ViewModels;
|
|
|
|
|
|
|
|
|
|
public class AddServerViewModel : MyReactiveObject
|
2023-01-01 11:42:01 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public ProfileItem SelectedSource { get; set; }
|
2024-08-30 05:15:25 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public string? CoreType { get; set; }
|
2023-01-01 11:42:01 +00:00
|
|
|
|
2025-11-02 07:17:47 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public string Cert { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string CertTip { get; set; }
|
|
|
|
|
|
2026-01-17 07:42:40 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public string CertSha { get; set; }
|
|
|
|
|
|
2026-02-05 11:48:33 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public string SalamanderPass { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public int AlterId { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string Ports { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
2026-03-10 01:14:57 +00:00
|
|
|
public int? UpMbps { get; set; }
|
2026-02-05 11:48:33 +00:00
|
|
|
|
|
|
|
|
[Reactive]
|
2026-03-10 01:14:57 +00:00
|
|
|
public int? DownMbps { get; set; }
|
2026-02-05 11:48:33 +00:00
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string HopInterval { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string Flow { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string VmessSecurity { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string VlessEncryption { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string SsMethod { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string WgPublicKey { get; set; }
|
2026-02-06 05:50:47 +00:00
|
|
|
|
2026-02-05 11:48:33 +00:00
|
|
|
//[Reactive]
|
|
|
|
|
//public string WgPresharedKey { get; set; }
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string WgInterfaceAddress { get; set; }
|
2026-02-06 05:50:47 +00:00
|
|
|
|
2026-02-05 11:48:33 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public string WgReserved { get; set; }
|
2026-02-06 05:50:47 +00:00
|
|
|
|
2026-02-05 11:48:33 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public int WgMtu { get; set; }
|
|
|
|
|
|
2026-03-21 08:54:36 +00:00
|
|
|
[Reactive]
|
|
|
|
|
public bool Uot { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string CongestionControl { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
2026-03-21 11:47:04 +00:00
|
|
|
public int? InsecureConcurrency { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public bool NaiveQuic { get; set; }
|
2026-03-21 08:54:36 +00:00
|
|
|
|
2026-03-28 13:18:58 +00:00
|
|
|
[Reactive]
|
2026-03-28 14:51:04 +00:00
|
|
|
public string RawHeaderType { get; set; }
|
2026-03-28 13:18:58 +00:00
|
|
|
|
|
|
|
|
[Reactive]
|
2026-03-28 15:09:30 +00:00
|
|
|
public string Host { get; set; }
|
2026-03-28 13:18:58 +00:00
|
|
|
|
|
|
|
|
[Reactive]
|
2026-03-28 15:09:30 +00:00
|
|
|
public string Path { get; set; }
|
2026-03-28 13:18:58 +00:00
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string XhttpMode { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string XhttpExtra { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string GrpcAuthority { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string GrpcServiceName { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string GrpcMode { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string KcpHeaderType { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public string KcpSeed { get; set; }
|
|
|
|
|
|
|
|
|
|
public string TransportHeaderType
|
|
|
|
|
{
|
|
|
|
|
get => SelectedSource.GetNetwork() switch
|
|
|
|
|
{
|
2026-03-28 14:51:04 +00:00
|
|
|
nameof(ETransport.raw) => RawHeaderType,
|
2026-03-28 13:18:58 +00:00
|
|
|
nameof(ETransport.kcp) => KcpHeaderType,
|
|
|
|
|
nameof(ETransport.xhttp) => XhttpMode,
|
|
|
|
|
nameof(ETransport.grpc) => GrpcMode,
|
|
|
|
|
_ => string.Empty,
|
|
|
|
|
};
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
switch (SelectedSource.GetNetwork())
|
|
|
|
|
{
|
2026-03-28 14:51:04 +00:00
|
|
|
case nameof(ETransport.raw):
|
|
|
|
|
RawHeaderType = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.kcp):
|
|
|
|
|
KcpHeaderType = value;
|
|
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.xhttp):
|
|
|
|
|
XhttpMode = value;
|
|
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.grpc):
|
|
|
|
|
GrpcMode = value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
this.RaisePropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string TransportHost
|
|
|
|
|
{
|
|
|
|
|
get => SelectedSource.GetNetwork() switch
|
|
|
|
|
{
|
2026-03-29 08:48:40 +00:00
|
|
|
nameof(ETransport.raw) => Host,
|
2026-03-28 15:09:30 +00:00
|
|
|
nameof(ETransport.ws) => Host,
|
|
|
|
|
nameof(ETransport.httpupgrade) => Host,
|
|
|
|
|
nameof(ETransport.xhttp) => Host,
|
2026-03-28 13:18:58 +00:00
|
|
|
nameof(ETransport.grpc) => GrpcAuthority,
|
|
|
|
|
_ => string.Empty,
|
|
|
|
|
};
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
switch (SelectedSource.GetNetwork())
|
|
|
|
|
{
|
2026-03-28 14:51:04 +00:00
|
|
|
case nameof(ETransport.raw):
|
2026-03-29 08:48:40 +00:00
|
|
|
Host = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.ws):
|
2026-03-28 15:09:30 +00:00
|
|
|
Host = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.httpupgrade):
|
2026-03-28 15:09:30 +00:00
|
|
|
Host = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.xhttp):
|
2026-03-28 15:09:30 +00:00
|
|
|
Host = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.grpc):
|
|
|
|
|
GrpcAuthority = value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
this.RaisePropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string TransportPath
|
|
|
|
|
{
|
|
|
|
|
get => SelectedSource.GetNetwork() switch
|
|
|
|
|
{
|
|
|
|
|
nameof(ETransport.kcp) => KcpSeed,
|
2026-03-28 15:09:30 +00:00
|
|
|
nameof(ETransport.ws) => Path,
|
|
|
|
|
nameof(ETransport.httpupgrade) => Path,
|
|
|
|
|
nameof(ETransport.xhttp) => Path,
|
2026-03-28 13:18:58 +00:00
|
|
|
nameof(ETransport.grpc) => GrpcServiceName,
|
|
|
|
|
_ => string.Empty,
|
|
|
|
|
};
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
switch (SelectedSource.GetNetwork())
|
|
|
|
|
{
|
|
|
|
|
case nameof(ETransport.kcp):
|
|
|
|
|
KcpSeed = value;
|
|
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.ws):
|
2026-03-28 15:09:30 +00:00
|
|
|
Path = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.httpupgrade):
|
2026-03-28 15:09:30 +00:00
|
|
|
Path = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.xhttp):
|
2026-03-28 15:09:30 +00:00
|
|
|
Path = value;
|
2026-03-28 13:18:58 +00:00
|
|
|
break;
|
|
|
|
|
case nameof(ETransport.grpc):
|
|
|
|
|
GrpcServiceName = value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
this.RaisePropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string TransportExtraText
|
|
|
|
|
{
|
|
|
|
|
get => SelectedSource.GetNetwork() == nameof(ETransport.xhttp)
|
|
|
|
|
? XhttpExtra
|
|
|
|
|
: string.Empty;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (SelectedSource.GetNetwork() == nameof(ETransport.xhttp))
|
|
|
|
|
{
|
|
|
|
|
XhttpExtra = value;
|
|
|
|
|
}
|
|
|
|
|
this.RaisePropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-02 07:17:47 +00:00
|
|
|
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
|
|
|
|
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
2025-04-02 03:44:23 +00:00
|
|
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
2023-01-01 11:42:01 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
public AddServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
|
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
_config = AppManager.Instance.Config;
|
2025-04-02 03:44:23 +00:00
|
|
|
_updateView = updateView;
|
|
|
|
|
|
2025-11-02 07:17:47 +00:00
|
|
|
FetchCertCmd = ReactiveCommand.CreateFromTask(async () =>
|
|
|
|
|
{
|
|
|
|
|
await FetchCert();
|
|
|
|
|
});
|
|
|
|
|
FetchCertChainCmd = ReactiveCommand.CreateFromTask(async () =>
|
|
|
|
|
{
|
|
|
|
|
await FetchCertChain();
|
|
|
|
|
});
|
2025-04-02 03:44:23 +00:00
|
|
|
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
2023-01-01 11:42:01 +00:00
|
|
|
{
|
2025-04-02 03:44:23 +00:00
|
|
|
await SaveServerAsync();
|
|
|
|
|
});
|
2023-01-01 11:42:01 +00:00
|
|
|
|
2025-11-02 07:17:47 +00:00
|
|
|
this.WhenAnyValue(x => x.Cert)
|
|
|
|
|
.Subscribe(_ => UpdateCertTip());
|
|
|
|
|
|
2026-01-17 07:42:40 +00:00
|
|
|
this.WhenAnyValue(x => x.CertSha)
|
|
|
|
|
.Subscribe(_ => UpdateCertTip());
|
|
|
|
|
|
2026-03-28 13:18:58 +00:00
|
|
|
this.WhenAnyValue(x => x.SelectedSource.Network)
|
|
|
|
|
.Subscribe(_ =>
|
|
|
|
|
{
|
|
|
|
|
this.RaisePropertyChanged(nameof(TransportHeaderType));
|
|
|
|
|
this.RaisePropertyChanged(nameof(TransportHost));
|
|
|
|
|
this.RaisePropertyChanged(nameof(TransportPath));
|
|
|
|
|
this.RaisePropertyChanged(nameof(TransportExtraText));
|
|
|
|
|
});
|
|
|
|
|
|
2026-01-17 07:42:40 +00:00
|
|
|
this.WhenAnyValue(x => x.Cert)
|
|
|
|
|
.Subscribe(_ => UpdateCertSha());
|
|
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
if (profileItem.IndexId.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
profileItem.Network = Global.DefaultNetwork;
|
|
|
|
|
profileItem.StreamSecurity = "";
|
|
|
|
|
SelectedSource = profileItem;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SelectedSource = JsonUtils.DeepCopy(profileItem);
|
|
|
|
|
}
|
|
|
|
|
CoreType = SelectedSource?.CoreType?.ToString();
|
2025-11-02 07:17:47 +00:00
|
|
|
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
2026-01-17 11:35:56 +00:00
|
|
|
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
|
2026-02-05 11:48:33 +00:00
|
|
|
|
|
|
|
|
var protocolExtra = SelectedSource?.GetProtocolExtra();
|
2026-03-28 13:18:58 +00:00
|
|
|
var transport = protocolExtra?.Transport ?? new TransportExtra();
|
2026-02-05 11:48:33 +00:00
|
|
|
Ports = protocolExtra?.Ports ?? string.Empty;
|
|
|
|
|
AlterId = int.TryParse(protocolExtra?.AlterId, out var result) ? result : 0;
|
|
|
|
|
Flow = protocolExtra?.Flow ?? string.Empty;
|
|
|
|
|
SalamanderPass = protocolExtra?.SalamanderPass ?? string.Empty;
|
2026-03-10 01:14:57 +00:00
|
|
|
UpMbps = protocolExtra?.UpMbps;
|
|
|
|
|
DownMbps = protocolExtra?.DownMbps;
|
2026-02-05 11:48:33 +00:00
|
|
|
HopInterval = protocolExtra?.HopInterval.IsNullOrEmpty() ?? true ? Global.Hysteria2DefaultHopInt.ToString() : protocolExtra.HopInterval;
|
|
|
|
|
VmessSecurity = protocolExtra?.VmessSecurity?.IsNullOrEmpty() == false ? protocolExtra.VmessSecurity : Global.DefaultSecurity;
|
|
|
|
|
VlessEncryption = protocolExtra?.VlessEncryption.IsNullOrEmpty() == false ? protocolExtra.VlessEncryption : Global.None;
|
|
|
|
|
SsMethod = protocolExtra?.SsMethod ?? string.Empty;
|
|
|
|
|
WgPublicKey = protocolExtra?.WgPublicKey ?? string.Empty;
|
|
|
|
|
WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty;
|
|
|
|
|
WgReserved = protocolExtra?.WgReserved ?? string.Empty;
|
|
|
|
|
WgMtu = protocolExtra?.WgMtu ?? 1280;
|
2026-03-21 08:54:36 +00:00
|
|
|
Uot = protocolExtra?.Uot ?? false;
|
|
|
|
|
CongestionControl = protocolExtra?.CongestionControl ?? string.Empty;
|
2026-03-21 11:47:04 +00:00
|
|
|
InsecureConcurrency = protocolExtra?.InsecureConcurrency > 0 ? protocolExtra.InsecureConcurrency : null;
|
|
|
|
|
NaiveQuic = protocolExtra?.NaiveQuic ?? false;
|
2026-03-28 13:18:58 +00:00
|
|
|
|
2026-03-28 14:51:04 +00:00
|
|
|
RawHeaderType = transport.RawHeaderType ?? Global.None;
|
2026-03-28 15:09:30 +00:00
|
|
|
Host = transport.Host ?? string.Empty;
|
|
|
|
|
Path = transport.Path ?? string.Empty;
|
2026-03-29 08:48:40 +00:00
|
|
|
XhttpMode = transport.XhttpMode ?? Global.DefaultXhttpMode;
|
2026-03-28 13:18:58 +00:00
|
|
|
XhttpExtra = transport.XhttpExtra ?? string.Empty;
|
|
|
|
|
GrpcAuthority = transport.GrpcAuthority ?? string.Empty;
|
|
|
|
|
GrpcServiceName = transport.GrpcServiceName ?? string.Empty;
|
|
|
|
|
GrpcMode = transport.GrpcMode.IsNullOrEmpty() ? Global.GrpcGunMode : transport.GrpcMode;
|
|
|
|
|
KcpHeaderType = transport.KcpHeaderType.IsNullOrEmpty() ? Global.None : transport.KcpHeaderType;
|
|
|
|
|
KcpSeed = transport.KcpSeed ?? string.Empty;
|
|
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
}
|
2024-10-21 05:46:13 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
private async Task SaveServerAsync()
|
|
|
|
|
{
|
|
|
|
|
if (SelectedSource.Remarks.IsNullOrEmpty())
|
|
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
|
2025-04-02 03:44:23 +00:00
|
|
|
return;
|
2023-01-01 11:42:01 +00:00
|
|
|
}
|
|
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
if (SelectedSource.Address.IsNullOrEmpty())
|
|
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.FillServerAddress);
|
2025-04-02 03:44:23 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var port = SelectedSource.Port.ToString();
|
|
|
|
|
if (port.IsNullOrEmpty() || !Utils.IsNumeric(port)
|
|
|
|
|
|| SelectedSource.Port <= 0 || SelectedSource.Port >= Global.MaxPort)
|
2023-01-01 11:42:01 +00:00
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.FillCorrectServerPort);
|
2025-04-02 03:44:23 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (SelectedSource.ConfigType == EConfigType.Shadowsocks)
|
|
|
|
|
{
|
2026-02-05 11:48:33 +00:00
|
|
|
if (SelectedSource.Password.IsNullOrEmpty())
|
2023-01-01 11:42:01 +00:00
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.FillPassword);
|
2023-01-01 11:42:01 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2026-02-05 11:48:33 +00:00
|
|
|
if (SsMethod.IsNullOrEmpty())
|
2023-01-01 11:42:01 +00:00
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectEncryption);
|
2023-01-01 11:42:01 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
}
|
|
|
|
|
if (SelectedSource.ConfigType is not EConfigType.SOCKS and not EConfigType.HTTP)
|
|
|
|
|
{
|
2026-02-05 11:48:33 +00:00
|
|
|
if (SelectedSource.Password.IsNullOrEmpty())
|
2023-01-01 11:42:01 +00:00
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.FillUUID);
|
2023-01-01 11:42:01 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2025-04-02 03:44:23 +00:00
|
|
|
}
|
|
|
|
|
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
2026-01-17 07:42:40 +00:00
|
|
|
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
|
|
|
|
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
|
2026-03-28 13:18:58 +00:00
|
|
|
if (!Global.Networks.Contains(SelectedSource.Network))
|
|
|
|
|
{
|
|
|
|
|
SelectedSource.Network = Global.DefaultNetwork;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var transport = new TransportExtra
|
|
|
|
|
{
|
2026-03-28 14:51:04 +00:00
|
|
|
RawHeaderType = RawHeaderType.NullIfEmpty(),
|
2026-03-28 15:09:30 +00:00
|
|
|
Host = Host.NullIfEmpty(),
|
|
|
|
|
Path = Path.NullIfEmpty(),
|
2026-03-28 13:18:58 +00:00
|
|
|
XhttpMode = XhttpMode.NullIfEmpty(),
|
|
|
|
|
XhttpExtra = XhttpExtra.NullIfEmpty(),
|
|
|
|
|
GrpcAuthority = GrpcAuthority.NullIfEmpty(),
|
|
|
|
|
GrpcServiceName = GrpcServiceName.NullIfEmpty(),
|
|
|
|
|
GrpcMode = GrpcMode.NullIfEmpty(),
|
|
|
|
|
KcpHeaderType = KcpHeaderType.NullIfEmpty(),
|
|
|
|
|
KcpSeed = KcpSeed.NullIfEmpty(),
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-05 11:48:33 +00:00
|
|
|
SelectedSource.SetProtocolExtra(SelectedSource.GetProtocolExtra() with
|
|
|
|
|
{
|
2026-03-28 13:18:58 +00:00
|
|
|
Transport = transport,
|
2026-02-05 11:48:33 +00:00
|
|
|
Ports = Ports.NullIfEmpty(),
|
|
|
|
|
AlterId = AlterId > 0 ? AlterId.ToString() : null,
|
|
|
|
|
Flow = Flow.NullIfEmpty(),
|
|
|
|
|
SalamanderPass = SalamanderPass.NullIfEmpty(),
|
2026-03-10 01:14:57 +00:00
|
|
|
UpMbps = UpMbps,
|
|
|
|
|
DownMbps = DownMbps,
|
2026-02-05 11:48:33 +00:00
|
|
|
HopInterval = HopInterval.NullIfEmpty(),
|
|
|
|
|
VmessSecurity = VmessSecurity.NullIfEmpty(),
|
|
|
|
|
VlessEncryption = VlessEncryption.NullIfEmpty(),
|
|
|
|
|
SsMethod = SsMethod.NullIfEmpty(),
|
|
|
|
|
WgPublicKey = WgPublicKey.NullIfEmpty(),
|
|
|
|
|
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
|
|
|
|
|
WgReserved = WgReserved.NullIfEmpty(),
|
|
|
|
|
WgMtu = WgMtu >= 576 ? WgMtu : null,
|
2026-03-21 08:54:36 +00:00
|
|
|
Uot = Uot ? true : null,
|
|
|
|
|
CongestionControl = CongestionControl.NullIfEmpty(),
|
2026-03-21 11:47:04 +00:00
|
|
|
InsecureConcurrency = InsecureConcurrency > 0 ? InsecureConcurrency : null,
|
|
|
|
|
NaiveQuic = NaiveQuic ? true : null,
|
2026-02-05 11:48:33 +00:00
|
|
|
});
|
2023-01-01 11:42:01 +00:00
|
|
|
|
2025-04-02 03:44:23 +00:00
|
|
|
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
|
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
2025-04-02 03:44:23 +00:00
|
|
|
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-08-17 09:31:55 +00:00
|
|
|
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
2023-01-01 11:42:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-11-02 07:17:47 +00:00
|
|
|
|
2025-11-09 03:20:30 +00:00
|
|
|
private void UpdateCertTip(string? errorMessage = null)
|
2025-11-02 07:17:47 +00:00
|
|
|
{
|
2025-11-09 03:20:30 +00:00
|
|
|
CertTip = errorMessage.IsNullOrEmpty()
|
2026-01-17 07:42:40 +00:00
|
|
|
? ((Cert.IsNullOrEmpty() && CertSha.IsNullOrEmpty()) ? ResUI.CertNotSet : ResUI.CertSet)
|
2025-11-09 03:20:30 +00:00
|
|
|
: errorMessage;
|
2025-11-02 07:17:47 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-17 07:42:40 +00:00
|
|
|
private void UpdateCertSha()
|
|
|
|
|
{
|
|
|
|
|
if (Cert.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var certList = CertPemManager.ParsePemChain(Cert);
|
|
|
|
|
if (certList.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 11:30:29 +00:00
|
|
|
List<string> shaList = [];
|
2026-01-17 07:42:40 +00:00
|
|
|
foreach (var cert in certList)
|
|
|
|
|
{
|
|
|
|
|
var sha = CertPemManager.GetCertSha256Thumbprint(cert);
|
|
|
|
|
if (sha.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
shaList.Add(sha);
|
|
|
|
|
}
|
2026-02-25 11:30:29 +00:00
|
|
|
CertSha = string.Join(',', shaList);
|
2026-01-17 07:42:40 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-02 07:17:47 +00:00
|
|
|
private async Task FetchCert()
|
|
|
|
|
{
|
|
|
|
|
if (SelectedSource.StreamSecurity != Global.StreamSecurity)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var domain = SelectedSource.Address;
|
|
|
|
|
var serverName = SelectedSource.Sni;
|
|
|
|
|
if (serverName.IsNullOrEmpty())
|
|
|
|
|
{
|
2026-03-28 13:18:58 +00:00
|
|
|
serverName = GetCurrentTransportHost();
|
2025-11-02 07:17:47 +00:00
|
|
|
}
|
|
|
|
|
if (serverName.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
serverName = SelectedSource.Address;
|
|
|
|
|
}
|
|
|
|
|
if (SelectedSource.Port > 0)
|
|
|
|
|
{
|
|
|
|
|
domain += $":{SelectedSource.Port}";
|
|
|
|
|
}
|
2026-01-17 07:42:40 +00:00
|
|
|
|
|
|
|
|
(Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
|
2025-11-09 03:20:30 +00:00
|
|
|
UpdateCertTip(certError);
|
2025-11-02 07:17:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task FetchCertChain()
|
|
|
|
|
{
|
|
|
|
|
if (SelectedSource.StreamSecurity != Global.StreamSecurity)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var domain = SelectedSource.Address;
|
|
|
|
|
var serverName = SelectedSource.Sni;
|
|
|
|
|
if (serverName.IsNullOrEmpty())
|
|
|
|
|
{
|
2026-03-28 13:18:58 +00:00
|
|
|
serverName = GetCurrentTransportHost();
|
2025-11-02 07:17:47 +00:00
|
|
|
}
|
|
|
|
|
if (serverName.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
serverName = SelectedSource.Address;
|
|
|
|
|
}
|
|
|
|
|
if (SelectedSource.Port > 0)
|
|
|
|
|
{
|
|
|
|
|
domain += $":{SelectedSource.Port}";
|
|
|
|
|
}
|
2026-01-17 07:42:40 +00:00
|
|
|
|
|
|
|
|
var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
|
2025-11-09 03:20:30 +00:00
|
|
|
Cert = CertPemManager.ConcatenatePemChain(certs);
|
|
|
|
|
UpdateCertTip(certError);
|
2025-11-02 07:17:47 +00:00
|
|
|
}
|
2026-03-28 13:18:58 +00:00
|
|
|
|
|
|
|
|
private string GetCurrentTransportHost()
|
|
|
|
|
{
|
|
|
|
|
return SelectedSource.GetNetwork() switch
|
|
|
|
|
{
|
2026-03-29 08:48:40 +00:00
|
|
|
nameof(ETransport.raw) => Host,
|
2026-03-28 15:09:30 +00:00
|
|
|
nameof(ETransport.ws) => Host,
|
|
|
|
|
nameof(ETransport.httpupgrade) => Host,
|
|
|
|
|
nameof(ETransport.xhttp) => Host,
|
2026-03-28 13:18:58 +00:00
|
|
|
nameof(ETransport.grpc) => GrpcAuthority,
|
|
|
|
|
_ => string.Empty,
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-01-30 09:10:05 +00:00
|
|
|
}
|