diff --git a/starpilot/system/the_galaxy/assets/mobile/js/api.js b/starpilot/system/the_galaxy/assets/mobile/js/api.js index 6fd194bd16..053eab8355 100644 --- a/starpilot/system/the_galaxy/assets/mobile/js/api.js +++ b/starpilot/system/the_galaxy/assets/mobile/js/api.js @@ -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) { diff --git a/starpilot/system/the_galaxy/assets/mobile/js/views/SystemTools.js b/starpilot/system/the_galaxy/assets/mobile/js/views/SystemTools.js index 7b5955be63..cd4ca5914f 100644 --- a/starpilot/system/the_galaxy/assets/mobile/js/views/SystemTools.js +++ b/starpilot/system/the_galaxy/assets/mobile/js/views/SystemTools.js @@ -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: `
+ Tailscale creates a secure, private connection between your openpilot device and your phone or PC so you can access and control it from anywhere! +
+Installing opens the Tailscale login page to authenticate this device.
+ +Last resort only. Factory Reset (also known as SAVE ME) wipes params, backups, themes, models, maps, and route data, then reboots the device. This cannot be undone.