diff options
| author | Christian Cleberg <[email protected]> | 2026-04-25 00:25:18 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-25 00:25:18 -0500 |
| commit | 1715e59287a7b48b29ed8cbf2cd45fcc92ec3126 (patch) | |
| tree | cdca41731ecf7eff1aa83b20dc2a7c5fd33e649f /DomainDig/DomainMonitoringService.swift | |
| parent | fd34553ab0e63b4d3bc22532b61f4fef32846f34 (diff) | |
| download | domain-dig-1715e59287a7b48b29ed8cbf2cd45fcc92ec3126.tar.gz domain-dig-1715e59287a7b48b29ed8cbf2cd45fcc92ec3126.tar.bz2 domain-dig-1715e59287a7b48b29ed8cbf2cd45fcc92ec3126.zip | |
DomainDig v4.0.0 — Integrations
* Outbound webhook delivery
* Native Slack webhook integration
* SMTP-based email alerts
* Per-integration event filtering
* Reliable local delivery queue
* Delivery logs and retry visibility
* Integration management UI
* Normalized event severity model
* Canonical structured event payloads
Diffstat (limited to 'DomainDig/DomainMonitoringService.swift')
| -rw-r--r-- | DomainDig/DomainMonitoringService.swift | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/DomainDig/DomainMonitoringService.swift b/DomainDig/DomainMonitoringService.swift index 5034402..31b1505 100644 --- a/DomainDig/DomainMonitoringService.swift +++ b/DomainDig/DomainMonitoringService.swift @@ -361,6 +361,12 @@ final class DomainMonitoringService { errors: errors ) saveLog(log) + let outboundEvents = monitoringEvents(from: log) + if outboundEvents.isEmpty { + IntegrationService.shared.recordNoOutboundEvents(for: log.summary) + } else { + IntegrationService.shared.enqueue(events: outboundEvents) + } return MonitoringRunOutcome( success: errors.count < results.count, @@ -375,6 +381,91 @@ final class DomainMonitoringService { MonitoringStorage.saveLogs(logs) } + private func monitoringEvents(from log: MonitoringLog) -> [MonitoringEvent] { + var events: [MonitoringEvent] = log.checkedDomains.compactMap { result in + if let errorMessage = result.errorMessage { + return MonitoringEvent( + type: .monitoringFailure, + severity: .critical, + domain: result.domain, + timestamp: result.checkedAt, + summary: errorMessage, + details: [ + "trigger": log.trigger.rawValue, + "resultSource": result.resultSource.rawValue + ] + ) + } + + if result.certificateWarningLevel == .critical { + return MonitoringEvent( + type: .certificateExpiring, + severity: .critical, + domain: result.domain, + timestamp: result.checkedAt, + summary: result.summaryMessage, + details: [ + "certificateWarningLevel": result.certificateWarningLevel.rawValue, + "trigger": log.trigger.rawValue + ] + ) + } + + guard let alertSeverity = result.alertSeverity else { + return nil + } + + return MonitoringEvent( + type: eventType(for: result.summaryMessage), + severity: EventSeverity(monitoringSeverity: alertSeverity), + domain: result.domain, + timestamp: result.checkedAt, + summary: result.summaryMessage, + details: [ + "alertSeverity": alertSeverity.title, + "certificateWarningLevel": result.certificateWarningLevel.rawValue, + "resultSource": result.resultSource.rawValue, + "trigger": log.trigger.rawValue + ] + ) + } + + if !log.errors.isEmpty { + events.append( + MonitoringEvent( + type: .monitoringFailure, + severity: .critical, + domain: "portfolio", + timestamp: log.timestamp, + summary: "Monitoring run completed with errors", + details: [ + "errors": log.errors.joined(separator: " | "), + "trigger": log.trigger.rawValue + ] + ) + ) + } + + return events + } + + private func eventType(for summary: String) -> MonitoringEventType { + let normalized = summary.lowercased() + if normalized.contains("dns") { + return .dnsChanged + } + if normalized.contains("certificate") { + return .certificateUpdated + } + if normalized.contains("redirect") { + return .redirectChanged + } + if normalized.contains("header") { + return .headersChanged + } + return .changeDetected + } + private func latestSnapshot(for trackedDomain: TrackedDomain, history: [HistoryEntry]) -> LookupSnapshot? { history.first(where: { entry in if let trackedDomainID = entry.trackedDomainID { |
