Commit 7b880ea410
Unsigned
Layout: unified · split
DomainDig/DomainMonitoringService.swift +41 −2
| @@ -291,6 +291,12 @@ final class DomainMonitoringService { | ||
| 291 | 291 | previousSnapshot: previousSnapshot |
| 292 | 292 | ) |
| 293 | 293 | let snapshot = Self.resolvedSnapshotAfterFallback(inspectedSnapshot, previousSnapshot: previousSnapshot) |
| 294 | // When the fallback fires, `snapshot` *is* `previousSnapshot`, so | |
| 295 | // every comparison below is old-against-old and would otherwise | |
| 296 | // report "No meaningful changes" for a domain we never reached. | |
| 297 | let unreachableReason = (previousSnapshot != nil && Self.shouldFallbackToSnapshot(inspectedSnapshot)) | |
| 298 | ? Self.firstErrorMessage(in: inspectedSnapshot) | |
| 299 | : nil | |
| 294 | 300 | let savedEntry: HistoryEntry? |
| 295 | 301 | if snapshot.statusMessage == nil { |
| 296 | 302 | savedEntry = persistSnapshot( |
| @@ -330,14 +336,16 @@ final class DomainMonitoringService { | ||
| 330 | 336 | historyEntryID: savedEntry?.id ?? snapshot.historyEntryID, |
| 331 | 337 | checkedAt: now, |
| 332 | 338 | didChange: alertDescriptor != nil, |
| 333 | summaryMessage: snapshot.statusMessage | |
| 339 | summaryMessage: unreachableReason.map { "Could not check — kept the previous result. \($0)" } | |
| 340 | ?? snapshot.statusMessage | |
| 334 | 341 | ?? alertDescriptor?.message |
| 335 | 342 | ?? savedEntry?.changeSummary?.message |
| 336 | 343 | ?? "No meaningful changes", |
| 337 | 344 | alertSeverity: alertDescriptor?.severity, |
| 338 | 345 | certificateWarningLevel: DomainDiffService.certificateWarningLevel(for: snapshot), |
| 339 | 346 | resultSource: snapshot.resultSource, |
| 340 | errorMessage: snapshot.statusMessage | |
| 347 | errorMessage: snapshot.statusMessage, | |
| 348 | unreachableReason: unreachableReason | |
| 341 | 349 | ) |
| 342 | 350 | results.append(result) |
| 343 | 351 | |
| @@ -397,6 +405,21 @@ final class DomainMonitoringService { | ||
| 397 | 405 | ) |
| 398 | 406 | } |
| 399 | 407 | |
| 408 | if let unreachableReason = result.unreachableReason { | |
| 409 | return MonitoringEvent( | |
| 410 | type: .monitoringFailure, | |
| 411 | severity: .warning, | |
| 412 | domain: result.domain, | |
| 413 | timestamp: result.checkedAt, | |
| 414 | summary: result.summaryMessage, | |
| 415 | details: [ | |
| 416 | "reason": unreachableReason, | |
| 417 | "trigger": log.trigger.rawValue, | |
| 418 | "resultSource": result.resultSource.rawValue | |
| 419 | ] | |
| 420 | ) | |
| 421 | } | |
| 422 | ||
| 400 | 423 | if result.certificateWarningLevel == .critical { |
| 401 | 424 | return MonitoringEvent( |
| 402 | 425 | type: .certificateExpiring, |
| @@ -943,6 +966,22 @@ final class DomainMonitoringService { | ||
| 943 | 966 | ) |
| 944 | 967 | } |
| 945 | 968 | |
| 969 | /// First reported error on a snapshot, used to explain a fallback. Mirrors | |
| 970 | /// the ordering `shouldFallbackToSnapshot` inspects. | |
| 971 | private static func firstErrorMessage(in snapshot: LookupSnapshot) -> String { | |
| 972 | [ | |
| 973 | snapshot.dnsError, | |
| 974 | snapshot.httpHeadersError, | |
| 975 | snapshot.sslError, | |
| 976 | snapshot.ownershipError, | |
| 977 | snapshot.subdomainsError, | |
| 978 | snapshot.redirectChainError, | |
| 979 | snapshot.ipGeolocationError | |
| 980 | ] | |
| 981 | .compactMap { $0 } | |
| 982 | .first ?? "The lookup could not reach the domain." | |
| 983 | } | |
| 984 | ||
| 946 | 985 | private static func shouldFallbackToSnapshot(_ snapshot: LookupSnapshot) -> Bool { |
| 947 | 986 | let candidateMessages = [ |
| 948 | 987 | snapshot.dnsError, |
DomainDig/Models.swift +7 −1
| @@ -1544,6 +1544,10 @@ struct MonitoringDomainResult: Codable, Identifiable, Equatable { | ||
| 1544 | 1544 | let certificateWarningLevel: CertificateWarningLevel |
| 1545 | 1545 | let resultSource: LookupResultSource |
| 1546 | 1546 | let errorMessage: String? |
| 1547 | /// Non-nil when the lookup failed and the previous snapshot was reused, so | |
| 1548 | /// this run compared the old data against itself and cannot claim the | |
| 1549 | /// domain is unchanged. Optional so already-persisted logs still decode. | |
| 1550 | let unreachableReason: String? | |
| 1547 | 1551 | |
| 1548 | 1552 | init( |
| 1549 | 1553 | id: UUID = UUID(), |
| @@ -1555,7 +1559,8 @@ struct MonitoringDomainResult: Codable, Identifiable, Equatable { | ||
| 1555 | 1559 | alertSeverity: MonitoringAlertSeverity?, |
| 1556 | 1560 | certificateWarningLevel: CertificateWarningLevel, |
| 1557 | 1561 | resultSource: LookupResultSource, |
| 1558 | errorMessage: String? = nil | |
| 1562 | errorMessage: String? = nil, | |
| 1563 | unreachableReason: String? = nil | |
| 1559 | 1564 | ) { |
| 1560 | 1565 | self.id = id |
| 1561 | 1566 | self.domain = domain |
| @@ -1567,6 +1572,7 @@ struct MonitoringDomainResult: Codable, Identifiable, Equatable { | ||
| 1567 | 1572 | self.certificateWarningLevel = certificateWarningLevel |
| 1568 | 1573 | self.resultSource = resultSource |
| 1569 | 1574 | self.errorMessage = errorMessage |
| 1575 | self.unreachableReason = unreachableReason | |
| 1570 | 1576 | } |
| 1571 | 1577 | } |
| 1572 | 1578 | |