Initial commit: IBKRTrader

.NET WinForms-Anwendung (Core, Modules/CongressTrading, UI).
Enthaelt .gitignore und settings.example.json als Konfigurationsvorlage.
Echte settings.json mit Zugangsdaten ist bewusst ausgeschlossen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-26 18:19:47 +02:00
co-authored by Claude Opus 4.8
commit ebeb035e92
47 changed files with 4527 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
using IBKRTrader.Core.Database;
using IBKRTrader.Core.Logging;
using IBKRTrader.Core.Settings;
namespace IBKRTrader.Core.Workers.BuiltIn;
/// <summary>
/// BackupWorker (Core) läuft alle 30 Minuten (konfigurierbar).
/// 1. Ruft mysqldump.exe auf → SQL-Dump in Backups\DB\
/// 2. Kopiert Logs\ → Backups\Logs\
/// Falls mysqldump nicht gefunden: Warnung und Überspringen.
/// </summary>
public class BackupWorker : WorkerBase
{
private readonly SettingsService _settings;
public override string Name => "BackupWorker";
public override string Module => "Core";
public override WorkerType Type => WorkerType.Worker;
protected override TimeSpan? Interval =>
TimeSpan.FromMinutes(_settings.Settings.WorkerSettings.BackupWorker.IntervalMinutes);
public BackupWorker(LoggingService logger, DatabaseService db, SettingsService settings)
: base(logger, db)
{
_settings = settings;
Info.Active = settings.Settings.WorkerSettings.BackupWorker.Enabled;
}
protected override async Task ExecuteAsync(CancellationToken ct)
{
var timestamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm");
var backupRoot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Backups");
var dbDir = Path.Combine(backupRoot, "DB");
var logsDir = Path.Combine(backupRoot, "Logs");
Directory.CreateDirectory(dbDir);
Directory.CreateDirectory(logsDir);
// ── 1. Datenbank-Dump ────────────────────────────────────────────────
await RunMysqlDumpAsync(dbDir, timestamp, ct);
// ── 2. Logs-Backup ───────────────────────────────────────────────────
CopyLogs(logsDir, timestamp);
Logger.Info(Module, $"Backup abgeschlossen: {timestamp}");
}
// ─── mysqldump ────────────────────────────────────────────────────────────
private async Task RunMysqlDumpAsync(string dbDir, string timestamp, CancellationToken ct)
{
var dump = FindMysqldump();
if (dump == null)
{
Logger.Warn(Module, "mysqldump.exe nicht gefunden DB-Backup übersprungen.");
return;
}
var db = _settings.Settings.Database;
var dumpFile = Path.Combine(dbDir, $"{db.Database}_{timestamp}.sql");
var args = $"--host={db.Host} --port={db.Port} " +
$"--user={db.User} --password={db.Password} " +
$"--single-transaction --routines --triggers " +
$"{db.Database} --result-file=\"{dumpFile}\"";
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = dump,
Arguments = args,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var proc = System.Diagnostics.Process.Start(psi)
?? throw new InvalidOperationException("mysqldump konnte nicht gestartet werden.");
await proc.WaitForExitAsync(ct);
if (proc.ExitCode != 0)
{
var err = await proc.StandardError.ReadToEndAsync(ct);
Logger.Warn(Module, $"mysqldump Exitcode {proc.ExitCode}: {err}");
}
else
{
Logger.Info(Module, $"DB-Dump erstellt: {Path.GetFileName(dumpFile)}");
}
}
private static string? FindMysqldump()
{
// Häufige Installationspfade auf Windows-Server
var candidates = new[]
{
"mysqldump.exe",
@"C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqldump.exe",
@"C:\Program Files\MySQL\MySQL Server 8.4\bin\mysqldump.exe",
@"C:\xampp\mysql\bin\mysqldump.exe"
};
foreach (var c in candidates)
if (File.Exists(c)) return c;
// PATH-Suche
var pathVar = Environment.GetEnvironmentVariable("PATH") ?? "";
foreach (var dir in pathVar.Split(';'))
{
var full = Path.Combine(dir.Trim(), "mysqldump.exe");
if (File.Exists(full)) return full;
}
return null;
}
// ─── Logs kopieren ────────────────────────────────────────────────────────
private void CopyLogs(string logsBackupDir, string timestamp)
{
try
{
var srcLogs = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
if (!Directory.Exists(srcLogs)) return;
var destDir = Path.Combine(logsBackupDir, timestamp);
CopyDirectory(srcLogs, destDir);
Logger.Info(Module, $"Logs-Backup erstellt: {destDir}");
}
catch (Exception ex)
{
Logger.Warn(Module, $"Logs-Backup fehlgeschlagen: {ex.Message}");
}
}
private static void CopyDirectory(string src, string dest)
{
Directory.CreateDirectory(dest);
foreach (var file in Directory.GetFiles(src))
File.Copy(file, Path.Combine(dest, Path.GetFileName(file)), overwrite: true);
foreach (var dir in Directory.GetDirectories(src))
CopyDirectory(dir, Path.Combine(dest, Path.GetFileName(dir)));
}
}