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

@@ -1,6 +1,8 @@
import SwiftUI
import NetworkExtension
@preconcurrency import NetworkExtension
import LocalAuthentication
import ProxyCore
import GRDB
@Observable
@MainActor
@@ -8,14 +10,44 @@ final class AppState {
var vpnStatus: NEVPNStatus = .disconnected
var isCertificateInstalled: Bool = false
var isCertificateTrusted: Bool = false
var isLocked: Bool = false
var runtimeStatus = ProxyRuntimeStatus()
var isAppLockEnabled: Bool {
get { UserDefaults.standard.bool(forKey: "appLockEnabled") }
set { UserDefaults.standard.set(newValue, forKey: "appLockEnabled") }
}
private var vpnManager: NETunnelProviderManager?
private var statusObservation: NSObjectProtocol?
@ObservationIgnored private var runtimeObservation: AnyDatabaseCancellable?
private let runtimeStatusRepo = RuntimeStatusRepository()
init() {
if UserDefaults.standard.bool(forKey: "appLockEnabled") {
isLocked = true
}
isCertificateInstalled = CertificateManager.shared.hasCA
Task {
await loadVPNManager()
}
observeRuntimeStatus()
}
func authenticate() {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
isLocked = false
return
}
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Unlock Proxy") { [weak self] success, _ in
Task { @MainActor in
if success {
self?.isLocked = false
}
}
}
}
func loadVPNManager() async {
@@ -70,6 +102,19 @@ final class AppState {
vpnStatus == .connected
}
var hasSharedCertificate: Bool {
guard let localFingerprint = CertificateManager.shared.caFingerprint else { return false }
return localFingerprint == runtimeStatus.caFingerprint
}
var isHTTPSInspectionVerified: Bool {
runtimeStatus.lastSuccessfulMITMAt != nil
}
var lastRuntimeError: String? {
runtimeStatus.lastMITMError ?? runtimeStatus.lastConnectError ?? runtimeStatus.lastProxyError
}
var vpnStatusText: String {
switch vpnStatus {
case .connected: "Connected"
@@ -100,4 +145,17 @@ final class AppState {
}
}
}
private func observeRuntimeStatus() {
runtimeObservation = runtimeStatusRepo.observeStatus()
.start(in: DatabaseManager.shared.dbPool) { error in
ProxyLogger.ui.error("AppState runtime observation error: \(error.localizedDescription)")
} onChange: { [weak self] status in
Task { @MainActor in
self?.runtimeStatus = status
self?.isCertificateInstalled = CertificateManager.shared.hasCA
self?.isCertificateTrusted = status.lastSuccessfulMITMAt != nil
}
}
}
}