瀏覽代碼

Svelte: Restore support for Tailscale connection

Alessandro Pignotti 9 月之前
父節點
當前提交
f4648b08e6
共有 4 個文件被更改,包括 145 次插入8 次删除
  1. 97 6
      src/lib/NetworkingTab.svelte
  2. 1 1
      src/lib/SideBar.svelte
  3. 46 0
      src/lib/network.js
  4. 1 1
      vite.config.js

+ 97 - 6
src/lib/NetworkingTab.svelte

@@ -1,11 +1,102 @@
 <script>
 <script>
-	var currentIp = null;
+	import { networkData, startLogin } from '$lib/network.js'
+	import { createEventDispatcher } from 'svelte';
+	var dispatch = createEventDispatcher();
+	var connectionState = networkData.connectionState;
+	function handleConnect() {
+		connectionState.set("DOWNLOADING");
+		dispatch('connect');
+	}
+	async function handleCopyIP(event)
+	{
+		// To prevent the default contexmenu from showing up when right-clicking..
+		event.preventDefault();
+		// Copy the IP to the clipboard.
+		try
+		{
+			await window.navigator.clipboard.writeText(networkData.currentIp)
+			connectionState.set("IPCOPIED");
+			setTimeout(() => {
+				connectionState.set("CONNECTED");
+			}, 2000);
+		}
+		catch(msg)
+		{
+			console.log("Copy ip to clipboard: Error: " + msg);
+		}
+	}
+	function getButtonText(state)
+	{
+		switch(state)
+		{
+			case "DISCONNECTED":
+				return "Connect to Tailscale";
+			case "DOWNLOADING":
+				return "Loading IP stack...";
+			case "LOGINSTARTING":
+				return "Starting Login...";
+			case "LOGINREADY":
+				return "Login to Tailscale";
+			case "CONNECTED":
+				return `IP: ${networkData.currentIp}`;
+			case "IPCOPIED":
+				return "Copied!";
+			default:
+				break;
+		}
+		return `Text for state: ${state}`;
+	}
+	function isClickableState(state)
+	{
+		switch(state)
+		{
+			case "DISCONNECTED":
+			case "LOGINREADY":
+			case "CONNECTED":
+				return true;
+		}
+		return false;
+	}
+	function getClickHandler(state)
+	{
+		switch(state)
+		{
+			case "DISCONNECTED":
+				return handleConnect;
+		}
+		return null;
+	}
+	function getClickUrl(state)
+	{
+		switch(state)
+		{
+			case "LOGINREADY":
+				return networkData.loginUrl;
+			case "CONNECTED":
+				return networkData.dashboardUrl;
+		}
+		return null;
+	}
+	function getButtonTooltip(state)
+	{
+		switch(state)
+		{
+			case "CONNECTED":
+				return "Right-click to copy";
+		}
+		return null;
+	}
+	function getRightClickHandler(state)
+	{
+		switch(state)
+		{
+			case "CONNECTED":
+				return handleCopyIP;
+		}
+		return null;
+	}
 </script>
 </script>
 <h1 class="text-lg font-bold">Networking</h1>
 <h1 class="text-lg font-bold">Networking</h1>
-{#if currentIp === null}
-	<p class="bg-neutral-700 hover:bg-neutral-500 p-2 rounded-md cursor-pointer"><img src="assets/tailscale.svg" class="inline w-8"/><span class="ml-1">Connect to Tailscale</span></p>
-{:else}
-	<p>Current IP: {currentIp}</p>
-{/if}
+<a href={getClickUrl($connectionState)} target="_blank" on:click={getClickHandler($connectionState)} on:contextmenu={getRightClickHandler($connectionState)}><p class="bg-neutral-700 p-2 rounded-md {isClickableState($connectionState) ? "hover:bg-neutral-500 cursor-pointer" : ""}" title={getButtonTooltip($connectionState)}><img src="assets/tailscale.svg" class="inline w-8"/><span class="ml-1">{getButtonText($connectionState)}</span></p></a>
 <p>WebVM can connect to the Internet via Tailscale</p>
 <p>WebVM can connect to the Internet via Tailscale</p>
 <p>Using Tailscale is required since browser do not support TCP/UDP sockets (yet!)</p>
 <p>Using Tailscale is required since browser do not support TCP/UDP sockets (yet!)</p>

+ 1 - 1
src/lib/SideBar.svelte

@@ -40,7 +40,7 @@
 	</div>
 	</div>
 	<div class="flex flex-col gap-5 shrink-0 w-60 h-full z-10 p-2 bg-neutral-600 text-gray-100" class:hidden={!activeInfo}>
 	<div class="flex flex-col gap-5 shrink-0 w-60 h-full z-10 p-2 bg-neutral-600 text-gray-100" class:hidden={!activeInfo}>
 		{#if activeInfo === 'Networking'}
 		{#if activeInfo === 'Networking'}
-			<NetworkingTab />
+			<NetworkingTab on:connect/>
 		{:else}
 		{:else}
 			<p>{activeInfo}</p>
 			<p>{activeInfo}</p>
 		{/if}
 		{/if}

+ 46 - 0
src/lib/network.js

@@ -0,0 +1,46 @@
+import { writable } from 'svelte/store';
+
+let params = new URLSearchParams("?"+window.location.hash.substr(1));
+let authKey = params.get("authKey") || undefined;
+let controlUrl = params.get("controlUrl") || undefined;
+let dashboardUrl = controlUrl ? null : "https://login.tailscale.com/admin/machines";
+let resolveLogin = null;
+let loginPromise = new Promise((f,r) => {
+	resolveLogin = f;
+});
+let connectionState = writable("DISCONNECTED");
+
+function loginUrlCb(url)
+{
+        resolveLogin(url);
+}
+
+function stateUpdateCb(state)
+{
+	switch(state)
+	{
+		case 6 /*Running*/:
+		{
+			connectionState.set("CONNECTED");
+			break;
+		}
+	}
+}
+
+function netmapUpdateCb(map)
+{
+	networkData.currentIp = map.self.addresses[0];
+}
+
+export async function startLogin()
+{
+	connectionState.set("LOGINSTARTING");
+	const url = await loginPromise;
+	networkData.loginUrl = url;
+	connectionState.set("LOGINREADY");
+	return url;
+}
+
+export const networkInterface = { authKey: authKey, controlUrl: controlUrl, loginUrlCb: loginUrlCb, stateUpdateCb: stateUpdateCb, netmapUpdateCb: netmapUpdateCb };
+
+export const networkData = { currentIp: null, connectionState: connectionState, loginUrl: null, dashboardUrl: dashboardUrl }

+ 1 - 1
vite.config.js

@@ -17,8 +17,8 @@ export default defineConfig({
 			targets: [
 			targets: [
 				{ src: 'tower.ico', dest: '' },
 				{ src: 'tower.ico', dest: '' },
 				{ src: 'scrollbar.css', dest: '' },
 				{ src: 'scrollbar.css', dest: '' },
-				{ src: 'network.js', dest: '' },
 				{ src: 'serviceWorker.js', dest: '' },
 				{ src: 'serviceWorker.js', dest: '' },
+				{ src: 'login.html', dest: '' },
 				{ src: 'assets/', dest: '' }
 				{ src: 'assets/', dest: '' }
 			]
 			]
 		})
 		})