Add iPad support, auto-pinning, and comprehensive logging

- Adaptive iPhone/iPad layout with NavigationSplitView sidebar
- Auto-detect SSL-pinned domains, fall back to passthrough
- Certificate install via local HTTP server (Safari profile flow)
- App Group-backed CA, per-domain leaf cert LRU cache
- DB-backed config repository, Darwin notification throttling
- Rules engine, breakpoint rules, pinned domain tracking
- os.Logger instrumentation across tunnel/proxy/mitm/capture/cert/rules/db/ipc/ui
- Fix dyld framework embed, race conditions, thread safety
This commit is contained in:
Trey t
2026-04-11 12:52:18 -05:00
parent c77e506db5
commit 148bc3887c
77 changed files with 6710 additions and 847 deletions

View File

@@ -8,6 +8,8 @@ struct HomeView: View {
@State private var searchText = ""
@State private var showClearConfirmation = false
@State private var observation: AnyDatabaseCancellable?
@State private var didInitializeObservers = false
@State private var refreshSequence = 0
private let trafficRepo = TrafficRepository()
@@ -52,6 +54,23 @@ struct HomeView: View {
.foregroundStyle(.tertiary)
}
}
.contextMenu {
Button {
addToSSLProxyingList(domain: group.domain)
} label: {
Label("Add to SSL Proxying", systemImage: "lock.shield")
}
Button {
addToBlockList(domain: group.domain)
} label: {
Label("Add to Block List", systemImage: "xmark.shield")
}
Button(role: .destructive) {
try? trafficRepo.deleteForDomain(group.domain)
} label: {
Label("Delete Domain", systemImage: "trash")
}
}
}
}
}
@@ -86,19 +105,70 @@ struct HomeView: View {
} message: {
Text("This will permanently delete all captured traffic.")
}
.onAppear {
ProxyLogger.ui.info("HomeView: onAppear vpnConnected=\(appState.isVPNConnected) domains=\(domains.count)")
}
.onDisappear {
ProxyLogger.ui.info("HomeView: onDisappear domains=\(domains.count)")
}
.task {
startObservation()
guard !didInitializeObservers else {
ProxyLogger.ui.info("HomeView: task rerun ignored; observers already initialized")
return
}
didInitializeObservers = true
ProxyLogger.ui.info("HomeView: initial task setup")
startObservation(source: "initial")
observeNewTraffic()
}
}
private func startObservation() {
private let rulesRepo = RulesRepository()
private func addToSSLProxyingList(domain: String) {
var entry = SSLProxyingEntry(domainPattern: domain, isInclude: true)
try? rulesRepo.insertSSLEntry(&entry)
// Auto-enable SSL proxying if not already
if !IPCManager.shared.isSSLProxyingEnabled {
IPCManager.shared.isSSLProxyingEnabled = true
}
IPCManager.shared.post(.configurationChanged)
}
private func addToBlockList(domain: String) {
var entry = BlockListEntry(urlPattern: "*\(domain)*")
try? rulesRepo.insertBlockEntry(&entry)
if !IPCManager.shared.isBlockListEnabled {
IPCManager.shared.isBlockListEnabled = true
}
IPCManager.shared.post(.configurationChanged)
}
private func startObservation(source: String) {
refreshSequence += 1
let sequence = refreshSequence
ProxyLogger.ui.info("HomeView: starting GRDB observation source=\(source) seq=\(sequence)")
observation?.cancel()
observation = trafficRepo.observeDomainGroups()
.start(in: DatabaseManager.shared.dbPool) { error in
print("[HomeView] Observation error: \(error)")
ProxyLogger.ui.error("HomeView: observation error source=\(source) seq=\(sequence) error=\(error.localizedDescription)")
} onChange: { newDomains in
let preview = newDomains.prefix(3).map(\.domain).joined(separator: ", ")
ProxyLogger.ui.info("HomeView: domains updated source=\(source) seq=\(sequence) count=\(newDomains.count) preview=\(preview)")
withAnimation {
domains = newDomains
}
}
}
private func observeNewTraffic() {
ProxyLogger.ui.info("HomeView: registering Darwin notification observer")
IPCManager.shared.observe(.newTrafficCaptured) {
ProxyLogger.ui.info("HomeView: Darwin notification received")
DispatchQueue.main.async {
startObservation(source: "darwin")
}
}
}
}