1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-18 17:15:20 -04:00

feat(tailscale): add connect/disconnect, exit-node and LAN-access controls (#2644)

* feat(tailscale): add connect/disconnect/exit-node/LAN-access backend

The Tailscale backend previously exposed only read-only status
(tailscale.getStatus, tailscale.refresh). This adds write actions through the
existing tailscale.com/client/local integration:

- tailscale.connect / tailscale.disconnect (EditPrefs WantRunning)
- tailscale.setExitNode (EditPrefs ExitNodeID; empty id clears it and any
  legacy ExitNodeIP, mirroring `tailscale set --exit-node`)
- tailscale.setAllowLanAccess (EditPrefs ExitNodeAllowLANAccess)

The manager's client interface gains GetPrefs/EditPrefs; fetchState merges
ExitNodeAllowLANAccess from prefs, and Peer exposes ExitNodeOption so the UI
can list exit-node-capable peers.

* feat(tailscale): expose the new actions in TailscaleService

Adds connectTailscale/disconnectTailscale, setExitNode/clearExitNode and
setAllowLanAccess wrappers, plus derived exitNodeOptions/currentExitNode and the
exitNodeAllowLanAccess state. Write-action errors surface via ToastService.

* feat(tailscale): add connection, exit-node and LAN-access controls to the widget

The control-center widget toggle was a no-op. It now connects/disconnects, and
the detail panel gains a connection status row with a connect/disconnect button,
an exit-node picker and a LAN-access toggle.
This commit is contained in:
Rocho
2026-06-16 15:08:22 +02:00
committed by GitHub
parent 2fd9de5062
commit 988b54515e
8 changed files with 499 additions and 39 deletions
+54
View File
@@ -41,6 +41,7 @@ Singleton {
property string tailnetName: ""
property var selfNode: null
property var peers: []
property bool exitNodeAllowLanAccess: false
property bool available: false
property bool stateInitialized: false
@@ -56,6 +57,19 @@ Singleton {
readonly property var onlinePeers: allPeersList.filter(p => p.online)
// Peers that may be used as an exit node (offered && approved). Self is
// excluded: a node can never route through itself, and tailscaled rejects it.
readonly property var exitNodeOptions: allPeersList.filter(p => p && p.exitNodeOption && p !== selfNode)
// The currently selected exit node, or null if none is in use.
readonly property var currentExitNode: {
for (const p of allPeersList) {
if (p && p.exitNode)
return p;
}
return null;
}
readonly property var myPeers: {
if (!selfNode)
return allPeersList;
@@ -141,6 +155,7 @@ Singleton {
tailnetName = data.tailnetName || "";
selfNode = data.self || null;
peers = data.peers || [];
exitNodeAllowLanAccess = data.exitNodeAllowLanAccess || false;
}
function refresh(callback) {
@@ -152,6 +167,45 @@ Singleton {
});
}
// sendAction issues a state-changing request. The backend refreshes and
// broadcasts on success, so subscribers update without an extra getStatus.
function sendAction(method, params, callback) {
if (!available)
return;
DMSService.sendRequest(method, params, response => {
if (response.error) {
root.log.warn(method + " failed: " + response.error);
ToastService.showError(I18n.tr("Tailscale action failed", "Toast shown when a Tailscale write action is rejected"), response.error);
}
if (callback)
callback(response);
});
}
function connectTailscale(callback) {
sendAction("tailscale.connect", null, callback);
}
function disconnectTailscale(callback) {
sendAction("tailscale.disconnect", null, callback);
}
function setExitNode(id, callback) {
sendAction("tailscale.setExitNode", {
"id": id || ""
}, callback);
}
function clearExitNode(callback) {
setExitNode("", callback);
}
function setAllowLanAccess(enabled, callback) {
sendAction("tailscale.setAllowLanAccess", {
"enabled": enabled
}, callback);
}
function isMine(peer) {
const myOwner = selfNode ? (selfNode.owner || "") : "";
if (peer.owner === myOwner && myOwner !== "")