diff --git a/Model.js b/Model.js index c18a764..e703951 100644 --- a/Model.js +++ b/Model.js @@ -52,6 +52,21 @@ function accountLabel(account) { return String(account.id || "Unknown account") } +function peerFromStatus(id, peer) { + return { + id: id, + HostName: displayHostName(peer.HostName, peer.DNSName), + DNSName: cleanDnsName(peer.DNSName), + TailscaleIPs: filterIPv4(peer.TailscaleIPs || []), + TailscaleIPv6: filterIPv6(peer.TailscaleIPs || []), + Online: peer.Online === true, + OS: String(peer.OS || ""), + Tags: peer.Tags || [], + ExitNodeOption: peer.ExitNodeOption === true, + ExitNode: peer.ExitNode === true + } +} + function parseStatus(raw) { var text = String(raw || "").trim() if (text === "") return { ok: true, unavailable: true, message: "Disconnected" } @@ -62,28 +77,24 @@ function parseStatus(raw) { var self = data.Self || {} var selfIps = filterIPv4(self.TailscaleIPs || data.TailscaleIPs || []) var peers = [] + var exitNodes = [] var rawPeers = data.Peer || {} for (var id in rawPeers) { var peer = rawPeers[id] || {} - if (peer.Online !== true) continue - peers.push({ - id: id, - HostName: displayHostName(peer.HostName, peer.DNSName), - DNSName: cleanDnsName(peer.DNSName), - TailscaleIPs: filterIPv4(peer.TailscaleIPs || []), - TailscaleIPv6: filterIPv6(peer.TailscaleIPs || []), - Online: true, - OS: String(peer.OS || ""), - Tags: peer.Tags || [], - ExitNodeOption: peer.ExitNodeOption === true, - ExitNode: peer.ExitNode === true - }) + var normalized = peerFromStatus(id, peer) + if (normalized.Online) { + peers.push(normalized) + if (normalized.ExitNodeOption) exitNodes.push(normalized) + } } peers.sort(function(a, b) { return String(a.HostName).localeCompare(String(b.HostName)) }) + exitNodes.sort(function(a, b) { + return String(a.HostName).localeCompare(String(b.HostName)) + }) return { ok: true, @@ -95,7 +106,8 @@ function parseStatus(raw) { selfName: displayHostName(self.HostName, self.DNSName), selfDnsName: cleanDnsName(self.DNSName), selfIp: selfIps.length > 0 ? selfIps[0] : "", - peers: peers + peers: peers, + exitNodes: exitNodes } } catch (e) { return { ok: false, unavailable: true, message: "Status error", error: "Failed to parse tailscale status" } @@ -143,6 +155,7 @@ if (typeof module !== "undefined") { displayHostName: displayHostName, osIcon: osIcon, accountLabel: accountLabel, + peerFromStatus: peerFromStatus, parseStatus: parseStatus, parseAccounts: parseAccounts } diff --git a/Panel.qml b/Panel.qml index 5711de5..a81a22d 100644 --- a/Panel.qml +++ b/Panel.qml @@ -39,14 +39,7 @@ Panel { readonly property string fontFamily: bar ? bar.fontFamily : Style.font.family readonly property bool showConnections: tailscale.accounts.length > 1 || tailscale.accountsAccessDenied readonly property bool showPeers: tailscale.running && tailscale.peers.length > 0 - readonly property var exitNodes: { - var nodes = [] - for (var i = 0; i < tailscale.peers.length; i++) { - var peer = tailscale.peers[i] - if (peer && peer.ExitNodeOption === true) nodes.push(peer) - } - return nodes - } + readonly property var exitNodes: tailscale.exitNodes readonly property bool showExitNodes: tailscale.running && exitNodes.length > 0 readonly property color iconColor: tailscale.running ? foreground : dim readonly property color hoverFill: bar ? Style.hoverFillFor(bar.foreground, Color.accent) : "transparent" @@ -877,7 +870,7 @@ Panel { Text { id: exitNodeGlyph - text: tailscale.osIcon(peer ? peer.OS : "") + text: "󱇢" color: exitNodeRow.activeExitNode || exitNodeRow.settingExitNode ? root.foreground : root.dim font.family: root.fontFamily font.pixelSize: Style.font.body diff --git a/Service.qml b/Service.qml index 25bf36d..846bc42 100644 --- a/Service.qml +++ b/Service.qml @@ -20,6 +20,7 @@ Item { property string selfIp: "" property string authUrl: "" property var peers: [] + property var exitNodes: [] property var accounts: [] property string selectedAccountId: "" property string selectedAccountLabel: "" @@ -158,6 +159,7 @@ Item { selfIp = "" authUrl = "" peers = [] + exitNodes = [] accounts = [] selectedAccountId = "" selectedAccountLabel = "" @@ -188,6 +190,7 @@ Item { selfDnsName = parsed.selfDnsName selfIp = parsed.selfIp peers = parsed.running ? parsed.peers : [] + exitNodes = parsed.running ? parsed.exitNodes : [] if (needsLogin) statusText = "Needs login" else if (running) { @@ -253,8 +256,9 @@ Item { function setExitNode(peer) { if (!installed || !running || !peer || exitNodeProcess.running) return - var target = exitNodeTarget(peer) - if (target === "") return + var active = peer.ExitNode === true + var target = active ? "" : exitNodeTarget(peer) + if (!active && target === "") return _exitNodeOutput = "" _exitNodeError = "" settingExitNodeId = String(peer.id || "")