import Foundation /// Centralized rules engine that checks proxy rules (block list, map local, DNS spoofing, no-cache) /// against live traffic. All methods are static and synchronous for use in NIO pipeline handlers. public enum RulesEngine { private static let rulesRepo = RulesRepository() // MARK: - Block List /// Returns the `BlockAction` if the given URL + method matches an enabled block rule, or nil. public static func checkBlockList(url: String, method: String) -> BlockAction? { guard IPCManager.shared.isBlockListEnabled else { return nil } do { let entries = try rulesRepo.fetchEnabledBlockEntries() for entry in entries { guard entry.method == "ANY" || entry.method == method else { continue } if blockEntry(entry, matches: url) { return entry.action } } } catch { print("[RulesEngine] Failed to check block list: \(error)") } return nil } // MARK: - Map Local /// Returns the first matching `MapLocalRule` for the URL + method, or nil. public static func checkMapLocal(url: String, method: String) -> MapLocalRule? { do { let rules = try rulesRepo.fetchEnabledMapLocalRules() for rule in rules { guard rule.method == "ANY" || rule.method == method else { continue } if WildcardMatcher.matches(url, pattern: rule.urlPattern) { return rule } } } catch { print("[RulesEngine] Failed to check map local rules: \(error)") } return nil } // MARK: - DNS Spoofing /// Returns the target domain if the given domain matches an enabled DNS spoof rule, or nil. public static func checkDNSSpoof(domain: String) -> String? { guard IPCManager.shared.isDNSSpoofingEnabled else { return nil } do { let rules = try rulesRepo.fetchEnabledDNSSpoofRules() for rule in rules { if WildcardMatcher.matches(domain, pattern: rule.sourceDomain) { return rule.targetDomain } } } catch { print("[RulesEngine] Failed to check DNS spoof rules: \(error)") } return nil } // MARK: - No-Cache /// Returns true if the no-caching toggle is enabled. public static func shouldStripCache() -> Bool { IPCManager.shared.isNoCachingEnabled } private static func blockEntry(_ entry: BlockListEntry, matches url: String) -> Bool { if WildcardMatcher.matches(url, pattern: entry.urlPattern) { return true } guard entry.includeSubpaths else { return false } guard !entry.urlPattern.contains("*"), !entry.urlPattern.contains("?") else { return false } let normalizedPattern = entry.urlPattern.hasSuffix("/") ? entry.urlPattern : "\(entry.urlPattern)/" return url == entry.urlPattern || url.hasPrefix(normalizedPattern) } }