summaryrefslogtreecommitdiffhomepage
path: root/cmd/tailscaled/cli/web.html
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/tailscaled/cli/web.html')
-rw-r--r--cmd/tailscaled/cli/web.html143
1 files changed, 143 insertions, 0 deletions
diff --git a/cmd/tailscaled/cli/web.html b/cmd/tailscaled/cli/web.html
new file mode 100644
index 000000000..2789d1e68
--- /dev/null
+++ b/cmd/tailscaled/cli/web.html
@@ -0,0 +1,143 @@
+<!doctype html>
+<html class="bg-gray-50">
+
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <link rel="shortcut icon"
+ href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQflAx4QGA4EvmzDAAAA30lEQVRIx2NgGAWMCKa8JKM4A8Ovt88ekyLCDGOoyDBJMjExMbFy8zF8/EKsCAMDE8yAPyIwFps48SJIBpAL4AZwvoSx/r0lXgQpDN58EWL5x/7/H+vL20+JFxluQKVe5b3Ke5V+0kQQCamfoYKBg4GDwUKI8d0BYkWQkrLKewYBKPPDHUFiRaiZkBgmwhj/F5IgggyUJ6i8V3mv0kCayDAAeEsklXqGAgYGhgV3CnGrwVciYSYk0kokhgS44/JxqqFpiYSZbEgskd4dEBRk1GD4wdB5twKXmlHAwMDAAACdEZau06NQUwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMC0wNy0xNVQxNTo1Mzo0MCswMDowMCVXsDIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjAtMDctMTVUMTU6NTM6NDArMDA6MDBUCgiOAAAAAElFTkSuQmCC" />
+ <title>Tailscale</title>
+ <style>{{template "web.css"}}</style>
+</head>
+
+<body class="py-14">
+<main class="container max-w-lg mx-auto py-6 px-8 bg-white rounded-md shadow-2xl" style="width: 95%">
+ <header class="flex justify-between items-center min-width-0 py-2 mb-8">
+ <svg width="26" height="26" viewBox="0 0 23 23" title="Tailscale" fill="none" xmlns="http://www.w3.org/2000/svg"
+ class="flex-shrink-0 mr-4">
+ <circle opacity="0.2" cx="3.4" cy="3.25" r="2.7" fill="currentColor"></circle>
+ <circle cx="3.4" cy="11.3" r="2.7" fill="currentColor"></circle>
+ <circle opacity="0.2" cx="3.4" cy="19.5" r="2.7" fill="currentColor"></circle>
+ <circle cx="11.5" cy="11.3" r="2.7" fill="currentColor"></circle>
+ <circle cx="11.5" cy="19.5" r="2.7" fill="currentColor"></circle>
+ <circle opacity="0.2" cx="11.5" cy="3.25" r="2.7" fill="currentColor"></circle>
+ <circle opacity="0.2" cx="19.5" cy="3.25" r="2.7" fill="currentColor"></circle>
+ <circle cx="19.5" cy="11.3" r="2.7" fill="currentColor"></circle>
+ <circle opacity="0.2" cx="19.5" cy="19.5" r="2.7" fill="currentColor"></circle>
+ </svg>
+ <div class="flex items-center justify-end space-x-2 w-2/3">
+ {{ with .Profile.LoginName }}
+ <div class="text-right truncate leading-4">
+ <h4 class="truncate">{{.}}</h4>
+ <a href="#" class="text-xs text-gray-500 hover:text-gray-700 js-loginButton">Switch account</a>
+ </div>
+ {{ end }}
+ <div class="relative flex-shrink-0 w-8 h-8 rounded-full overflow-hidden">
+ {{ with .Profile.ProfilePicURL }}
+ <div class="w-8 h-8 flex pointer-events-none rounded-full bg-gray-200"
+ style="background-image: url('{{.}}'); background-size: cover;"></div>
+ {{ else }}
+ <div class="w-8 h-8 flex pointer-events-none rounded-full border border-gray-400 border-dashed"></div>
+ {{ end }}
+ </div>
+ </div>
+ </header>
+ {{ if .IP }}
+ <div
+ class="border border-gray-200 bg-gray-0 rounded-lg p-2 pl-3 pr-3 mb-8 width-full flex items-center justify-between">
+ <div class="flex items-center min-width-0">
+ <svg class="flex-shrink-0 text-gray-600 mr-3 ml-1" xmlns="http://www.w3.org/2000/svg" width="20" height="20"
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
+ stroke-linejoin="round">
+ <rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect>
+ <rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect>
+ <line x1="6" y1="6" x2="6.01" y2="6"></line>
+ <line x1="6" y1="18" x2="6.01" y2="18"></line>
+ </svg>
+ <h4 class="font-semibold truncate mr-2">{{.DeviceName}}</h4>
+ </div>
+ <h5>{{.IP}}</h5>
+ </div>
+ {{ end }}
+ {{ if or (eq .Status "NeedsLogin") (eq .Status "NoState") }}
+ {{ if .IP }}
+ <div class="mb-6">
+ <p class="text-gray-700">Your device's key has expired. Reauthenticate this device by logging in again, or <a
+ href="https://tailscale.com/kb/1028/key-expiry" class="link" target="_blank">learn more</a>.</p>
+ </div>
+ <a href="#" class="mb-4 js-loginButton" target="_blank">
+ <button class="button button-blue w-full">Reauthenticate</button>
+ </a>
+ {{ else }}
+ <div class="mb-6">
+ <h3 class="text-3xl font-semibold mb-3">Log in</h3>
+ <p class="text-gray-700">Get started by logging in to your Tailscale network. Or,&nbsp;learn&nbsp;more at <a
+ href="https://tailscale.com/" class="link" target="_blank">tailscale.com</a>.</p>
+ </div>
+ <a href="#" class="mb-4 js-loginButton" target="_blank">
+ <button class="button button-blue w-full">Log In</button>
+ </a>
+ {{ end }}
+ {{ else if eq .Status "NeedsMachineAuth" }}
+ <div class="mb-4">
+ This device is authorized, but needs approval from a network admin before it can connect to the network.
+ </div>
+ {{ else }}
+ <div class="mb-4">
+ <p>You are connected! Access this device over Tailscale using the device name or IP address above.</p>
+ </div>
+ <a href="#" class="mb-4 link font-medium js-loginButton" target="_blank">Reauthenticate</a>
+ {{ end }}
+</main>
+<script>(function () {
+let loginButtons = document.querySelectorAll(".js-loginButton");
+let fetchingUrl = false;
+
+function handleClick(e) {
+ e.preventDefault();
+
+ if (fetchingUrl) {
+ return;
+ }
+
+ fetchingUrl = true;
+ const urlParams = new URLSearchParams(window.location.search);
+ const token = urlParams.get("SynoToken");
+ const nextParams = new URLSearchParams({ up: true });
+ if (token) {
+ nextParams.set("SynoToken", token)
+ }
+ const nextUrl = new URL(window.location);
+ nextUrl.search = nextParams.toString()
+ const url = nextUrl.toString();
+
+ fetch(url, {
+ method: "POST",
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/json",
+ }
+ }).then(res => res.json()).then(res => {
+ fetchingUrl = false;
+ const err = res["error"];
+ if (err) {
+ throw new Error(err);
+ }
+ const url = res["url"];
+ if (url) {
+ document.location.href = url;
+ } else {
+ location.reload();
+ }
+ }).catch(err => {
+ alert("Failed to log in: " + err.message);
+ });
+}
+
+Array.from(loginButtons).forEach(el => {
+ el.addEventListener("click", handleClick);
+})
+})();</script>
+</body>
+
+</html>