mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
Add local bypass
This commit is contained in:
parent
fff51301cf
commit
bb6934de5d
4 changed files with 84 additions and 21 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import OSLog
|
import OSLog
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
/// Server state enumeration
|
/// Server state enumeration
|
||||||
enum ServerState {
|
enum ServerState {
|
||||||
|
|
@ -45,6 +46,21 @@ final class BunServer {
|
||||||
|
|
||||||
var bindAddress: String = "127.0.0.1"
|
var bindAddress: String = "127.0.0.1"
|
||||||
|
|
||||||
|
/// Local authentication token for bypassing auth on localhost
|
||||||
|
private let localAuthToken: String = {
|
||||||
|
// Generate a secure random token for this session
|
||||||
|
let randomData = Data((0..<32).map { _ in UInt8.random(in: 0...255) })
|
||||||
|
return randomData.base64EncodedString()
|
||||||
|
.replacingOccurrences(of: "+", with: "-")
|
||||||
|
.replacingOccurrences(of: "/", with: "_")
|
||||||
|
.replacingOccurrences(of: "=", with: "")
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// Get the local auth token for use in HTTP requests
|
||||||
|
var localToken: String {
|
||||||
|
localAuthToken
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
@ -151,6 +167,13 @@ final class BunServer {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add local bypass authentication for the Mac app
|
||||||
|
if authMode != "none" {
|
||||||
|
// Enable local bypass with our generated token
|
||||||
|
vibetunnelArgs += " --allow-local-bypass --local-auth-token \(localAuthToken)"
|
||||||
|
logger.info("Local authentication bypass enabled for Mac app")
|
||||||
|
}
|
||||||
|
|
||||||
// Create wrapper to run vibetunnel with a parent death signal
|
// Create wrapper to run vibetunnel with a parent death signal
|
||||||
// Using a subshell that monitors parent process and kills vibetunnel if parent dies
|
// Using a subshell that monitors parent process and kills vibetunnel if parent dies
|
||||||
let parentPid = ProcessInfo.processInfo.processIdentifier
|
let parentPid = ProcessInfo.processInfo.processIdentifier
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,9 @@ class ServerManager {
|
||||||
|
|
||||||
logger.info("Started server on port \(self.port)")
|
logger.info("Started server on port \(self.port)")
|
||||||
|
|
||||||
|
// Pass the local auth token to SessionMonitor
|
||||||
|
SessionMonitor.shared.setLocalAuthToken(server.localToken)
|
||||||
|
|
||||||
// Trigger cleanup of old sessions after server starts
|
// Trigger cleanup of old sessions after server starts
|
||||||
await triggerInitialCleanup()
|
await triggerInitialCleanup()
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -254,6 +257,9 @@ class ServerManager {
|
||||||
bunServer = nil
|
bunServer = nil
|
||||||
isRunning = false
|
isRunning = false
|
||||||
|
|
||||||
|
// Clear the auth token from SessionMonitor
|
||||||
|
SessionMonitor.shared.setLocalAuthToken(nil)
|
||||||
|
|
||||||
// Reset crash tracking when manually stopped
|
// Reset crash tracking when manually stopped
|
||||||
consecutiveCrashes = 0
|
consecutiveCrashes = 0
|
||||||
lastCrashTime = nil
|
lastCrashTime = nil
|
||||||
|
|
@ -317,6 +323,11 @@ class ServerManager {
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.timeoutInterval = 10
|
request.timeoutInterval = 10
|
||||||
|
|
||||||
|
// Add local auth token if available
|
||||||
|
if let server = bunServer {
|
||||||
|
request.setValue(server.localToken, forHTTPHeaderField: "X-VibeTunnel-Local")
|
||||||
|
}
|
||||||
|
|
||||||
// Make the cleanup request
|
// Make the cleanup request
|
||||||
let (data, response) = try await URLSession.shared.data(for: request)
|
let (data, response) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,18 @@ final class SessionMonitor {
|
||||||
private var lastFetch: Date?
|
private var lastFetch: Date?
|
||||||
private let cacheInterval: TimeInterval = 2.0
|
private let cacheInterval: TimeInterval = 2.0
|
||||||
private let serverPort: Int
|
private let serverPort: Int
|
||||||
|
private var localAuthToken: String?
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
let port = UserDefaults.standard.integer(forKey: "serverPort")
|
let port = UserDefaults.standard.integer(forKey: "serverPort")
|
||||||
self.serverPort = port > 0 ? port : 4_020
|
self.serverPort = port > 0 ? port : 4_020
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the local auth token for server requests
|
||||||
|
func setLocalAuthToken(_ token: String?) {
|
||||||
|
self.localAuthToken = token
|
||||||
|
}
|
||||||
|
|
||||||
/// Number of running sessions
|
/// Number of running sessions
|
||||||
var sessionCount: Int {
|
var sessionCount: Int {
|
||||||
sessions.values.count { $0.isRunning }
|
sessions.values.count { $0.isRunning }
|
||||||
|
|
@ -69,7 +75,13 @@ final class SessionMonitor {
|
||||||
throw URLError(.badURL)
|
throw URLError(.badURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
let request = URLRequest(url: url, timeoutInterval: 3.0)
|
var request = URLRequest(url: url, timeoutInterval: 3.0)
|
||||||
|
|
||||||
|
// Add local auth token if available
|
||||||
|
if let token = localAuthToken {
|
||||||
|
request.setValue(token, forHTTPHeaderField: "X-VibeTunnel-Local")
|
||||||
|
}
|
||||||
|
|
||||||
let (data, response) = try await URLSession.shared.data(for: request)
|
let (data, response) = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse,
|
guard let httpResponse = response as? HTTPURLResponse,
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,43 @@ struct DashboardSettingsView: View {
|
||||||
serverManager: serverManager
|
serverManager: serverManager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Dashboard URL display
|
||||||
|
VStack(spacing: 4) {
|
||||||
|
if accessMode == .localhost {
|
||||||
|
HStack(spacing: 5) {
|
||||||
|
Text("Dashboard available at")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
|
||||||
|
if let url = URL(string: "http://127.0.0.1:\(serverPort)") {
|
||||||
|
Link(url.absoluteString, destination: url)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if accessMode == .network {
|
||||||
|
if let ip = localIPAddress {
|
||||||
|
HStack(spacing: 5) {
|
||||||
|
Text("Dashboard available at")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
|
||||||
|
if let url = URL(string: "http://\(ip):\(serverPort)") {
|
||||||
|
Link(url.absoluteString, destination: url)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("Fetching local IP address...")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
|
||||||
NgrokIntegrationSection(
|
NgrokIntegrationSection(
|
||||||
ngrokEnabled: $ngrokEnabled,
|
ngrokEnabled: $ngrokEnabled,
|
||||||
ngrokAuthToken: $ngrokAuthToken,
|
ngrokAuthToken: $ngrokAuthToken,
|
||||||
|
|
@ -427,26 +464,6 @@ private struct AccessModeView: View {
|
||||||
restartServerWithNewBindAddress()
|
restartServerWithNewBindAddress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if accessMode == .network {
|
|
||||||
if let ip = localIPAddress {
|
|
||||||
HStack {
|
|
||||||
Text("Dashboard available at")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
|
|
||||||
if let url = URL(string: "http://\(ip):\(serverPort)") {
|
|
||||||
Link(url.absoluteString, destination: url)
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(.blue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text("Fetching local IP address...")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue