diff --git a/Deploy-LoginMonitor.ps1 b/Deploy-LoginMonitor.ps1 index ce40b76..2840abb 100644 --- a/Deploy-LoginMonitor.ps1 +++ b/Deploy-LoginMonitor.ps1 @@ -644,12 +644,35 @@ function Get-RdpMonitorSacBlockFromExample { } return @( '# --- Security Alert Center (SAC) ---' - '$UseSAC = ''dual''' + '$UseSAC = ''fallback''' '$SacUrl = ''https://sac.kalinamall.ru''' '$SacApiKey = ''sac_CHANGE_ME''' ) -join "`r`n" } +function Sync-RdpMonitorUseSacFallbackMode { + param([string]$LocalSettings) + + if (-not (Test-Path -LiteralPath $LocalSettings)) { return $false } + $c = Get-RdpMonitorSettingsRaw -Path $LocalSettings + if ([string]::IsNullOrWhiteSpace($c)) { return $false } + if ($c -match '(?m)^\s*\$UseSAC\s*=\s*[''"]fallback[''"]') { return $false } + if ($c -notmatch '(?m)^\s*\$UseSAC\s*=') { return $false } + + $newContent = [regex]::Replace( + $c, + '(?m)^(\s*\$UseSAC\s*=\s*)[''"][^''"]+[''"]', + '$1''fallback''' + ) + if ($newContent -eq $c) { return $false } + + $bak = "$LocalSettings.bak.$(Get-Date -Format 'yyyyMMdd_HHmmss')" + Copy-Item -LiteralPath $LocalSettings -Destination $bak -Force + [System.IO.File]::WriteAllText($LocalSettings, $newContent.TrimEnd() + "`r`n", $Utf8Bom) + Write-DeployLog "login_monitor.settings.ps1: UseSAC переключён на fallback (резервная копия: $bak)." + return $true +} + function Update-RdpMonitorSettingsSacBlockIfMissing { param( [string]$LocalSettings, @@ -675,7 +698,7 @@ function Update-RdpMonitorSettingsSacBlockIfMissing { $bak = "$LocalSettings.bak.$(Get-Date -Format 'yyyyMMdd_HHmmss')" Copy-Item -LiteralPath $LocalSettings -Destination $bak -Force - Write-DeployLog "Добавление блока SAC (UseSAC=dual) в login_monitor.settings.ps1; резервная копия: $bak" + Write-DeployLog "Добавление блока SAC (UseSAC=fallback) в login_monitor.settings.ps1; резервная копия: $bak" if ($c -match '(?m)^\s*\$UseSAC\s*=') { $c = [regex]::Replace($c, '(?m)^\s*\$UseSAC\s*=.*$', '') @@ -689,7 +712,7 @@ function Update-RdpMonitorSettingsSacBlockIfMissing { $newContent = ($c.TrimEnd() + "`r`n`r`n" + $sacBlock + "`r`n").TrimEnd() + "`r`n" [System.IO.File]::WriteAllText($LocalSettings, $newContent, $Utf8Bom) - Write-DeployLog "login_monitor.settings.ps1: добавлен блок SAC (UseSAC=dual из example на шаре)." + Write-DeployLog "login_monitor.settings.ps1: добавлен блок SAC (UseSAC=fallback из example на шаре)." return $true } @@ -717,7 +740,7 @@ function Sync-RdpMonitorSettingsFromShare { } if ($needsCreate) { - Write-DeployLog "Создан login_monitor.settings.ps1 из example (Telegram + SAC dual)." + Write-DeployLog "Создан login_monitor.settings.ps1 из example (Telegram + SAC fallback)." Copy-Item -LiteralPath $ExampleOnShare -Destination $LocalSettings -Force Invoke-RdpMonitorSettingsPostPatches -LocalSettings $LocalSettings | Out-Null return @@ -831,6 +854,7 @@ try { } $cmp = Compare-VersionStrings -Left $shareVerRaw -Right $localVerRaw + $isScriptVersionUpgrade = ($null -ne $cmp -and $cmp -gt 0 -and -not [string]::IsNullOrWhiteSpace($localVerRaw)) if ($null -ne $cmp) { if ($cmp -eq 0) { if (-not $needsSacBootstrap) { @@ -869,7 +893,7 @@ try { if ($WhatIf) { if ($needsSettingsBootstrap) { - Write-DeployLog "[WhatIf] Донастроить login_monitor.settings.ps1 (SAC dual из example)." + Write-DeployLog "[WhatIf] Донастроить login_monitor.settings.ps1 (SAC fallback из example)." } if ($needsBundleSync) { Write-DeployLog "[WhatIf] Синхронизировать пакет: $($DeployBundleFiles -join ', ')." @@ -889,6 +913,10 @@ try { Sync-RdpMonitorSettingsFromShare -ExampleOnShare $settingsExampleShare -LocalSettings $settingsLocal + if ($isScriptVersionUpgrade) { + Sync-RdpMonitorUseSacFallbackMode -LocalSettings $settingsLocal | Out-Null + } + $installArgs = @( '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $LocalScript, '-InstallTasks', '-SkipImmediateMainRun' ) diff --git a/Login_Monitor.ps1 b/Login_Monitor.ps1 index 90e7ec1..644d794 100644 --- a/Login_Monitor.ps1 +++ b/Login_Monitor.ps1 @@ -3109,28 +3109,70 @@ function Test-DailyReportEnabledFlag { return $true } +function Read-LastDailyReportSentDate { + if (-not (Test-Path -LiteralPath $LastReportFile)) { return $null } + $txt = Get-Content -LiteralPath $LastReportFile -ErrorAction SilentlyContinue | Select-Object -First 1 + if ([string]::IsNullOrWhiteSpace($txt)) { return $null } + $raw = $txt.Trim() + if ($raw -match '^(\d{4}-\d{2}-\d{2})') { + try { return [datetime]::ParseExact($matches[1], 'yyyy-MM-dd', $null).Date } catch { } + } + try { + return ([datetime]::ParseExact($raw, 'yyyy-MM-dd HH:mm:ss', $null)).Date + } catch { + return $null + } +} + +function Try-ClaimDailyReportSlotToday { + param([datetime]$Now) + + $lockPath = Join-Path $script:InstallRoot 'Logs\.daily_report_claim.lock' + $lockStream = $null + try { + $lockStream = [System.IO.File]::Open( + $lockPath, + [System.IO.FileMode]::OpenOrCreate, + [System.IO.FileAccess]::ReadWrite, + [System.IO.FileShare]::None + ) + } catch { + Write-Log 'Ежедневный отчёт: другой процесс уже отправляет (lock).' + return $false + } + + try { + $lastDate = Read-LastDailyReportSentDate + if ($null -ne $lastDate -and $lastDate -ge $Now.Date) { + return $false + } + Write-TextFileUtf8Bom -Path $LastReportFile -Text ($Now.ToString('yyyy-MM-dd')) + return $true + } finally { + if ($null -ne $lockStream) { + $lockStream.Dispose() + } + Remove-Item -LiteralPath $lockPath -Force -ErrorAction SilentlyContinue + } +} + function Check-AndSendDailyReport { if (-not (Test-DailyReportEnabledFlag)) { return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute) } - $lastReport = $null - if (Test-Path $LastReportFile) { - $txt = Get-Content $LastReportFile -ErrorAction SilentlyContinue - if ($txt) { $lastReport = [datetime]::ParseExact($txt, "yyyy-MM-dd HH:mm:ss", $null) } - } $now = Get-Date $reportSlotToday = Get-Date -Year $now.Year -Month $now.Month -Day $now.Day -Hour $DailyReportHour -Minute $DailyReportMinute -Second 0 $shouldSend = $false if ($now -ge $reportSlotToday) { - if ($null -eq $lastReport) { + $lastDate = Read-LastDailyReportSentDate + if ($null -eq $lastDate -or $lastDate -lt $now.Date) { $shouldSend = $true - } else { - $dLast = $lastReport.Date - if ($dLast -lt $now.Date) { $shouldSend = $true } } } - if ($shouldSend) { Send-DailyReport | Out-Null } + if ($shouldSend -and (Try-ClaimDailyReportSlotToday -Now $now)) { + Send-DailyReport | Out-Null + } return (Get-NextLocalSlotBoundary -Hour $DailyReportHour -Minute $DailyReportMinute) } diff --git a/login_monitor.settings.example.ps1 b/login_monitor.settings.example.ps1 index 588874d..edc6bf7 100644 --- a/login_monitor.settings.example.ps1 +++ b/login_monitor.settings.example.ps1 @@ -34,7 +34,7 @@ $NotifyOrder = 'tg' # --- Security Alert Center (SAC) --- # off | exclusive | dual | fallback — см. security-alert-center/docs/agent-integration.md -$UseSAC = 'dual' +$UseSAC = 'fallback' $SacUrl = 'https://sac.kalinamall.ru' $SacApiKey = 'sac_UkOsAT3UWiQS54KK5OJPBDCSucysQDrKFju28wmYiz8' # $SacSpoolDir = 'C:\ProgramData\RDP-login-monitor\sac-spool'