macOS: add remote gateway token field
This commit is contained in:
parent
6372242da7
commit
a7be243b7a
@ -213,6 +213,10 @@ final class AppState {
|
|||||||
didSet { self.syncGatewayConfigIfNeeded() }
|
didSet { self.syncGatewayConfigIfNeeded() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var remoteToken: String {
|
||||||
|
didSet { self.syncGatewayConfigIfNeeded() }
|
||||||
|
}
|
||||||
|
|
||||||
var remoteIdentity: String {
|
var remoteIdentity: String {
|
||||||
didSet { self.ifNotPreview { UserDefaults.standard.set(self.remoteIdentity, forKey: remoteIdentityKey) } }
|
didSet { self.ifNotPreview { UserDefaults.standard.set(self.remoteIdentity, forKey: remoteIdentityKey) } }
|
||||||
}
|
}
|
||||||
@ -278,6 +282,7 @@ final class AppState {
|
|||||||
let configRoot = MoltbotConfigFile.loadDict()
|
let configRoot = MoltbotConfigFile.loadDict()
|
||||||
let configRemoteUrl = GatewayRemoteConfig.resolveUrlString(root: configRoot)
|
let configRemoteUrl = GatewayRemoteConfig.resolveUrlString(root: configRoot)
|
||||||
let configRemoteTransport = GatewayRemoteConfig.resolveTransport(root: configRoot)
|
let configRemoteTransport = GatewayRemoteConfig.resolveTransport(root: configRoot)
|
||||||
|
let configRemoteToken = Self.resolveRemoteToken(root: configRoot)
|
||||||
let resolvedConnectionMode = ConnectionModeResolver.resolve(root: configRoot).mode
|
let resolvedConnectionMode = ConnectionModeResolver.resolve(root: configRoot).mode
|
||||||
self.remoteTransport = configRemoteTransport
|
self.remoteTransport = configRemoteTransport
|
||||||
self.connectionMode = resolvedConnectionMode
|
self.connectionMode = resolvedConnectionMode
|
||||||
@ -293,6 +298,7 @@ final class AppState {
|
|||||||
self.remoteTarget = storedRemoteTarget
|
self.remoteTarget = storedRemoteTarget
|
||||||
}
|
}
|
||||||
self.remoteUrl = configRemoteUrl ?? ""
|
self.remoteUrl = configRemoteUrl ?? ""
|
||||||
|
self.remoteToken = configRemoteToken
|
||||||
self.remoteIdentity = UserDefaults.standard.string(forKey: remoteIdentityKey) ?? ""
|
self.remoteIdentity = UserDefaults.standard.string(forKey: remoteIdentityKey) ?? ""
|
||||||
self.remoteProjectRoot = UserDefaults.standard.string(forKey: remoteProjectRootKey) ?? ""
|
self.remoteProjectRoot = UserDefaults.standard.string(forKey: remoteProjectRootKey) ?? ""
|
||||||
self.remoteCliPath = UserDefaults.standard.string(forKey: remoteCliPathKey) ?? ""
|
self.remoteCliPath = UserDefaults.standard.string(forKey: remoteCliPathKey) ?? ""
|
||||||
@ -352,6 +358,16 @@ final class AppState {
|
|||||||
return trimmed
|
return trimmed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func resolveRemoteToken(root: [String: Any]) -> String {
|
||||||
|
guard let gateway = root["gateway"] as? [String: Any],
|
||||||
|
let remote = gateway["remote"] as? [String: Any],
|
||||||
|
let token = remote["token"] as? String
|
||||||
|
else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return token.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
}
|
||||||
|
|
||||||
private func startConfigWatcher() {
|
private func startConfigWatcher() {
|
||||||
let configUrl = MoltbotConfigFile.url()
|
let configUrl = MoltbotConfigFile.url()
|
||||||
self.configWatcher = ConfigFileWatcher(url: configUrl) { [weak self] in
|
self.configWatcher = ConfigFileWatcher(url: configUrl) { [weak self] in
|
||||||
@ -375,6 +391,7 @@ final class AppState {
|
|||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
.isEmpty ?? true)
|
.isEmpty ?? true)
|
||||||
let remoteTransport = GatewayRemoteConfig.resolveTransport(root: root)
|
let remoteTransport = GatewayRemoteConfig.resolveTransport(root: root)
|
||||||
|
let remoteToken = Self.resolveRemoteToken(root: root)
|
||||||
|
|
||||||
let desiredMode: ConnectionMode? = switch modeRaw {
|
let desiredMode: ConnectionMode? = switch modeRaw {
|
||||||
case "local":
|
case "local":
|
||||||
@ -402,6 +419,9 @@ final class AppState {
|
|||||||
if remoteUrlText != self.remoteUrl {
|
if remoteUrlText != self.remoteUrl {
|
||||||
self.remoteUrl = remoteUrlText
|
self.remoteUrl = remoteUrlText
|
||||||
}
|
}
|
||||||
|
if remoteToken != self.remoteToken {
|
||||||
|
self.remoteToken = remoteToken
|
||||||
|
}
|
||||||
|
|
||||||
let targetMode = desiredMode ?? self.connectionMode
|
let targetMode = desiredMode ?? self.connectionMode
|
||||||
if targetMode == .remote,
|
if targetMode == .remote,
|
||||||
@ -437,6 +457,7 @@ final class AppState {
|
|||||||
let remoteIdentity = self.remoteIdentity
|
let remoteIdentity = self.remoteIdentity
|
||||||
let remoteTransport = self.remoteTransport
|
let remoteTransport = self.remoteTransport
|
||||||
let remoteUrl = self.remoteUrl
|
let remoteUrl = self.remoteUrl
|
||||||
|
let remoteToken = self.remoteToken
|
||||||
let desiredMode: String? = switch connectionMode {
|
let desiredMode: String? = switch connectionMode {
|
||||||
case .local:
|
case .local:
|
||||||
"local"
|
"local"
|
||||||
@ -529,6 +550,17 @@ final class AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let trimmedToken = remoteToken.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
if !trimmedToken.isEmpty {
|
||||||
|
if (remote["token"] as? String) != trimmedToken {
|
||||||
|
remote["token"] = trimmedToken
|
||||||
|
remoteChanged = true
|
||||||
|
}
|
||||||
|
} else if remote["token"] != nil {
|
||||||
|
remote.removeValue(forKey: "token")
|
||||||
|
remoteChanged = true
|
||||||
|
}
|
||||||
|
|
||||||
if remoteChanged {
|
if remoteChanged {
|
||||||
gateway["remote"] = remote
|
gateway["remote"] = remote
|
||||||
changed = true
|
changed = true
|
||||||
@ -684,6 +716,7 @@ extension AppState {
|
|||||||
state.canvasEnabled = true
|
state.canvasEnabled = true
|
||||||
state.remoteTarget = "user@example.com"
|
state.remoteTarget = "user@example.com"
|
||||||
state.remoteUrl = "wss://gateway.example.ts.net"
|
state.remoteUrl = "wss://gateway.example.ts.net"
|
||||||
|
state.remoteToken = "example-token"
|
||||||
state.remoteIdentity = "~/.ssh/id_ed25519"
|
state.remoteIdentity = "~/.ssh/id_ed25519"
|
||||||
state.remoteProjectRoot = "~/Projects/moltbot"
|
state.remoteProjectRoot = "~/Projects/moltbot"
|
||||||
state.remoteCliPath = ""
|
state.remoteCliPath = ""
|
||||||
|
|||||||
@ -145,6 +145,8 @@ struct GeneralSettings: View {
|
|||||||
self.remoteDirectRow
|
self.remoteDirectRow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.remoteTokenRow
|
||||||
|
|
||||||
GatewayDiscoveryInlineList(
|
GatewayDiscoveryInlineList(
|
||||||
discovery: self.gatewayDiscovery,
|
discovery: self.gatewayDiscovery,
|
||||||
currentTarget: self.state.remoteTarget,
|
currentTarget: self.state.remoteTarget,
|
||||||
@ -305,6 +307,23 @@ struct GeneralSettings: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var remoteTokenRow: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
|
HStack(alignment: .center, spacing: 10) {
|
||||||
|
Text("Gateway token")
|
||||||
|
.font(.callout.weight(.semibold))
|
||||||
|
.frame(width: self.remoteLabelWidth, alignment: .leading)
|
||||||
|
SecureField("Shared gateway auth token", text: self.$state.remoteToken)
|
||||||
|
.textFieldStyle(.roundedBorder)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
Text("Must match gateway.auth.token (or gateway.remote.token) on the gateway host.")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.padding(.leading, self.remoteLabelWidth + 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var controlStatusLine: String {
|
private var controlStatusLine: String {
|
||||||
switch ControlChannel.shared.state {
|
switch ControlChannel.shared.state {
|
||||||
case .connected: "Connected"
|
case .connected: "Connected"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user