Erster Commit des bestehenden monolithischen WinForms-Copytraders, inklusive der Alt-Backups (*.bak), damit diese dauerhaft in der Historie rekonstruierbar bleiben. Threema-Lib unter libs/ wurde vendored (nested .git entfernt). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
165 lines
5.3 KiB
C#
165 lines
5.3 KiB
C#
using System;
|
|
using MongoDB.Driver;
|
|
using PolyTraderSharp.Extensions;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Hosting;
|
|
using PolyTraderSharp.Models;
|
|
|
|
namespace PolyTraderSharp.Services
|
|
{
|
|
public class MullvadVpnService : BackgroundService
|
|
{
|
|
private readonly TerminalLogger _logger;
|
|
private ServerSettings _settings;
|
|
private readonly string _settingsPath = "server_settings.xml";
|
|
private bool _isConnected = false;
|
|
private int _consecutiveFailures = 0;
|
|
private readonly int _maxRetries = 3;
|
|
|
|
public bool IsConnected => _isConnected;
|
|
|
|
public MullvadVpnService(TerminalLogger logger)
|
|
{
|
|
_logger = logger;
|
|
_settings = ServerSettings.Load(_settingsPath);
|
|
}
|
|
|
|
public void ReloadSettings()
|
|
{
|
|
_settings = ServerSettings.Load(_settingsPath);
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
if (_settings.VpnEnabled)
|
|
{
|
|
await HealthCheckAsync();
|
|
}
|
|
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
|
|
}
|
|
}
|
|
|
|
public async Task<bool> HealthCheckAsync()
|
|
{
|
|
if (!_settings.VpnEnabled) return true;
|
|
|
|
string status = await RunCliAsync("status");
|
|
if (status.Contains("Connected"))
|
|
{
|
|
_isConnected = true;
|
|
_consecutiveFailures = 0;
|
|
return true;
|
|
}
|
|
|
|
// Try reconnecting
|
|
_logger.Warning("VPN is not connected. Attempting to reconnect...");
|
|
if (await ConnectAsync()) return true;
|
|
|
|
_consecutiveFailures++;
|
|
if (_consecutiveFailures >= _maxRetries)
|
|
{
|
|
_logger.Error($"VPN unrecoverable after {_maxRetries} retries.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public async Task<bool> ConnectAsync()
|
|
{
|
|
if (!_settings.VpnEnabled) return true;
|
|
|
|
string status = await RunCliAsync("status");
|
|
if (status.Contains("Connected"))
|
|
{
|
|
if (string.IsNullOrEmpty(_settings.VpnLocation) || status.Contains(_settings.VpnLocation, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_logger.Info($"VPN already connected tightly to {_settings.VpnLocation}");
|
|
_isConnected = true;
|
|
_consecutiveFailures = 0;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(_settings.MullvadAccount))
|
|
{
|
|
await RunCliAsync($"account login {_settings.MullvadAccount}");
|
|
await Task.Delay(1000);
|
|
}
|
|
|
|
await RunCliAsync("lan set allow");
|
|
|
|
if (!string.IsNullOrEmpty(_settings.VpnLocation))
|
|
{
|
|
await RunCliAsync($"relay set location {_settings.VpnLocation}");
|
|
await Task.Delay(1000);
|
|
}
|
|
|
|
await RunCliAsync("connect");
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
await Task.Delay(2000);
|
|
string check = await RunCliAsync("status");
|
|
if (check.Contains("Connected"))
|
|
{
|
|
_isConnected = true;
|
|
_consecutiveFailures = 0;
|
|
_logger.Info($"VPN connected successfully: {check.Trim()}");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
_logger.Error("VPN failed to connect after waiting");
|
|
_consecutiveFailures++;
|
|
return false;
|
|
}
|
|
|
|
public async Task<bool> DisconnectAsync()
|
|
{
|
|
string result = await RunCliAsync("disconnect");
|
|
_isConnected = false;
|
|
_logger.Info("VPN disconnected.");
|
|
return true;
|
|
}
|
|
|
|
private async Task<string> RunCliAsync(string args)
|
|
{
|
|
try
|
|
{
|
|
if (!File.Exists(_settings.MullvadCliPath))
|
|
{
|
|
_logger.Error($"Mullvad CLI not found at: {_settings.MullvadCliPath}");
|
|
return string.Empty;
|
|
}
|
|
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = _settings.MullvadCliPath,
|
|
Arguments = args,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
using var process = Process.Start(psi);
|
|
if (process == null) return string.Empty;
|
|
|
|
await process.WaitForExitAsync();
|
|
string output = await process.StandardOutput.ReadToEndAsync();
|
|
string err = await process.StandardError.ReadToEndAsync();
|
|
return string.IsNullOrWhiteSpace(output) ? err : output;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error($"Mullvad CLI exc: {ex.Message}");
|
|
return string.Empty;
|
|
}
|
|
}
|
|
}
|
|
}
|