Baseline: Ausgangszustand vor Modularisierung
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>
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user