Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

130 строки
8.5KB

  1. param(
  2. [Parameter(Mandatory = $true)][ValidateSet('Recover','ReviewOptOut','Resume','PauseAll','Cancel','ResolveAccepted','AuthorizeRetry','Status')][string]$Action,
  3. [int]$CampaignId,
  4. [int]$DeliveryId,
  5. [string]$ConfigPath = (Join-Path (Split-Path -Parent $PSScriptRoot) 'config.ini')
  6. )
  7. $ErrorActionPreference = 'Stop'
  8. . (Join-Path $PSScriptRoot 'dispatch-common.ps1')
  9. $context = Get-MailContext $ConfigPath
  10. $lock = Enter-DispatchLock -WaitMilliseconds 30000
  11. $db = $null
  12. try {
  13. $db = Open-MailDatabase $context
  14. if ($Action -eq 'Status') {
  15. $state = Get-OperationalState $db
  16. $command = New-DbCommand $db "SELECT COUNT(*) FROM [Campaign] WHERE [Status]='running'"
  17. $running = [int]$command.ExecuteScalar()
  18. $counts = @{}
  19. foreach ($status in @('pending','in_flight','accepted','uncertain','suppressed','canceled')) {
  20. $command = New-DbCommand $db 'SELECT COUNT(*) FROM [Delivery] WHERE [Status]=?'
  21. Add-TextParameter $command 'status' 30 $status
  22. $counts[$status] = [int]$command.ExecuteScalar()
  23. }
  24. $clock = Get-DispatchClock $db $context.Settings['SMTP_USER'].ToLowerInvariant() (Get-QueueInterval $context)
  25. $nextText = if ($null -eq $clock.Next) { $null } else { $clock.Next.ToString('o') }
  26. $reviewText = if ($null -eq $state.Reviewed) { $null } else { $state.Reviewed.ToString('o') }
  27. [pscustomobject]@{ Paused = $state.Paused; RunningCampaigns = $running;
  28. Counts = $counts; NextAllowedAttemptUtc = $nextText;
  29. LastOptOutReviewUtc = $reviewText } | ConvertTo-Json -Compress -Depth 4
  30. return
  31. }
  32. $transaction = $db.BeginTransaction()
  33. try {
  34. if ($Action -eq 'Recover') {
  35. $command = New-DbCommand $db "UPDATE [Campaign] SET [Status]='approved_paused' WHERE [Status]='running'" $transaction
  36. $null = $command.ExecuteNonQuery()
  37. $command = New-DbCommand $db 'UPDATE [OperationalState] SET [GlobalPause]=TRUE WHERE [Id]=1' $transaction
  38. $null = $command.ExecuteNonQuery()
  39. $command = New-DbCommand $db "UPDATE [Attempt] SET [Outcome]='uncertain',[EndedAtUtc]=?,[SanitizedError]='HTA session ended before provider outcome was recorded' WHERE [Outcome]='started'" $transaction
  40. $null = $command.Parameters.Add('ended', [System.Data.OleDb.OleDbType]::Date)
  41. $command.Parameters[0].Value = [DateTime]::UtcNow
  42. $null = $command.ExecuteNonQuery()
  43. $command = New-DbCommand $db "UPDATE [Delivery] SET [Status]='uncertain',[LastErrorCode]='session_interrupted' WHERE [Status]='in_flight'" $transaction
  44. $null = $command.ExecuteNonQuery()
  45. Write-CampaignAudit $db 'dispatcher_recovered' 'session' 'Open-window dispatcher started paused' $transaction
  46. $message = 'Recovered previous session; all campaigns are paused.'
  47. }
  48. elseif ($Action -eq 'ReviewOptOut') {
  49. $command = New-DbCommand $db 'UPDATE [OperationalState] SET [LastOptOutReviewAtUtc]=? WHERE [Id]=1' $transaction
  50. $null = $command.Parameters.Add('reviewed', [System.Data.OleDb.OleDbType]::Date)
  51. $command.Parameters[0].Value = [DateTime]::UtcNow
  52. $null = $command.ExecuteNonQuery()
  53. Write-CampaignAudit $db 'opt_out_reviewed' 'mailbox' 'Operator confirmed reply mailbox review' $transaction
  54. $message = 'Reply mailbox review recorded.'
  55. }
  56. elseif ($Action -eq 'PauseAll') {
  57. $command = New-DbCommand $db "UPDATE [Campaign] SET [Status]='approved_paused' WHERE [Status]='running'" $transaction
  58. $null = $command.ExecuteNonQuery()
  59. $command = New-DbCommand $db 'UPDATE [OperationalState] SET [GlobalPause]=TRUE WHERE [Id]=1' $transaction
  60. $null = $command.ExecuteNonQuery()
  61. Write-CampaignAudit $db 'dispatch_paused' 'all' 'Operator paused all campaigns' $transaction
  62. $message = 'All campaigns paused. A current in-flight attempt may finish.'
  63. }
  64. elseif ($Action -eq 'Resume') {
  65. if ($CampaignId -lt 1) { throw 'Select a campaign to resume' }
  66. $null = Get-CampaignIdentity $context
  67. $null = Get-QueueInterval $context
  68. $state = Get-OperationalState $db $transaction
  69. Assert-OptOutReviewCurrent $context $state
  70. $command = New-DbCommand $db "SELECT COUNT(*) FROM [Delivery] WHERE [Status]='uncertain'" $transaction
  71. if ([int]$command.ExecuteScalar() -gt 0) {
  72. throw 'Resolve uncertain deliveries before resuming any campaign'
  73. }
  74. $command = New-DbCommand $db "SELECT COUNT(*) FROM [Delivery] WHERE [CampaignId]=? AND [Status]='pending'" $transaction
  75. $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::Integer)
  76. $command.Parameters[0].Value = $CampaignId
  77. if ([int]$command.ExecuteScalar() -lt 1) { throw 'Campaign has no pending deliveries' }
  78. $command = New-DbCommand $db "UPDATE [Campaign] SET [Status]='running' WHERE [Id]=? AND [Status]='approved_paused'" $transaction
  79. $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::Integer)
  80. $command.Parameters[0].Value = $CampaignId
  81. if ($command.ExecuteNonQuery() -ne 1) { throw 'Campaign is not paused or does not exist' }
  82. $command = New-DbCommand $db 'UPDATE [OperationalState] SET [GlobalPause]=FALSE WHERE [Id]=1' $transaction
  83. $null = $command.ExecuteNonQuery()
  84. Write-CampaignAudit $db 'campaign_resumed' ([string]$CampaignId) 'Operator explicitly resumed campaign' $transaction
  85. $message = "Campaign $CampaignId resumed. Dispatch runs only while this HTA is open."
  86. }
  87. elseif ($Action -eq 'Cancel') {
  88. if ($CampaignId -lt 1) { throw 'Select a campaign to cancel' }
  89. $command = New-DbCommand $db "UPDATE [Campaign] SET [Status]='canceled' WHERE [Id]=? AND [Status] IN ('approved_paused','running')" $transaction
  90. $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::Integer)
  91. $command.Parameters[0].Value = $CampaignId
  92. if ($command.ExecuteNonQuery() -ne 1) { throw 'Campaign is not active or does not exist' }
  93. $command = New-DbCommand $db "UPDATE [Delivery] SET [Status]='canceled' WHERE [CampaignId]=? AND [Status]='pending'" $transaction
  94. $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::Integer)
  95. $command.Parameters[0].Value = $CampaignId
  96. $null = $command.ExecuteNonQuery()
  97. Write-CampaignAudit $db 'campaign_canceled' ([string]$CampaignId) 'Operator canceled pending deliveries' $transaction
  98. $message = "Campaign $CampaignId canceled. A current in-flight attempt may finish."
  99. }
  100. elseif ($Action -in @('ResolveAccepted','AuthorizeRetry')) {
  101. if ($DeliveryId -lt 1) { throw 'Select an uncertain delivery' }
  102. $state = Get-OperationalState $db $transaction
  103. if (-not $state.Paused) { throw 'Pause all campaigns before resolving an uncertain delivery' }
  104. if ($Action -eq 'AuthorizeRetry') {
  105. $command = New-DbCommand $db "UPDATE [Delivery] SET [Status]='pending',[LastErrorCode]='manual_retry_authorized' WHERE [Id]=? AND [Status]='uncertain' AND [AttemptCount]<3" $transaction
  106. }
  107. else {
  108. $command = New-DbCommand $db "UPDATE [Delivery] SET [Status]='accepted',[LastErrorCode]='operator_verified' WHERE [Id]=? AND [Status]='uncertain'" $transaction
  109. }
  110. $null = $command.Parameters.Add('id', [System.Data.OleDb.OleDbType]::Integer)
  111. $command.Parameters[0].Value = $DeliveryId
  112. if ($command.ExecuteNonQuery() -ne 1) {
  113. throw 'Delivery is not uncertain, or the three-attempt manual retry limit was reached'
  114. }
  115. $auditAction = if ($Action -eq 'AuthorizeRetry') { 'retry_authorized' } else { 'delivery_verified_accepted' }
  116. Write-CampaignAudit $db $auditAction ([string]$DeliveryId) 'Operator reconciled uncertain provider result' $transaction
  117. $message = if ($Action -eq 'AuthorizeRetry') {
  118. "Delivery $DeliveryId is pending for one explicitly authorized retry. Resume the campaign when ready."
  119. } else { "Delivery $DeliveryId marked accepted after operator verification." }
  120. }
  121. $transaction.Commit()
  122. Write-Output $message
  123. }
  124. catch { $transaction.Rollback(); throw }
  125. }
  126. finally {
  127. if ($db) { $db.Dispose() }
  128. $lock.Dispose()
  129. }

Powered by TurnKey Linux.