diff options
Diffstat (limited to 'DomainDig/ContentView.swift')
| -rw-r--r-- | DomainDig/ContentView.swift | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/DomainDig/ContentView.swift b/DomainDig/ContentView.swift index c5c8e50..3716f88 100644 --- a/DomainDig/ContentView.swift +++ b/DomainDig/ContentView.swift @@ -2560,6 +2560,10 @@ struct SettingsView: View { IntegrationsSettingsView() } + NavigationLink("Local API") { + LocalAPISettingsView() + } + NavigationLink("iCloud Sync") { CloudSyncSettingsView() } @@ -2749,6 +2753,123 @@ private struct CloudSyncSettingsView: View { } } +private struct LocalAPISettingsView: View { + @Environment(\.appDensity) private var appDensity + @State private var localAPIService = LocalAPIService.shared + @State private var portText = "" + + var body: some View { + Form { + Section("Local API") { + Toggle( + "Enable Local API", + isOn: Binding( + get: { localAPIService.config.isEnabled }, + set: { localAPIService.setEnabled($0) } + ) + ) + + TextField( + "Port", + text: Binding( + get: { portText }, + set: { newValue in + portText = newValue + if let port = Int(newValue) { + localAPIService.setPort(port) + } + } + ) + ) + .keyboardType(.numberPad) + + LabeledContent("Address", value: localAPIService.address) + LabeledContent("Status", value: localAPIService.isRunning ? "Running" : (localAPIService.config.isEnabled ? "Stopped" : "Disabled")) + LabeledContent("Token", value: localAPIService.maskedToken) + + if let statusMessage = localAPIService.statusMessage { + Text(statusMessage) + .font(appDensity.font(.caption, design: .default)) + .foregroundStyle(.secondary) + } + } + + Section("Authentication") { + Button("Copy Token") { + localAPIService.copyToken() + } + + Button("Rotate Token") { + localAPIService.rotateToken() + } + + Text("Every request requires either `Authorization: Bearer <token>` or `X-API-Token`. DomainDig stores the token in Keychain and only binds the server to localhost.") + .font(appDensity.font(.caption, design: .default)) + .foregroundStyle(.secondary) + } + + Section("Request Logging") { + Toggle( + "Log Requests", + isOn: Binding( + get: { localAPIService.config.requestLoggingEnabled }, + set: { localAPIService.setRequestLoggingEnabled($0) } + ) + ) + + if localAPIService.requestLogs.isEmpty { + Text("No local API requests logged yet.") + .font(appDensity.font(.caption, design: .default)) + .foregroundStyle(.secondary) + } else { + ForEach(localAPIService.requestLogs.prefix(25)) { log in + VStack(alignment: .leading, spacing: 4) { + HStack { + Text("\(log.method) \(log.path)") + .font(appDensity.font(.callout, design: .monospaced)) + Spacer() + Text("\(log.statusCode)") + .font(appDensity.font(.caption, design: .default)) + .foregroundStyle(log.statusCode >= 400 ? .red : .secondary) + } + + Text(log.timestamp.formatted(date: .abbreviated, time: .standard)) + .font(appDensity.font(.caption2, design: .default)) + .foregroundStyle(.secondary) + + Text("\(Int(log.duration * 1000)) ms") + .font(appDensity.font(.caption2, design: .default)) + .foregroundStyle(.secondary) + } + } + } + + Button("Clear Logs", role: .destructive) { + localAPIService.clearRequestLogs() + } + } + + Section("Control") { + Button("Restart Server") { + localAPIService.setEnabled(false) + localAPIService.setEnabled(true) + } + .disabled(!localAPIService.config.isEnabled) + + Button("Stop Server") { + localAPIService.stopServer() + } + .disabled(!localAPIService.isRunning) + } + } + .navigationTitle("Local API") + .onAppear { + portText = String(localAPIService.config.port) + localAPIService.refresh() + } + } +} + private struct MonitoringSettingsView: View { @Environment(\.appDensity) private var appDensity @Bindable var viewModel: DomainViewModel |
