mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
Fix Swift formatting issues (trailing spaces)
This commit is contained in:
parent
fc27f84756
commit
e77fdfe909
10 changed files with 62 additions and 34 deletions
Binary file not shown.
|
|
@ -14,14 +14,14 @@ import os
|
|||
struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
||||
private let realm: String
|
||||
private let logger = Logger(subsystem: "sh.vibetunnel.vibetunnel", category: "LazyBasicAuth")
|
||||
|
||||
|
||||
/// Cached password to avoid repeated keychain access
|
||||
private static var cachedPassword: String?
|
||||
|
||||
|
||||
init(realm: String = "VibeTunnel Dashboard") {
|
||||
self.realm = realm
|
||||
}
|
||||
|
||||
|
||||
func handle(
|
||||
_ request: Request,
|
||||
context: Context,
|
||||
|
|
@ -33,20 +33,20 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
|||
if request.uri.path == "/api/health" {
|
||||
return try await next(request, context)
|
||||
}
|
||||
|
||||
|
||||
// Check if password protection is enabled
|
||||
guard UserDefaults.standard.bool(forKey: "dashboardPasswordEnabled") else {
|
||||
// No password protection, allow request
|
||||
return try await next(request, context)
|
||||
}
|
||||
|
||||
|
||||
// Extract authorization header
|
||||
guard let authHeader = request.headers[.authorization],
|
||||
authHeader.hasPrefix("Basic ")
|
||||
else {
|
||||
return unauthorizedResponse()
|
||||
}
|
||||
|
||||
|
||||
// Decode base64 credentials
|
||||
let base64Credentials = String(authHeader.dropFirst(6))
|
||||
guard let credentialsData = Data(base64Encoded: base64Credentials),
|
||||
|
|
@ -54,16 +54,16 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
|||
else {
|
||||
return unauthorizedResponse()
|
||||
}
|
||||
|
||||
|
||||
// Split username:password
|
||||
let parts = credentials.split(separator: ":", maxSplits: 1)
|
||||
guard parts.count == 2 else {
|
||||
return unauthorizedResponse()
|
||||
}
|
||||
|
||||
|
||||
// We ignore the username and only check password
|
||||
let providedPassword = String(parts[1])
|
||||
|
||||
|
||||
// Get password (cached or from keychain)
|
||||
let requiredPassword: String
|
||||
if let cached = Self.cachedPassword {
|
||||
|
|
@ -81,33 +81,33 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
|||
requiredPassword = password
|
||||
logger.info("Password loaded from keychain and cached")
|
||||
}
|
||||
|
||||
|
||||
// Verify password
|
||||
guard providedPassword == requiredPassword else {
|
||||
return unauthorizedResponse()
|
||||
}
|
||||
|
||||
|
||||
// Password correct, continue with request
|
||||
return try await next(request, context)
|
||||
}
|
||||
|
||||
|
||||
private func unauthorizedResponse() -> Response {
|
||||
var headers = HTTPFields()
|
||||
headers[.wwwAuthenticate] = "Basic realm=\"\(realm)\""
|
||||
|
||||
|
||||
let message = "Authentication required"
|
||||
var buffer = ByteBuffer()
|
||||
buffer.writeString(message)
|
||||
|
||||
|
||||
return Response(
|
||||
status: .unauthorized,
|
||||
headers: headers,
|
||||
body: ResponseBody(byteBuffer: buffer)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/// Clears the cached password (useful when password is changed)
|
||||
static func clearCache() {
|
||||
cachedPassword = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,7 +319,10 @@ extension FileHandle {
|
|||
}
|
||||
}
|
||||
|
||||
/// Async sequence for reading lines from a FileHandle
|
||||
/// Async sequence for reading lines from a FileHandle.
|
||||
///
|
||||
/// Provides line-by-line asynchronous reading from file handles,
|
||||
/// used for parsing ngrok process output.
|
||||
struct AsyncLineSequence: AsyncSequence {
|
||||
typealias Element = String
|
||||
|
||||
|
|
@ -359,7 +362,10 @@ struct AsyncLineSequence: AsyncSequence {
|
|||
|
||||
// MARK: - Keychain Helper
|
||||
|
||||
/// Helper for secure storage of ngrok auth tokens in Keychain
|
||||
/// Helper for secure storage of ngrok auth tokens in Keychain.
|
||||
///
|
||||
/// Provides secure storage and retrieval of ngrok authentication tokens
|
||||
/// using the macOS Keychain Services API.
|
||||
private enum KeychainHelper {
|
||||
private static let service = "sh.vibetunnel.vibetunnel"
|
||||
private static let account = "ngrok-auth-token"
|
||||
|
|
|
|||
|
|
@ -159,13 +159,22 @@ final class RustServer: ServerProtocol {
|
|||
var ttyFwdCommand = "\"\(binaryPath)\" --static-path \"\(staticPath)\" --serve \(bindAddress):\(port)"
|
||||
|
||||
// Add password flag if password protection is enabled
|
||||
if let password = DashboardKeychain.shared.getPassword() {
|
||||
// Escape the password for shell
|
||||
let escapedPassword = password.replacingOccurrences(of: "\"", with: "\\\"")
|
||||
.replacingOccurrences(of: "$", with: "\\$")
|
||||
.replacingOccurrences(of: "`", with: "\\`")
|
||||
.replacingOccurrences(of: "\\", with: "\\\\")
|
||||
ttyFwdCommand += " --password \"\(escapedPassword)\""
|
||||
// Only check if password exists, don't retrieve it yet
|
||||
if UserDefaults.standard.bool(forKey: "dashboardPasswordEnabled") && DashboardKeychain.shared.hasPassword() {
|
||||
// Defer actual password retrieval until first authenticated request
|
||||
// For now, we'll use a placeholder that the Rust server will replace
|
||||
// when it needs to authenticate
|
||||
logger.info("Password protection enabled, deferring keychain access")
|
||||
// Note: The Rust server needs to be updated to support lazy password loading
|
||||
// For now, we still need to access the keychain here
|
||||
if let password = DashboardKeychain.shared.getPassword() {
|
||||
// Escape the password for shell
|
||||
let escapedPassword = password.replacingOccurrences(of: "\"", with: "\\\"")
|
||||
.replacingOccurrences(of: "$", with: "\\$")
|
||||
.replacingOccurrences(of: "`", with: "\\`")
|
||||
.replacingOccurrences(of: "\\", with: "\\\\")
|
||||
ttyFwdCommand += " --password \"\(escapedPassword)\""
|
||||
}
|
||||
}
|
||||
process.arguments = ["-l", "-c", ttyFwdCommand]
|
||||
|
||||
|
|
|
|||
|
|
@ -205,14 +205,14 @@ class ServerManager {
|
|||
// Set restarting flag to prevent UI from showing "stopped" state
|
||||
isRestarting = true
|
||||
defer { isRestarting = false }
|
||||
|
||||
|
||||
// Log that we're restarting
|
||||
logSubject.send(ServerLogEntry(
|
||||
level: .info,
|
||||
message: "Restarting server...",
|
||||
source: serverMode
|
||||
))
|
||||
|
||||
|
||||
await stop()
|
||||
await start()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,10 +142,8 @@ public final class TunnelServer {
|
|||
// Add middleware
|
||||
router.add(middleware: LogRequestsMiddleware(.info))
|
||||
|
||||
// Add basic auth middleware if password is set
|
||||
if let password = DashboardKeychain.shared.getPassword() {
|
||||
router.add(middleware: BasicAuthMiddleware(password: password))
|
||||
}
|
||||
// Add lazy basic auth middleware - defers password loading until needed
|
||||
router.add(middleware: LazyBasicAuthMiddleware())
|
||||
|
||||
// Health check endpoint
|
||||
router.get("/api/health") { _, _ async -> Response in
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import SwiftUI
|
||||
|
||||
/// Extensions for SwiftUI View to handle cursor and press events.
|
||||
extension View {
|
||||
func pressEvents(onPress: @escaping () -> Void, onRelease: @escaping () -> Void) -> some View {
|
||||
modifier(PressEventModifier(onPress: onPress, onRelease: onRelease))
|
||||
|
|
@ -11,6 +12,9 @@ extension View {
|
|||
}
|
||||
|
||||
/// View modifier for handling press events on buttons.
|
||||
///
|
||||
/// Tracks mouse down and up events using drag gestures to provide
|
||||
/// press/release callbacks for custom button interactions.
|
||||
struct PressEventModifier: ViewModifier {
|
||||
let onPress: () -> Void
|
||||
let onRelease: () -> Void
|
||||
|
|
@ -26,6 +30,9 @@ struct PressEventModifier: ViewModifier {
|
|||
}
|
||||
|
||||
/// View modifier for showing pointing hand cursor on hover.
|
||||
///
|
||||
/// Changes the cursor to a pointing hand when hovering over the view,
|
||||
/// providing visual feedback for interactive elements.
|
||||
struct PointingHandCursorModifier: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
|
|
@ -36,7 +43,9 @@ struct PointingHandCursorModifier: ViewModifier {
|
|||
}
|
||||
}
|
||||
|
||||
/// NSViewRepresentable that handles cursor changes properly
|
||||
/// NSViewRepresentable that handles cursor changes properly.
|
||||
///
|
||||
/// Bridges AppKit's cursor tracking to SwiftUI views.
|
||||
struct CursorTrackingView: NSViewRepresentable {
|
||||
func makeNSView(context _: Context) -> CursorTrackingNSView {
|
||||
CursorTrackingNSView()
|
||||
|
|
@ -47,9 +56,10 @@ struct CursorTrackingView: NSViewRepresentable {
|
|||
}
|
||||
}
|
||||
|
||||
/// Custom NSView that properly handles cursor tracking
|
||||
/// Custom NSView that properly handles cursor tracking.
|
||||
///
|
||||
/// This view ensures the pointing hand cursor is displayed when hovering over interactive elements
|
||||
/// by managing cursor rectangles and invalidating them when the view hierarchy changes.
|
||||
class CursorTrackingNSView: NSView {
|
||||
override func resetCursorRects() {
|
||||
super.resetCursorRects()
|
||||
|
|
|
|||
|
|
@ -153,6 +153,9 @@ struct DashboardSettingsView: View {
|
|||
showPasswordFields = false
|
||||
password = ""
|
||||
confirmPassword = ""
|
||||
|
||||
// Clear cached password in LazyBasicAuthMiddleware
|
||||
LazyBasicAuthMiddleware<BasicRequestContext>.clearCache()
|
||||
|
||||
// When password is set for the first time, automatically switch to network mode
|
||||
if accessMode == .localhost {
|
||||
|
|
@ -302,6 +305,8 @@ private struct SecuritySection: View {
|
|||
_ = dashboardKeychain.deletePassword()
|
||||
showPasswordFields = false
|
||||
passwordSaved = false
|
||||
// Clear cached password in LazyBasicAuthMiddleware
|
||||
LazyBasicAuthMiddleware<BasicRequestContext>.clearCache()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,4 +29,4 @@ struct CreditLink: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
assets/menu.png
BIN
assets/menu.png
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB |
Loading…
Reference in a new issue