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:
@@ -7,6 +7,9 @@ struct SSLProxyingListView: View {
|
||||
@State private var entries: [SSLProxyingEntry] = []
|
||||
@State private var showAddInclude = false
|
||||
@State private var showAddExclude = false
|
||||
@State private var editingEntry: SSLProxyingEntry?
|
||||
@State private var entryToDelete: SSLProxyingEntry?
|
||||
@State private var showDeleteConfirmation = false
|
||||
@State private var observation: AnyDatabaseCancellable?
|
||||
|
||||
private let rulesRepo = RulesRepository()
|
||||
@@ -41,13 +44,17 @@ struct SSLProxyingListView: View {
|
||||
.font(.subheadline)
|
||||
} else {
|
||||
ForEach(includeEntries) { entry in
|
||||
Text(entry.domainPattern)
|
||||
Button {
|
||||
editingEntry = entry
|
||||
} label: {
|
||||
Text(entry.domainPattern)
|
||||
.foregroundStyle(.primary)
|
||||
}
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
for index in indexSet {
|
||||
if let id = includeEntries[index].id {
|
||||
try? rulesRepo.deleteSSLEntry(id: id)
|
||||
}
|
||||
if let index = indexSet.first {
|
||||
entryToDelete = includeEntries[index]
|
||||
showDeleteConfirmation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,13 +67,17 @@ struct SSLProxyingListView: View {
|
||||
.font(.subheadline)
|
||||
} else {
|
||||
ForEach(excludeEntries) { entry in
|
||||
Text(entry.domainPattern)
|
||||
Button {
|
||||
editingEntry = entry
|
||||
} label: {
|
||||
Text(entry.domainPattern)
|
||||
.foregroundStyle(.primary)
|
||||
}
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
for index in indexSet {
|
||||
if let id = excludeEntries[index].id {
|
||||
try? rulesRepo.deleteSSLEntry(id: id)
|
||||
}
|
||||
if let index = indexSet.first {
|
||||
entryToDelete = excludeEntries[index]
|
||||
showDeleteConfirmation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,6 +92,7 @@ struct SSLProxyingListView: View {
|
||||
Divider()
|
||||
Button("Clear All Rules", role: .destructive) {
|
||||
try? rulesRepo.deleteAllSSLEntries()
|
||||
IPCManager.shared.post(.configurationChanged)
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "ellipsis.circle")
|
||||
@@ -91,12 +103,37 @@ struct SSLProxyingListView: View {
|
||||
DomainEntrySheet(title: "New Include Entry", isInclude: true) { pattern in
|
||||
var entry = SSLProxyingEntry(domainPattern: pattern, isInclude: true)
|
||||
try? rulesRepo.insertSSLEntry(&entry)
|
||||
IPCManager.shared.isSSLProxyingEnabled = true
|
||||
IPCManager.shared.post(.configurationChanged)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showAddExclude) {
|
||||
DomainEntrySheet(title: "New Exclude Entry", isInclude: false) { pattern in
|
||||
var entry = SSLProxyingEntry(domainPattern: pattern, isInclude: false)
|
||||
try? rulesRepo.insertSSLEntry(&entry)
|
||||
IPCManager.shared.post(.configurationChanged)
|
||||
}
|
||||
}
|
||||
.sheet(item: $editingEntry) { entry in
|
||||
DomainEntrySheet(
|
||||
title: entry.isInclude ? "Edit Include Entry" : "Edit Exclude Entry",
|
||||
isInclude: entry.isInclude,
|
||||
existingEntry: entry
|
||||
) { pattern in
|
||||
var updated = entry
|
||||
updated.domainPattern = pattern
|
||||
try? rulesRepo.updateSSLEntry(updated)
|
||||
if updated.isInclude {
|
||||
IPCManager.shared.isSSLProxyingEnabled = true
|
||||
}
|
||||
IPCManager.shared.post(.configurationChanged)
|
||||
}
|
||||
}
|
||||
.confirmationDialog("Delete this rule?", isPresented: $showDeleteConfirmation, presenting: entryToDelete) { entry in
|
||||
Button("Delete", role: .destructive) {
|
||||
if let id = entry.id {
|
||||
try? rulesRepo.deleteSSLEntry(id: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
@@ -115,11 +152,19 @@ struct SSLProxyingListView: View {
|
||||
struct DomainEntrySheet: View {
|
||||
let title: String
|
||||
let isInclude: Bool
|
||||
let existingEntry: SSLProxyingEntry?
|
||||
let onSave: (String) -> Void
|
||||
|
||||
@State private var domainPattern = ""
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
init(title: String, isInclude: Bool, existingEntry: SSLProxyingEntry? = nil, onSave: @escaping (String) -> Void) {
|
||||
self.title = title
|
||||
self.isInclude = isInclude
|
||||
self.existingEntry = existingEntry
|
||||
self.onSave = onSave
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
@@ -145,6 +190,11 @@ struct DomainEntrySheet: View {
|
||||
.disabled(domainPattern.isEmpty)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if let entry = existingEntry {
|
||||
domainPattern = entry.domainPattern
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user