mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
Ensure tailscale can be disabled when not running
This commit is contained in:
parent
8814f2623f
commit
459cf52ef6
2 changed files with 160 additions and 82 deletions
|
|
@ -311,102 +311,155 @@ private struct TailscaleIntegrationSection: View {
|
|||
.buttonStyle(.link)
|
||||
.controlSize(.small)
|
||||
}
|
||||
} else if tailscaleService.isRunning {
|
||||
// Tailscale Serve toggle
|
||||
HStack {
|
||||
Toggle("Enable Tailscale Serve Integration", isOn: $tailscaleServeEnabled)
|
||||
.onChange(of: tailscaleServeEnabled) { _, newValue in
|
||||
logger.info("Tailscale Serve integration \(newValue ? "enabled" : "disabled")")
|
||||
// Restart server to apply the new setting
|
||||
Task {
|
||||
await serverManager.restart()
|
||||
} else if !tailscaleService.isRunning {
|
||||
// Show Tailscale preferences even when not running
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
// Tailscale Serve toggle - always available when installed
|
||||
HStack {
|
||||
Toggle("Enable Tailscale Serve Integration", isOn: $tailscaleServeEnabled)
|
||||
.onChange(of: tailscaleServeEnabled) { _, newValue in
|
||||
logger.info("Tailscale Serve integration \(newValue ? "enabled" : "disabled")")
|
||||
// Restart server to apply the new setting
|
||||
Task {
|
||||
await serverManager.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
Spacer()
|
||||
|
||||
if tailscaleServeEnabled {
|
||||
// Show status indicator - fixed height to prevent jumping
|
||||
HStack(spacing: 4) {
|
||||
if tailscaleServeStatus.isLoading {
|
||||
ProgressView()
|
||||
.scaleEffect(0.7)
|
||||
} else if tailscaleServeStatus.isRunning {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("Running")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} else if let error = tailscaleServeStatus.lastError {
|
||||
// Show status when enabled but not running
|
||||
if tailscaleServeEnabled {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.help("Error: \(error)")
|
||||
Text("Error")
|
||||
Text("Tailscale not running")
|
||||
.font(.caption)
|
||||
.foregroundColor(.orange)
|
||||
} else {
|
||||
Image(systemName: "circle")
|
||||
.foregroundColor(.gray)
|
||||
Text("Starting...")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.frame(height: 16)
|
||||
}
|
||||
.frame(height: 16) // Fixed height prevents UI jumping
|
||||
}
|
||||
|
||||
// Show action button to start Tailscale
|
||||
if tailscaleService.isInstalled && !tailscaleService.isRunning {
|
||||
Button(action: {
|
||||
tailscaleService.openTailscaleApp()
|
||||
}, label: {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "play.circle")
|
||||
Text("Start Tailscale")
|
||||
}
|
||||
})
|
||||
.buttonStyle(.borderedProminent)
|
||||
.controlSize(.small)
|
||||
}
|
||||
|
||||
// Show help text about what will happen when enabled
|
||||
if tailscaleServeEnabled {
|
||||
Text("Tailscale Serve will activate automatically when Tailscale is running.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Tailscale is running - show full interface
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
// Tailscale Serve toggle
|
||||
HStack {
|
||||
Toggle("Enable Tailscale Serve Integration", isOn: $tailscaleServeEnabled)
|
||||
.onChange(of: tailscaleServeEnabled) { _, newValue in
|
||||
logger.info("Tailscale Serve integration \(newValue ? "enabled" : "disabled")")
|
||||
// Restart server to apply the new setting
|
||||
Task {
|
||||
await serverManager.restart()
|
||||
}
|
||||
}
|
||||
|
||||
// Show dashboard URL when running
|
||||
if let hostname = tailscaleService.tailscaleHostname {
|
||||
InlineClickableURLView(
|
||||
label: "Access VibeTunnel at:",
|
||||
url: TailscaleURLHelper.constructURL(
|
||||
hostname: hostname,
|
||||
port: serverPort,
|
||||
isTailscaleServeEnabled: tailscaleServeEnabled
|
||||
)?.absoluteString ?? ""
|
||||
)
|
||||
Spacer()
|
||||
|
||||
// Show warning if in localhost-only mode
|
||||
if accessMode == .localhost && !tailscaleServeEnabled {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 12))
|
||||
if tailscaleServeEnabled {
|
||||
// Show status indicator - fixed height to prevent jumping
|
||||
HStack(spacing: 4) {
|
||||
if tailscaleServeStatus.isLoading {
|
||||
ProgressView()
|
||||
.scaleEffect(0.7)
|
||||
} else if tailscaleServeStatus.isRunning {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("Running")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} else if let error = tailscaleServeStatus.lastError {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.help("Error: \(error)")
|
||||
Text("Error")
|
||||
.font(.caption)
|
||||
.foregroundColor(.orange)
|
||||
} else {
|
||||
Image(systemName: "circle")
|
||||
.foregroundColor(.gray)
|
||||
Text("Starting...")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.frame(height: 16) // Fixed height prevents UI jumping
|
||||
}
|
||||
}
|
||||
|
||||
// Show dashboard URL when running
|
||||
if let hostname = tailscaleService.tailscaleHostname {
|
||||
InlineClickableURLView(
|
||||
label: "Access VibeTunnel at:",
|
||||
url: TailscaleURLHelper.constructURL(
|
||||
hostname: hostname,
|
||||
port: serverPort,
|
||||
isTailscaleServeEnabled: tailscaleServeEnabled
|
||||
)?.absoluteString ?? ""
|
||||
)
|
||||
|
||||
// Show warning if in localhost-only mode
|
||||
if accessMode == .localhost && !tailscaleServeEnabled {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 12))
|
||||
Text(
|
||||
"Server is in localhost-only mode. Change to 'Network' mode above to access via Tailscale."
|
||||
)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
// Show error details if any
|
||||
if tailscaleServeEnabled, let error = tailscaleServeStatus.lastError {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 12))
|
||||
Text("Error: \(error)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.orange)
|
||||
.lineLimit(2)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
.padding(.horizontal, 8)
|
||||
.background(Color.orange.opacity(0.1))
|
||||
.cornerRadius(4)
|
||||
}
|
||||
|
||||
// Help text about Tailscale Serve
|
||||
if tailscaleServeEnabled && tailscaleServeStatus.isRunning {
|
||||
Text(
|
||||
"Server is in localhost-only mode. Change to 'Network' mode above to access via Tailscale."
|
||||
"Tailscale Serve provides secure access with automatic authentication using Tailscale identity headers."
|
||||
)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.top, 4)
|
||||
}
|
||||
}
|
||||
|
||||
// Show error details if any
|
||||
if tailscaleServeEnabled, let error = tailscaleServeStatus.lastError {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 12))
|
||||
Text("Error: \(error)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.orange)
|
||||
.lineLimit(2)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
.padding(.horizontal, 8)
|
||||
.background(Color.orange.opacity(0.1))
|
||||
.cornerRadius(4)
|
||||
}
|
||||
|
||||
// Help text about Tailscale Serve
|
||||
if tailscaleServeEnabled && tailscaleServeStatus.isRunning {
|
||||
Text(
|
||||
"Tailscale Serve provides secure access with automatic authentication using Tailscale identity headers."
|
||||
)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.top, 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@ struct AccessModeView: View {
|
|||
|
||||
@AppStorage(AppConstants.UserDefaultsKeys.tailscaleServeEnabled)
|
||||
private var tailscaleServeEnabled = false
|
||||
|
||||
@Environment(TailscaleService.self)
|
||||
private var tailscaleService
|
||||
@Environment(TailscaleServeStatusService.self)
|
||||
private var tailscaleServeStatus
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
|
|
@ -95,14 +100,14 @@ struct AccessModeView: View {
|
|||
.font(.callout)
|
||||
Spacer()
|
||||
|
||||
if tailscaleServeEnabled {
|
||||
// When Tailscale Serve is enabled, force localhost mode
|
||||
if shouldLockToLocalhost {
|
||||
// Only lock when Tailscale Serve is actually working
|
||||
Text("Localhost")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Image(systemName: "lock.shield.fill")
|
||||
.foregroundColor(.blue)
|
||||
.help("Tailscale Serve requires localhost binding for security")
|
||||
.help("Tailscale Serve active - locked to localhost for security")
|
||||
} else {
|
||||
Picker("", selection: $accessModeString) {
|
||||
ForEach(DashboardAccessMode.allCases, id: \.rawValue) { mode in
|
||||
|
|
@ -117,7 +122,20 @@ struct AccessModeView: View {
|
|||
}
|
||||
}
|
||||
|
||||
if tailscaleServeEnabled && accessMode == .network {
|
||||
// Show warning when Tailscale Serve is enabled but not working
|
||||
if tailscaleServeEnabled && !shouldLockToLocalhost {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.font(.caption)
|
||||
Text("Tailscale Serve enabled but not active - using selected access mode")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
// Show info when Tailscale Serve is active and locked
|
||||
if shouldLockToLocalhost && accessMode == .network {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "info.circle.fill")
|
||||
.foregroundColor(.blue)
|
||||
|
|
@ -129,6 +147,13 @@ struct AccessModeView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only lock to localhost when Tailscale Serve is enabled AND actually working
|
||||
private var shouldLockToLocalhost: Bool {
|
||||
tailscaleServeEnabled &&
|
||||
tailscaleService.isRunning &&
|
||||
tailscaleServeStatus.isRunning
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Port Configuration View
|
||||
|
|
|
|||
Loading…
Reference in a new issue