This commit is contained in:
Peter Steinberger 2025-06-17 01:41:49 +02:00
parent 5d6d630c61
commit 411b79832d
10 changed files with 110 additions and 12 deletions

View file

@ -4,7 +4,12 @@ import Hummingbird
import HummingbirdCore
import NIOCore
/// Middleware that implements HTTP Basic Authentication
/// Middleware that implements HTTP Basic Authentication.
///
/// Provides password-based access control for the VibeTunnel dashboard.
/// Validates incoming requests against a configured password using
/// standard HTTP Basic Authentication. Exempts health check endpoints
/// from authentication requirements.
struct BasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
let password: String
let realm: String

View file

@ -1,11 +1,20 @@
import Foundation
import Logging
/// Generates Asciinema cast v2 format files from terminal session output
/// Generates Asciinema cast v2 format files from terminal session output.
///
/// Creates recordings of terminal sessions in the Asciinema cast format,
/// which can be played back using Asciinema players. Handles timing information,
/// terminal dimensions, and output/input event recording.
///
/// Format specification: https://docs.asciinema.org/manual/asciicast/v2/
struct CastFileGenerator {
private let logger = Logger(label: "VibeTunnel.CastFileGenerator")
/// Header structure for Asciinema cast v2 format.
///
/// Contains metadata about the terminal recording including
/// dimensions, timing, and environment information.
struct CastHeader: Codable {
let version: Int = 2
let width: Int
@ -30,6 +39,9 @@ struct CastFileGenerator {
}
}
/// Represents a single event in the Asciinema recording.
///
/// Each event captures either terminal output or input at a specific timestamp.
struct CastEvent {
let time: TimeInterval
let eventType: String

View file

@ -2,7 +2,11 @@ import Foundation
import os
import Security
/// Service for managing dashboard password in keychain
/// Service for managing dashboard password in keychain.
///
/// Provides secure storage and retrieval of the dashboard authentication
/// password using the macOS Keychain. Handles password generation,
/// updates, and deletion with proper error handling and logging.
@MainActor
final class DashboardKeychain {
static let shared = DashboardKeychain()

View file

@ -1,12 +1,19 @@
import Foundation
import HTTPTypes
/// Protocol for HTTP client abstraction to enable testing
/// Protocol for HTTP client abstraction to enable testing.
///
/// Defines the interface for making HTTP requests, allowing for
/// easy mocking and testing of network-dependent code.
public protocol HTTPClientProtocol {
func data(for request: HTTPRequest, body: Data?) async throws -> (Data, HTTPResponse)
}
/// Real HTTP client implementation
/// Real HTTP client implementation.
///
/// Concrete implementation of HTTPClientProtocol using URLSession
/// for actual network requests. Converts between HTTPTypes and
/// Foundation's URLRequest/URLResponse types.
public final class HTTPClient: HTTPClientProtocol {
private let session: URLSession
@ -29,6 +36,7 @@ public final class HTTPClient: HTTPClientProtocol {
}
}
/// Errors that can occur during HTTP client operations.
enum HTTPClientError: Error {
case invalidResponse
}

View file

@ -48,6 +48,7 @@ class ServerManager {
private(set) var currentServer: ServerProtocol?
private(set) var isRunning = false
private(set) var isSwitching = false
private(set) var isRestarting = false
private(set) var lastError: Error?
private let logger = Logger(subsystem: "com.steipete.VibeTunnel", category: "ServerManager")
@ -193,11 +194,25 @@ class ServerManager {
))
// Update ServerMonitor for compatibility
ServerMonitor.shared.isServerRunning = false
// Only set to false if we're not in the middle of a restart
if !isRestarting {
ServerMonitor.shared.isServerRunning = false
}
}
/// Restart the current server
func restart() async {
// 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()
}

View file

@ -49,7 +49,9 @@ public final class ServerMonitor {
/// Syncs state with ServerManager
private func syncWithServerManager() async {
isServerRunning = ServerManager.shared.isRunning
// Consider the server as running if it's actually running OR if it's restarting
// This prevents the UI from showing "stopped" during restart
isServerRunning = ServerManager.shared.isRunning || ServerManager.shared.isRestarting
}
/// Starts the server if not already running
@ -68,7 +70,9 @@ public final class ServerMonitor {
/// Restarts the server
public func restartServer() async throws {
// During restart, we maintain the running state to prevent UI flicker
await ServerManager.shared.restart()
// Sync after restart completes
await syncWithServerManager()
}

View file

@ -4,7 +4,11 @@ import os.log
import Sparkle
import UserNotifications
/// SparkleUpdaterManager with automatic update downloads enabled
/// SparkleUpdaterManager with automatic update downloads enabled.
///
/// Manages application updates using the Sparkle framework. Handles automatic
/// update checking, downloading, and installation while respecting user preferences
/// and update channels. Integrates with macOS notifications for update announcements.
@available(macOS 10.15, *)
@MainActor
public final class SparkleUpdaterManager: NSObject, SPUUpdaterDelegate {

View file

@ -1,7 +1,11 @@
import Foundation
import os.log
/// Manages interactions with the tty-fwd command-line tool
/// Manages interactions with the tty-fwd command-line tool.
///
/// Provides a high-level interface for executing the bundled tty-fwd
/// binary, handling process management, error conditions, and ensuring
/// proper executable permissions. Used for terminal multiplexing operations.
@MainActor
final class TTYForwardManager {
static let shared = TTYForwardManager()
@ -101,7 +105,10 @@ final class TTYForwardManager {
}
}
/// Errors that can occur when working with the tty-fwd binary
/// Errors that can occur when working with the tty-fwd binary.
///
/// Represents failures specific to tty-fwd execution including
/// missing executable, permission issues, and runtime failures.
enum TTYForwardError: LocalizedError {
case executableNotFound
case notExecutable

View file

@ -2,14 +2,21 @@ import Combine
import Foundation
import Logging
/// Holds pipes for a terminal session
/// Holds pipes for a terminal session.
///
/// Encapsulates the standard I/O pipes used for communicating
/// with a terminal process.
private struct SessionPipes {
let stdin: Pipe
let stdout: Pipe
let stderr: Pipe
}
/// Manages terminal sessions and command execution
/// Manages terminal sessions and command execution.
///
/// An actor that handles the lifecycle of terminal sessions, including
/// process creation, I/O handling, and command execution. Provides
/// thread-safe management of multiple concurrent terminal sessions.
actor TerminalManager {
private var sessions: [UUID: TunnelSession] = [:]
private var processes: [UUID: Process] = [:]

View file

@ -0,0 +1,32 @@
import SwiftUI
// MARK: - Credit Link Component
/// Credit link component for individual contributors.
///
/// This component displays a contributor's handle as a clickable link
/// that opens their website when clicked.
struct CreditLink: View {
let name: String
let url: String
@State private var isHovering = false
var body: some View {
Button(action: {
if let linkURL = URL(string: url) {
NSWorkspace.shared.open(linkURL)
}
}, label: {
Text(name)
.font(.caption)
.underline(isHovering, color: .accentColor)
})
.buttonStyle(.link)
.pointingHandCursor()
.onHover { hovering in
withAnimation(.easeInOut(duration: 0.2)) {
isHovering = hovering
}
}
}
}