diff --git a/Services/KeyScannerService.cs b/Services/KeyScannerService.cs
index 3cc3330..2ab7b9c 100644
--- a/Services/KeyScannerService.cs
+++ b/Services/KeyScannerService.cs
@@ -23,7 +23,7 @@ public class KeyScannerService : BackgroundService
private string _githubToken => _settings.Get("GitHubToken");
private string _serperApiKey => _settings.Get("SerperApiKey");
- private int _intervalHours => _settings.GetInt("ScanIntervalHours", 0);
+ private int _dailyHour => _settings.GetInt("ScanDailyHour", -1);
// Status-Tracking
private readonly object _lock = new();
@@ -125,45 +125,43 @@ public class KeyScannerService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- var hasGitHub = !string.IsNullOrWhiteSpace(_githubToken);
- var hasSerper = !string.IsNullOrWhiteSpace(_serperApiKey);
-
- if (!hasGitHub && !hasSerper)
- {
- _logger.LogInformation("KeyScannerService deaktiviert (kein GitHubToken/SerperApiKey konfiguriert)");
- // Warte trotzdem auf manuelles Trigger-Signal
- await WaitForTriggerOnly(stoppingToken);
- return;
- }
-
- if (_intervalHours <= 0)
- {
- _logger.LogInformation("KeyScannerService: Kein Intervall konfiguriert, nur manueller Trigger aktiv");
- await WaitForTriggerOnly(stoppingToken);
- return;
- }
-
- _logger.LogInformation("KeyScannerService gestartet (Intervall: {Hours}h)", _intervalHours);
+ _logger.LogInformation("KeyScannerService gestartet");
while (!stoppingToken.IsCancellationRequested)
{
- await RunScan(stoppingToken);
+ // Berechne Wartezeit bis zum nächsten Scan oder warte auf manuellen Trigger
+ var waitTime = GetTimeUntilNextScan();
+ if (waitTime != null)
+ _logger.LogInformation("KeyScannerService: Nächster Full-Scan um {Hour}:00 (in {Minutes} Min)", _dailyHour, (int)waitTime.Value.TotalMinutes);
- // Warte auf Intervall oder manuelles Signal
- var waitResult = await WaitWithTrigger(TimeSpan.FromHours(_intervalHours), stoppingToken);
- if (waitResult) _logger.LogInformation("KeyScannerService: Manueller Scan angefordert");
+ var triggered = await WaitWithTrigger(waitTime ?? Timeout.InfiniteTimeSpan, stoppingToken);
+ if (stoppingToken.IsCancellationRequested) break;
+
+ if (triggered)
+ {
+ _logger.LogInformation("KeyScannerService: Manueller Scan angefordert");
+ }
+ else
+ {
+ // Automatischer täglicher Full-Scan
+ lock (_lock) _scanMode = "full";
+ _logger.LogInformation("KeyScannerService: Täglicher Full-Scan gestartet ({Hour}:00)", _dailyHour);
+ }
+
+ await RunScan(stoppingToken);
}
}
- /// Wartet nur auf manuelles Trigger-Signal (kein automatisches Intervall).
- private async Task WaitForTriggerOnly(CancellationToken stoppingToken)
+ /// Berechnet die Wartezeit bis zur konfigurierten Scan-Stunde. Null wenn deaktiviert (-1).
+ private TimeSpan? GetTimeUntilNextScan()
{
- while (!stoppingToken.IsCancellationRequested)
- {
- await WaitWithTrigger(Timeout.InfiniteTimeSpan, stoppingToken);
- if (!stoppingToken.IsCancellationRequested)
- await RunScan(stoppingToken);
- }
+ var hour = _dailyHour;
+ if (hour < 0 || hour > 23) return null;
+
+ var now = DateTime.Now;
+ var nextRun = new DateTime(now.Year, now.Month, now.Day, hour, 0, 0);
+ if (nextRun <= now) nextRun = nextRun.AddDays(1);
+ return nextRun - now;
}
/// Wartet auf Timeout oder Trigger-Signal. Gibt true zurück wenn getriggert.
diff --git a/Services/ServerSettingsService.cs b/Services/ServerSettingsService.cs
index 7eec3f3..2c1da2b 100644
--- a/Services/ServerSettingsService.cs
+++ b/Services/ServerSettingsService.cs
@@ -33,7 +33,7 @@ public class ServerSettingsService
new SettingDefinition( "GeminiApiKey", "string", "", "Gemini API-Key (für Gemini-Provider im Chat)", "password" ),
new SettingDefinition( "GitHubToken", "string", "", "GitHub PAT für Key-Scanner und Code-Search Tool", "password" ),
new SettingDefinition( "SerperApiKey", "string", "", "Serper.dev API-Key für Google-Suche (Key-Scanner + Tool)", "password" ),
- new SettingDefinition( "ScanIntervalHours", "int", "0", "Key-Scanner Intervall in Stunden (0 = nur manuell)", "number" ),
+ new SettingDefinition( "ScanDailyHour", "int", "-1", "Key-Scanner Full-Scan Uhrzeit (0-23, -1 = deaktiviert)", "number" ),
new SettingDefinition( "GitHubEventScanner", "bool", "false", "GitHub Event Scanner (Echtzeit-Monitoring öffentlicher Pushes)", "toggle" ),
new SettingDefinition( "HuggingFaceToken", "string", "", "HuggingFace Token (optional, erhöht Rate-Limits)", "password" ),
new SettingDefinition( "ScannerKeyTypes", "string_array", "anthropic_oauth,krea_ai", "Key-Scanner: Aktive Key-Typen", "checkboxes" ),