tailscale anyone?

This commit is contained in:
Prabhaav Pillai
2026-09-10 19:52:14 -04:00
parent 08a11c445c
commit 52c61da75d
2 changed files with 79 additions and 1 deletions
@@ -280,6 +280,10 @@ export const api = {
getPlotsLive() { return request("/api/plots/live") },
getGalaxySession() { return request("/api/galaxy/session") },
getTailscaleInstalled() { return request("/api/tailscale/installed", { cache: "no-store" }) },
setupTailscale() { return request("/api/tailscale/setup", { method: "POST" }) },
uninstallTailscale() { return request("/api/tailscale/uninstall", { method: "POST" }) },
getThemeList() { return request("/api/themes/list") },
getThemeDefault() { return request("/api/themes/default") },
loadTheme(path, type) {
@@ -29,6 +29,9 @@ export const SystemTools = {
autoUpdateBusy: false,
profiles: [],
profileBusy: "",
tailscaleInstalled: false,
tailscaleLoaded: false,
tailscaleBusy: false,
}
},
created() {
@@ -38,7 +41,7 @@ export const SystemTools = {
})
this.poll.start()
},
mounted() { this.loadBranches(); this.loadProfiles() },
mounted() { this.loadBranches(); this.loadProfiles(); this.loadTailscale() },
beforeUnmount() { this.poll?.destroy() },
computed: {
updateAvailable() { return this.checkedForUpdates && !!this.fastStatus?.updateAvailable && !this.fastStatus?.running },
@@ -273,6 +276,51 @@ export const SystemTools = {
showSnackbar(e?.message || "Failed to delete driving routes.", "error")
}
},
async loadTailscale() {
try {
const data = await api.getTailscaleInstalled()
this.tailscaleInstalled = !!data?.installed
} catch (e) {
this.tailscaleInstalled = false
} finally {
this.tailscaleLoaded = true
}
},
async installTailscale() {
if (this.tailscaleBusy) return
this.tailscaleBusy = true
showSnackbar("Install started...")
try {
const result = await api.setupTailscale()
showSnackbar(result?.message || "Tailscale setup started.")
if (result?.auth_url) window.open(result.auth_url, "_blank", "noopener")
await this.loadTailscale()
} catch (e) {
showSnackbar(e?.message || "Failed to install Tailscale.", "error")
} finally {
this.tailscaleBusy = false
}
},
async uninstallTailscale() {
if (this.tailscaleBusy) return
if (!(await GalaxyConfirm({
title: "Uninstall Tailscale?",
message: "This disconnects the device from your tailnet and removes the Tailscale binaries and state.",
confirmLabel: "Uninstall",
danger: true,
}))) return
this.tailscaleBusy = true
showSnackbar("Uninstall started...")
try {
const result = await api.uninstallTailscale()
showSnackbar(result?.message || "Tailscale uninstalled.")
await this.loadTailscale()
} catch (e) {
showSnackbar(e?.message || "Failed to uninstall Tailscale.", "error")
} finally {
this.tailscaleBusy = false
}
},
},
template: `
<div>
@@ -412,6 +460,32 @@ export const SystemTools = {
</div>
</GalaxySection>
<GalaxySection title="Tailscale" icon="bi-shield-lock" :collapsible="false">
<div style="padding: var(--sp-3); display:grid; gap:12px;">
<p class="gx-note" style="margin:0;">
Tailscale creates a secure, private connection between your openpilot device and your phone or PC so you can access and control it from anywhere!
</p>
<GxNotice v-if="!tailscaleLoaded" tone="info" icon="bi-arrow-repeat" text="Checking Tailscale install status..." />
<template v-else>
<GxNotice tone="warn" text="Not recommended. Using Galaxy Tunnel is the preferred remote connection method." />
<div style="display:flex; gap:8px; flex-wrap:wrap; align-items:center;">
<button v-if="!tailscaleInstalled" type="button" class="gx-btn" :disabled="tailscaleBusy" @click="installTailscale">
<i class="bi" :class="tailscaleBusy ? 'bi-arrow-repeat gx-spin' : 'bi-download'"></i>
{{ tailscaleBusy ? 'Installing...' : 'Install Tailscale' }}
</button>
<button v-else type="button" class="gx-btn gx-btn--danger" :disabled="tailscaleBusy" @click="uninstallTailscale">
<i class="bi" :class="tailscaleBusy ? 'bi-arrow-repeat gx-spin' : 'bi-trash'"></i>
{{ tailscaleBusy ? 'Uninstalling...' : 'Uninstall Tailscale' }}
</button>
<a class="gx-btn gx-btn--tonal" href="https://tailscale.com/download" target="_blank" rel="noopener">
<i class="bi bi-box-arrow-up-right"></i> Download for your other devices
</a>
</div>
<p class="gx-note" style="margin:0;">Installing opens the Tailscale login page to authenticate this device.</p>
</template>
</div>
</GalaxySection>
<GalaxySection title="Danger Zone" icon="bi-exclamation-triangle" :collapsible="false">
<div style="padding: var(--sp-3); display:grid; gap:12px;">
<p class="gx-note" style="margin:0;">Last resort only. <strong>Factory Reset (also known as SAVE ME)</strong> wipes params, backups, themes, models, maps, and route data, then reboots the device. This cannot be undone.</p>