using System.Diagnostics; using Cronos; namespace OneDriveBackupService; public class Worker(ILogger logger, ConfigData config, OneDriveClient client) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (Environment.GetCommandLineArgs().Contains("--run-once")) { logger.LogInformation("Manual backup triggered"); await RunBackup(DateTime.Now, stoppingToken); return; } var expression = CronExpression.Parse(config.Schedule); while (!stoppingToken.IsCancellationRequested) { var utcNow = DateTime.UtcNow; var cronTime = expression.GetNextOccurrence(utcNow); var delay = cronTime.GetValueOrDefault(utcNow) - utcNow; if (delay < TimeSpan.Zero) delay = TimeSpan.Zero; if (!cronTime.HasValue) { logger.LogError("Cron expression falied, falling back to default delay"); delay = TimeSpan.FromHours(12); } var nextRun = DateTime.Now + delay; logger.LogInformation("Next backup run: {time}", nextRun.ToString("f")); await Task.Delay(delay, stoppingToken); await RunBackup(nextRun, stoppingToken); } } private async Task RunBackup(DateTime optimalTime, CancellationToken stoppingToken) { logger.LogInformation("Starting backup at {now}", DateTime.Now.ToString("f")); var file = await CreateBackupArchive(optimalTime, stoppingToken); if (string.IsNullOrEmpty(file)) { logger.LogError("Backup archive creation failed"); return; } logger.LogInformation("Backup archive created, starting upload..."); var uploadResult = await client.UploadFile(file, stoppingToken); if (!uploadResult.UploadSucceeded) { logger.LogError("Upload failed"); File.Delete(file); return; } logger.LogInformation("Upload completed"); File.Delete(file); var count = await client.DeleteOldFiles(stoppingToken); logger.LogInformation("Deleted {count} old backups", count); logger.LogInformation("Backup completed"); } private async Task CreateBackupArchive(DateTime optimalTime, CancellationToken stoppingToken) { var timestamp = optimalTime.ToString("yyyyMMdd_HHmmss"); var output = $"/tmp/backup_{timestamp}.tar.gz"; var process = new Process { StartInfo = new ProcessStartInfo { FileName = "tar", ArgumentList = { "-czf", output, "-C", config.LocalRoot, "-T", Path.GetRelativePath(config.LocalRoot, config.IncludeFile) }, RedirectStandardOutput = true, RedirectStandardError = true } }; process.Start(); await process.WaitForExitAsync(stoppingToken); if (process.ExitCode != 0) { return string.Empty; } return output; } }