Skip to content

Commit

Permalink
Merge pull request #3 from softnetics/suphon/integrate-dns-sd
Browse files Browse the repository at this point in the history
Switch from OrbStack to dns-sd
  • Loading branch information
suphon-t authored Jan 15, 2024
2 parents 379eafb + 9b5fd18 commit ef29cc4
Show file tree
Hide file tree
Showing 66 changed files with 3,423 additions and 781 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-cheetahs-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@softnetics/dotlocal": minor
---

Switch from OrbStack to dns-sd
4 changes: 2 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: build
on:
push:
branches:
- main
- '**'
tags:
- "v*.*.*"

Expand All @@ -18,7 +18,7 @@ permissions:
repository-projects: read
security-events: read
statuses: read

jobs:
build:
runs-on: macos-13
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ fastlane/test_output
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/
default.profraw
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"clientcommon.h": "c"
}
}
6 changes: 6 additions & 0 deletions Config/Config.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@
// https://help.apple.com/xcode/#/dev745c5c974

#include "Version.xcconfig"

APP_BUNDLE_IDENTIFIER = dev.suphon.DotLocal
HELPER_TOOL_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).helper

APP_VERSION = $(MARKETING_VERSION)
HELPER_VERSION = $(MARKETING_VERSION)
570 changes: 516 additions & 54 deletions DotLocal.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"pins" : [
{
"identity" : "authorized",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/Authorized.git",
"state" : {
"revision" : "e490b9d3f4a0e8b17a8b39b5a9750b8e0be7548a",
"version" : "1.0.0"
}
},
{
"identity" : "blessed",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/Blessed",
"state" : {
"revision" : "e7c730ea4bcd2df7b61f022dbd38c5cdc2c875de",
"version" : "0.6.0"
}
},
{
"identity" : "defaults",
"kind" : "remoteSourceControl",
Expand All @@ -9,6 +27,15 @@
"revision" : "d8a9f5105607c85b544558e7f5b51d6c360ba88b"
}
},
{
"identity" : "embeddedpropertylist",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/EmbeddedPropertyList.git",
"state" : {
"revision" : "21bd832e28a9a66ecdb7b4c21910bb0487a22fe5",
"version" : "2.0.2"
}
},
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
Expand All @@ -27,6 +54,24 @@
"revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc"
}
},
{
"identity" : "required",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/Required.git",
"state" : {
"revision" : "82a4fbd388346ca40b1bbe815014dc45a75d503c",
"version" : "0.1.1"
}
},
{
"identity" : "securexpc",
"kind" : "remoteSourceControl",
"location" : "https://github.com/trilemma-dev/SecureXPC",
"state" : {
"revision" : "d6e439e2b805de8be9b584fff97cf2f6a839a656",
"version" : "0.8.0"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
Expand Down
18 changes: 18 additions & 0 deletions DotLocal/AppConfig.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// AppConfig.xcconfig
// DotLocal
//
// Created by Suphon Thanakornpakapong on 11/1/2567 BE.
//

// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974

#include "Config/Config.xcconfig"

TARGET_DIRECTORY = DotLocal

PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER)
SWIFT_ACTIVE_COMPILATION_CONDITIONS = APP

INFOPLIST_FILE = $(TARGET_DIRECTORY)/Info.plist
20 changes: 15 additions & 5 deletions DotLocal/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import Foundation
import AppKit
import Defaults
import SecureXPC

class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
DaemonManager.shared.start()
override init() {
_ = HelperManager.shared
ClientManager.shared.checkInstalled()
}

Expand All @@ -27,8 +28,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return true
}

func applicationWillTerminate(_ notification: Notification) {
DaemonManager.shared.stop()
DaemonManager.shared.wait()
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
print("applicationShouldTerminate called, stopping daemon and helper")
if HelperManager.shared.installationStatus.isReady {
Task {
await DaemonManager.shared.stop()
try? await HelperManager.shared.xpcClient.send(to: SharedConstants.exitRoute)
NSApplication.shared.terminate(nil)
}
return .terminateLater
} else {
return .terminateNow
}
}
}
12 changes: 6 additions & 6 deletions DotLocal/AppMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ struct AppMenu: View {
switch daemonManager.state {
case .stopped:
Button("DotLocal is not running") {}.disabled(true)
case .starting:
case .starting, .unknown:
Button("DotLocal is starting") {}.disabled(true)
case .started:
case .started(let mappings):
Section("Routes") {
MappingListMenu()
MappingListMenu(mappings: mappings)
}
}
Divider()
Expand All @@ -39,14 +39,14 @@ struct AppMenu: View {
}

struct MappingListMenu: View {
@StateObject var vm = MappingListViewModel()
var mappings: [Mapping]
@Environment(\.openURL) var openURL

var body: some View {
if vm.mappings.isEmpty {
if mappings.isEmpty {
Button("No Routes", action: {}).disabled(true)
} else {
ForEach(vm.mappings) { mapping in
ForEach(mappings) { mapping in
let url = URL(string: "http://\(mapping.host)\(mapping.pathPrefix)")!
Button(action: { openURL(url) }, label: {
Text(getLabel(mapping: mapping))
Expand Down
18 changes: 13 additions & 5 deletions DotLocal/ClientManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@
//

import Foundation
import SecureXPC

class ClientManager: ObservableObject {
static let shared = ClientManager()

@Published var installed = false
private let clientUrl = Bundle.main.bundleURL.appendingPathComponent("Contents/Resources/bin/dotlocal")
private let target = "/usr/local/bin/dotlocal"

private init() {}

func installCli() async {
_ = await Sudo.run(path: clientUrl.path(percentEncoded: false), arguments: ["install"])
checkInstalled()
do {
try await HelperManager.shared.xpcClient.sendMessage(Bundle.main.bundleURL, to: SharedConstants.installClientRoute)
checkInstalled()
} catch {
print("error installing cli: \(error)")
}
}

func uninstallCli() async {
_ = await Sudo.run(path: clientUrl.path(percentEncoded: false), arguments: ["uninstall"])
checkInstalled()
do {
try await HelperManager.shared.xpcClient.send(to: SharedConstants.uninstallClientRoute)
checkInstalled()
} catch {
print("error uninstalling cli: \(error)")
}
}

func checkInstalled() {
Expand Down
77 changes: 65 additions & 12 deletions DotLocal/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,43 @@
//

import SwiftUI
import ServiceManagement
import Blessed
import Authorized
import SecureXPC

struct ContentView: View {
@StateObject var daemonManager = DaemonManager.shared
@StateObject var helperManager = HelperManager.shared

var body: some View {
let status = helperManager.installationStatus
VStack {
switch daemonManager.state {
case .stopped:
Text("DotLocal is not running")
case .starting:
ProgressView()
case .started:
MappingList()
if status.isReady {
VStack {
switch daemonManager.state {
case .stopped:
Text("DotLocal is not running")
case .starting, .unknown:
ProgressView()
case .started:
MappingList()
}
}.toolbar() {
StartStopButton(state: daemonManager.state, onStart: {
Task {
await daemonManager.start()
}
}, onStop: {
Task {
await daemonManager.stop()
}
})
}
} else {
RequiresHelperView()
}
}
.frame(maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, maxHeight: .infinity)
.toolbar() {
StartStopButton(state: daemonManager.state, onStart: { daemonManager.start() }, onStop: { daemonManager.stop() })
}
}
}

Expand All @@ -39,7 +57,7 @@ struct StartStopButton: View {
Button(action: onStart) {
Label("Start", systemImage: "play.fill")
}
case .starting:
case .starting, .unknown:
ProgressView().controlSize(.small)
case .started:
Button(action: onStop) {
Expand All @@ -49,6 +67,41 @@ struct StartStopButton: View {
}
}

struct RequiresHelperView: View {
@State private var didError = false
@State private var errorMessage = ""

var body: some View {
VStack(spacing: 8) {
Text("Helper Not Installed").font(.title).fontWeight(.bold)
Text("Please install the helper in order to use DotLocal")
Button(action: {
do {
try PrivilegedHelperManager.shared
.authorizeAndBless(message: nil)
} catch AuthorizationError.canceled {
// No user feedback needed, user canceled
} catch {
errorMessage = error.localizedDescription
didError = true
}
}, label: {
Text("Install Helper")
})
}
.foregroundStyle(.secondary)
.alert(
"Install failed",
isPresented: $didError,
presenting: errorMessage
) { _ in
Button("OK") {}
} message: { message in
Text(message)
}
}
}

#Preview {
ContentView()
}
Loading

0 comments on commit ef29cc4

Please sign in to comment.