mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Changed all the html into go using go-elem (#2161)
* Changed all the HTML into go using go-elem
            Created templates package in ./hscontrol/templates.
            Moved the registerWebAPITemplate into the templates package as a function to be called.
            Replaced the apple and windows html files with go-elem.
* update flake
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
---------
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
			
			
This commit is contained in:
		
							parent
							
								
									9515040161
								
							
						
					
					
						commit
						24e7851a40
					
				| @ -32,7 +32,7 @@ | ||||
| 
 | ||||
|           # When updating go.mod or go.sum, a new sha will need to be calculated, | ||||
|           # update this if you have a mismatch after doing a change to thos files. | ||||
|           vendorHash = "sha256-SDJSFji6498WI9bJLmY62VGt21TtD2GxrxRAWyYyr0c="; | ||||
|           vendorHash = "sha256-CMkYTRjmhvTTrB7JbLj0cj9VEyzpG0iUWXkaOagwYTk="; | ||||
| 
 | ||||
|           subPackages = ["cmd/headscale"]; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @ -4,6 +4,7 @@ go 1.23.1 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/AlecAivazis/survey/v2 v2.3.7 | ||||
| 	github.com/chasefleming/elem-go v0.29.0 | ||||
| 	github.com/coder/websocket v1.8.12 | ||||
| 	github.com/coreos/go-oidc/v3 v3.11.0 | ||||
| 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc | ||||
|  | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @ -90,6 +90,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= | ||||
| github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||||
| github.com/chasefleming/elem-go v0.29.0 h1:WwrjQcVn6xldhexluvl2Z3sgKi9HTMuzWeEXO4PHsmg= | ||||
| github.com/chasefleming/elem-go v0.29.0/go.mod h1:hz73qILBIKnTgOujnSMtEj20/epI+f6vg71RUilJAA4= | ||||
| github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= | ||||
| github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= | ||||
| github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= | ||||
|  | ||||
| @ -1,17 +1,19 @@ | ||||
| package hscontrol | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"html/template" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/chasefleming/elem-go" | ||||
| 	"github.com/chasefleming/elem-go/attrs" | ||||
| 	"github.com/chasefleming/elem-go/styles" | ||||
| 	"github.com/gorilla/mux" | ||||
| 	"github.com/juanfont/headscale/hscontrol/templates" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"tailscale.com/tailcfg" | ||||
| 	"tailscale.com/types/key" | ||||
| @ -135,38 +137,37 @@ func (h *Headscale) HealthHandler( | ||||
| 	respond(nil) | ||||
| } | ||||
| 
 | ||||
| type registerWebAPITemplateConfig struct { | ||||
| 	Key string | ||||
| var codeStyleRegisterWebAPI = styles.Props{ | ||||
| 	styles.Display:         "block", | ||||
| 	styles.Padding:         "20px", | ||||
| 	styles.Border:          "1px solid #bbb", | ||||
| 	styles.BackgroundColor: "#eee", | ||||
| } | ||||
| 
 | ||||
| var registerWebAPITemplate = template.Must( | ||||
| 	template.New("registerweb").Parse(` | ||||
| <html> | ||||
| 	<head> | ||||
| 		<title>Registration - Headscale</title> | ||||
| 		<meta name=viewport content="width=device-width, initial-scale=1"> | ||||
| 		<style> | ||||
| 			body { | ||||
| 				font-family: sans; | ||||
| 			} | ||||
| 			code { | ||||
| 				display: block; | ||||
| 				padding: 20px; | ||||
| 				border: 1px solid #bbb; | ||||
| 				background-color: #eee; | ||||
| 			} | ||||
| 		</style> | ||||
| 	</head> | ||||
| 	<body> | ||||
| 		<h1>headscale</h1> | ||||
| 		<h2>Machine registration</h2> | ||||
| 		<p> | ||||
| 			Run the command below in the headscale server to add this machine to your network: | ||||
| 		</p> | ||||
| 		<code>headscale nodes register --user USERNAME --key {{.Key}}</code> | ||||
| 	</body> | ||||
| </html> | ||||
| `)) | ||||
| func registerWebHTML(key string) *elem.Element { | ||||
| 	return elem.Html(nil, | ||||
| 		elem.Head( | ||||
| 			nil, | ||||
| 			elem.Title(nil, elem.Text("Registration - Headscale")), | ||||
| 			elem.Meta(attrs.Props{ | ||||
| 				attrs.Name:    "viewport", | ||||
| 				attrs.Content: "width=device-width, initial-scale=1", | ||||
| 			}), | ||||
| 		), | ||||
| 		elem.Body(attrs.Props{ | ||||
| 			attrs.Style: styles.Props{ | ||||
| 				styles.FontFamily: "sans", | ||||
| 			}.ToInline(), | ||||
| 		}, | ||||
| 			elem.H1(nil, elem.Text("headscale")), | ||||
| 			elem.H2(nil, elem.Text("Machine registration")), | ||||
| 			elem.P(nil, elem.Text("Run the command below in the headscale server to add this machine to your network:")), | ||||
| 			elem.Code(attrs.Props{attrs.Style: codeStyleRegisterWebAPI.ToInline()}, | ||||
| 				elem.Text(fmt.Sprintf("headscale nodes register --user USERNAME --key %s", key)), | ||||
| 			), | ||||
| 		), | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| type AuthProviderWeb struct { | ||||
| 	serverURL string | ||||
| @ -220,34 +221,14 @@ func (a *AuthProviderWeb) RegisterHandler( | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	var content bytes.Buffer | ||||
| 	if err := registerWebAPITemplate.Execute(&content, registerWebAPITemplateConfig{ | ||||
| 		Key: machineKey.String(), | ||||
| 	}); err != nil { | ||||
| 		log.Error(). | ||||
| 			Str("func", "RegisterWebAPI"). | ||||
| 			Err(err). | ||||
| 			Msg("Could not render register web API template") | ||||
| 		writer.Header().Set("Content-Type", "text/plain; charset=utf-8") | ||||
| 		writer.WriteHeader(http.StatusInternalServerError) | ||||
| 		_, err = writer.Write([]byte("Could not render register web API template")) | ||||
| 		if err != nil { | ||||
| 	writer.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	writer.WriteHeader(http.StatusOK) | ||||
| 	if _, err := writer.Write([]byte(registerWebHTML(machineKey.String()).Render())); err != nil { | ||||
| 		if _, err := writer.Write([]byte(templates.RegisterWeb(machineKey.String()).Render())); err != nil { | ||||
| 			log.Error(). | ||||
| 				Caller(). | ||||
| 				Err(err). | ||||
| 				Msg("Failed to write response") | ||||
| 		} | ||||
| 
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	writer.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	writer.WriteHeader(http.StatusOK) | ||||
| 	_, err = writer.Write(content.Bytes()) | ||||
| 	if err != nil { | ||||
| 		log.Error(). | ||||
| 			Caller(). | ||||
| 			Err(err). | ||||
| 			Msg("Failed to write response") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -9,49 +9,19 @@ import ( | ||||
| 
 | ||||
| 	"github.com/gofrs/uuid/v5" | ||||
| 	"github.com/gorilla/mux" | ||||
| 	"github.com/juanfont/headscale/hscontrol/templates" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| ) | ||||
| 
 | ||||
| //go:embed templates/apple.html
 | ||||
| var appleTemplate string | ||||
| 
 | ||||
| //go:embed templates/windows.html
 | ||||
| var windowsTemplate string | ||||
| 
 | ||||
| // WindowsConfigMessage shows a simple message in the browser for how to configure the Windows Tailscale client.
 | ||||
| func (h *Headscale) WindowsConfigMessage( | ||||
| 	writer http.ResponseWriter, | ||||
| 	req *http.Request, | ||||
| ) { | ||||
| 	winTemplate := template.Must(template.New("windows").Parse(windowsTemplate)) | ||||
| 	config := map[string]interface{}{ | ||||
| 		"URL": h.cfg.ServerURL, | ||||
| 	} | ||||
| 
 | ||||
| 	var payload bytes.Buffer | ||||
| 	if err := winTemplate.Execute(&payload, config); err != nil { | ||||
| 		log.Error(). | ||||
| 			Str("handler", "WindowsRegConfig"). | ||||
| 			Err(err). | ||||
| 			Msg("Could not render Windows index template") | ||||
| 
 | ||||
| 		writer.Header().Set("Content-Type", "text/plain; charset=utf-8") | ||||
| 		writer.WriteHeader(http.StatusInternalServerError) | ||||
| 		_, err := writer.Write([]byte("Could not render Windows index template")) | ||||
| 		if err != nil { | ||||
| 			log.Error(). | ||||
| 				Caller(). | ||||
| 				Err(err). | ||||
| 				Msg("Failed to write response") | ||||
| 		} | ||||
| 
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	writer.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	writer.WriteHeader(http.StatusOK) | ||||
| 	_, err := writer.Write(payload.Bytes()) | ||||
| 	if err != nil { | ||||
| 
 | ||||
| 	if _, err := writer.Write([]byte(templates.Windows(h.cfg.ServerURL).Render())); err != nil { | ||||
| 		log.Error(). | ||||
| 			Caller(). | ||||
| 			Err(err). | ||||
| @ -64,36 +34,10 @@ func (h *Headscale) AppleConfigMessage( | ||||
| 	writer http.ResponseWriter, | ||||
| 	req *http.Request, | ||||
| ) { | ||||
| 	appleTemplate := template.Must(template.New("apple").Parse(appleTemplate)) | ||||
| 
 | ||||
| 	config := map[string]interface{}{ | ||||
| 		"URL": h.cfg.ServerURL, | ||||
| 	} | ||||
| 
 | ||||
| 	var payload bytes.Buffer | ||||
| 	if err := appleTemplate.Execute(&payload, config); err != nil { | ||||
| 		log.Error(). | ||||
| 			Str("handler", "AppleMobileConfig"). | ||||
| 			Err(err). | ||||
| 			Msg("Could not render Apple index template") | ||||
| 
 | ||||
| 		writer.Header().Set("Content-Type", "text/plain; charset=utf-8") | ||||
| 		writer.WriteHeader(http.StatusInternalServerError) | ||||
| 		_, err := writer.Write([]byte("Could not render Apple index template")) | ||||
| 		if err != nil { | ||||
| 			log.Error(). | ||||
| 				Caller(). | ||||
| 				Err(err). | ||||
| 				Msg("Failed to write response") | ||||
| 		} | ||||
| 
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	writer.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 	writer.WriteHeader(http.StatusOK) | ||||
| 	_, err := writer.Write(payload.Bytes()) | ||||
| 	if err != nil { | ||||
| 
 | ||||
| 	if _, err := writer.Write([]byte(templates.Apple(h.cfg.ServerURL).Render())); err != nil { | ||||
| 		log.Error(). | ||||
| 			Caller(). | ||||
| 			Err(err). | ||||
|  | ||||
							
								
								
									
										149
									
								
								hscontrol/templates/apple.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								hscontrol/templates/apple.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,149 @@ | ||||
| package templates | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/chasefleming/elem-go" | ||||
| 	"github.com/chasefleming/elem-go/attrs" | ||||
| ) | ||||
| 
 | ||||
| func Apple(url string) *elem.Element { | ||||
| 	return HtmlStructure( | ||||
| 		elem.Title(nil, | ||||
| 			elem.Text("headscale - Apple")), | ||||
| 		elem.Body(attrs.Props{ | ||||
| 			attrs.Style: bodyStyle.ToInline(), | ||||
| 		}, | ||||
| 			headerOne("headscale: iOS configuration"), | ||||
| 			headerTwo("GUI"), | ||||
| 			elem.Ol(nil, | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("Install the official Tailscale iOS client from the "), | ||||
| 					elem.A(attrs.Props{attrs.Href: "https://apps.apple.com/app/tailscale/id1470499037"}, | ||||
| 						elem.Text("App store"), | ||||
| 					), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("Open Tailscale and make sure you are "), | ||||
| 					elem.I(nil, elem.Text("not ")), | ||||
| 					elem.Text("logged in to any account"), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("Open Settings on the iOS device"), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`Scroll down to the "third party apps" section, under "Game Center" or "TV Provider"`), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("Find Tailscale and select it"), | ||||
| 					elem.Ul(nil, | ||||
| 						elem.Li(nil, | ||||
| 							elem.Text(`If the iOS device was previously logged into Tailscale, switch the "Reset Keychain" toggle to "on"`), | ||||
| 						), | ||||
| 					), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(fmt.Sprintf(`Enter "%s" under "Alternate Coordination Server URL"`,url)), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("Restart the app by closing it from the iOS app switcher, open the app and select the regular sign in option "), | ||||
| 					elem.I(nil, elem.Text("(non-SSO)")), | ||||
| 					elem.Text(". It should open up to the headscale authentication page."), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("Enter your credentials and log in. Headscale should now be working on your iOS device"), | ||||
| 				), | ||||
| 			), | ||||
| 			headerOne("headscale: macOS configuration"), | ||||
| 			headerTwo("Command line"), | ||||
| 			elem.P(nil, | ||||
| 				elem.Text("Use Tailscale's login command to add your profile:"), | ||||
| 			), | ||||
| 			elem.Pre(nil, | ||||
| 				elem.Code(nil, | ||||
| 					elem.Text(fmt.Sprintf("tailscale login --login-server %s",url)), | ||||
| 				), | ||||
| 			), | ||||
| 			headerTwo("GUI"), | ||||
| 			elem.Ol(nil, | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("ALT + Click the Tailscale icon in the menu and hover over the Debug menu"), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`Under "Custom Login Server", select "Add Account..."`), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(fmt.Sprintf(`Enter "%s" of the headscale instance and press "Add Account"`,url)), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`Follow the login procedure in the browser`), | ||||
| 				), | ||||
| 			), | ||||
| 			headerTwo("Profiles"), | ||||
| 			elem.P(nil, | ||||
| 				elem.Text("Headscale can be set to the default server by installing a Headscale configuration profile:"), | ||||
| 			), | ||||
| 			elem.P(nil, | ||||
| 				elem.A(attrs.Props{attrs.Href: "/apple/macos-app-store", attrs.Download: "headscale_macos.mobileconfig"}, | ||||
| 					elem.Text("macOS AppStore profile "), | ||||
| 				), | ||||
| 				elem.A(attrs.Props{attrs.Href: "/apple/macos-standalone", attrs.Download: "headscale_macos.mobileconfig"}, | ||||
| 					elem.Text("macOS Standalone profile"), | ||||
| 				), | ||||
| 			), | ||||
| 			elem.Ol(nil, | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text("Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed"), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`Open System Preferences and go to "Profiles"`), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`Find and install the Headscale profile`), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`Restart Tailscale.app and log in`), | ||||
| 				), | ||||
| 			), | ||||
| 			elem.P(nil, elem.Text("Or")), | ||||
| 			elem.P(nil, | ||||
| 				elem.Text("Use your terminal to configure the default setting for Tailscale by issuing:"), | ||||
| 			), | ||||
| 			elem.Ul(nil, | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`for app store client:`), | ||||
| 					elem.Code(nil, | ||||
| 						elem.Text(fmt.Sprintf(`defaults write io.tailscale.ipn.macos ControlURL %s`,url)), | ||||
| 					), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`for standalone client:`), | ||||
| 					elem.Code(nil, | ||||
| 						elem.Text(fmt.Sprintf(`defaults write io.tailscale.ipn.macsys ControlURL %s`,url)), | ||||
| 					), | ||||
| 				), | ||||
| 			), | ||||
| 			elem.P(nil, | ||||
| 				elem.Text("Restart Tailscale.app and log in."), | ||||
| 			), | ||||
| 			headerThree("Caution"), | ||||
| 			elem.P(nil, | ||||
| 				elem.Text("You should always download and inspect the profile before installing it:"), | ||||
| 			), | ||||
| 			elem.Ul(nil, | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`for app store client: `), | ||||
| 					elem.Code(nil, | ||||
| 						elem.Text(fmt.Sprintf(`curl %s/apple/macos-app-store`,url)), | ||||
| 					), | ||||
| 				), | ||||
| 				elem.Li(nil, | ||||
| 					elem.Text(`for standalone client: `), | ||||
| 					elem.Code(nil, | ||||
| 						elem.Text(fmt.Sprintf(`curl %s/apple/macos-standalone`,url)), | ||||
| 					), | ||||
| 				), | ||||
| 			), | ||||
| 		), | ||||
| 	) | ||||
| } | ||||
| @ -1,131 +0,0 @@ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>headscale - Apple</title> | ||||
|     <style> | ||||
|       body { | ||||
|         margin: 40px auto; | ||||
|         max-width: 800px; | ||||
|         line-height: 1.5; | ||||
|         font-size: 16px; | ||||
|         color: #444; | ||||
|         padding: 0 10px; | ||||
|         font-family: Sans-serif; | ||||
|       } | ||||
| 
 | ||||
|       h1, | ||||
|       h2, | ||||
|       h3 { | ||||
|         line-height: 1.2; | ||||
|       } | ||||
|     </style> | ||||
|   </head> | ||||
| 
 | ||||
|   <body> | ||||
|     <h1>headscale: iOS configuration</h1> | ||||
|     <h2>GUI</h2> | ||||
|     <ol> | ||||
|       <li> | ||||
|         Install the official Tailscale iOS client from the | ||||
|         <a href="https://apps.apple.com/app/tailscale/id1470499037" | ||||
|           >App store</a | ||||
|         > | ||||
|       </li> | ||||
|       <li> | ||||
|         Open Tailscale and make sure you are <i>not</i> logged in to any account | ||||
|       </li> | ||||
|       <li>Open Settings on the iOS device</li> | ||||
|       <li> | ||||
|         Scroll down to the "third party apps" section, under "Game Center" or | ||||
|         "TV Provider" | ||||
|       </li> | ||||
|       <li> | ||||
|         Find Tailscale and select it | ||||
|         <ul> | ||||
|           <li> | ||||
|             If the iOS device was previously logged into Tailscale, switch the | ||||
|             "Reset Keychain" toggle to "on" | ||||
|           </li> | ||||
|         </ul> | ||||
|       </li> | ||||
|       <li>Enter "{{.URL}}" under "Alternate Coordination Server URL"</li> | ||||
|       <li> | ||||
|         Restart the app by closing it from the iOS app switcher, open the app | ||||
|         and select the regular sign in option <i>(non-SSO)</i>. It should open | ||||
|         up to the headscale authentication page. | ||||
|       </li> | ||||
|       <li> | ||||
|         Enter your credentials and log in. Headscale should now be working on | ||||
|         your iOS device | ||||
|       </li> | ||||
|     </ol> | ||||
|     <h1>headscale: macOS configuration</h1> | ||||
|     <h2>Command line</h2> | ||||
|     <p>Use Tailscale's login command to add your profile:</p> | ||||
|     <pre><code>tailscale login --login-server {{.URL}}</code></pre> | ||||
|     <h2>GUI</h2> | ||||
|     <ol> | ||||
|       <li> | ||||
|         ALT + Click the Tailscale icon in the menu and hover over the Debug menu | ||||
|       </li> | ||||
|       <li>Under "Custom Login Server", select "Add Account..."</li> | ||||
|       <li> | ||||
|         Enter "{{.URL}}" of the headscale instance and press "Add Account" | ||||
|       </li> | ||||
|       <li>Follow the login procedure in the browser</li> | ||||
|     </ol> | ||||
|     <h2>Profiles</h2> | ||||
|     <p> | ||||
|       Headscale can be set to the default server by installing a Headscale | ||||
|       configuration profile: | ||||
|     </p> | ||||
|     <p> | ||||
|       <a href="/apple/macos-app-store" download="headscale_macos.mobileconfig" | ||||
|         >macOS AppStore profile</a | ||||
|       > | ||||
|       <a href="/apple/macos-standalone" download="headscale_macos.mobileconfig" | ||||
|         >macOS Standalone profile</a | ||||
|       > | ||||
|     </p> | ||||
|     <ol> | ||||
|       <li> | ||||
|         Download the profile, then open it. When it has been opened, there | ||||
|         should be a notification that a profile can be installed | ||||
|       </li> | ||||
|       <li>Open System Preferences and go to "Profiles"</li> | ||||
|       <li>Find and install the Headscale profile</li> | ||||
|       <li>Restart Tailscale.app and log in</li> | ||||
|     </ol> | ||||
|     <p>Or</p> | ||||
|     <p> | ||||
|       Use your terminal to configure the default setting for Tailscale by | ||||
|       issuing: | ||||
|     </p> | ||||
|     <ul> | ||||
|       <li> | ||||
|         for app store client: | ||||
|         <code>defaults write io.tailscale.ipn.macos ControlURL {{.URL}}</code> | ||||
|       </li> | ||||
|       <li> | ||||
|         for standalone client: | ||||
|         <code>defaults write io.tailscale.ipn.macsys ControlURL {{.URL}}</code> | ||||
|       </li> | ||||
|     </ul> | ||||
|     <p>Restart Tailscale.app and log in.</p> | ||||
|     <h3>Caution</h3> | ||||
|     <p> | ||||
|       You should always download and inspect the profile before installing it: | ||||
|     </p> | ||||
|     <ul> | ||||
|       <li> | ||||
|         for app store client: <code>curl {{.URL}}/apple/macos-app-store</code> | ||||
|       </li> | ||||
|       <li> | ||||
|         for standalone client: <code>curl {{.URL}}/apple/macos-standalone</code> | ||||
|       </li> | ||||
|     </ul> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										56
									
								
								hscontrol/templates/general.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								hscontrol/templates/general.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| package templates | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/chasefleming/elem-go" | ||||
| 	"github.com/chasefleming/elem-go/attrs" | ||||
| 	"github.com/chasefleming/elem-go/styles" | ||||
| ) | ||||
| 
 | ||||
| var bodyStyle = styles.Props{ | ||||
| 	styles.Margin:     "40px auto", | ||||
| 	styles.MaxWidth:   "800px", | ||||
| 	styles.LineHeight: "1.5", | ||||
| 	styles.FontSize:   "16px", | ||||
| 	styles.Color:      "#444", | ||||
| 	styles.Padding:    "0 10px", | ||||
| 	styles.FontFamily: "Sans-serif", | ||||
| } | ||||
| 
 | ||||
| var headerStyle = styles.Props{ | ||||
| 	styles.LineHeight: "1.2", | ||||
| } | ||||
| 
 | ||||
| func headerOne(text string) *elem.Element { | ||||
| 	return elem.H1(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text)) | ||||
| } | ||||
| 
 | ||||
| func headerTwo(text string) *elem.Element { | ||||
| 	return elem.H2(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text)) | ||||
| } | ||||
| 
 | ||||
| func headerThree(text string) *elem.Element { | ||||
| 	return elem.H3(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text)) | ||||
| } | ||||
| 
 | ||||
| func HtmlStructure(head, body *elem.Element) *elem.Element { | ||||
| 	return elem.Html(nil, | ||||
| 		elem.Head( | ||||
| 			attrs.Props{ | ||||
| 				attrs.Lang: "en", | ||||
| 			}, | ||||
| 			elem.Meta(attrs.Props{ | ||||
| 				attrs.Charset: "UTF-8", | ||||
| 			}), | ||||
| 			elem.Meta(attrs.Props{ | ||||
| 				attrs.HTTPequiv: "X-UA-Compatible", | ||||
| 				attrs.Content:   "IE=edge", | ||||
| 			}), | ||||
| 			elem.Meta(attrs.Props{ | ||||
| 				attrs.Name:    "viewport", | ||||
| 				attrs.Content: "width=device-width, initial-scale=1.0", | ||||
| 			}), | ||||
| 			head, | ||||
| 		), | ||||
| 		body, | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										34
									
								
								hscontrol/templates/register_web.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								hscontrol/templates/register_web.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| package templates | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/chasefleming/elem-go" | ||||
| 	"github.com/chasefleming/elem-go/attrs" | ||||
| 	"github.com/chasefleming/elem-go/styles" | ||||
| ) | ||||
| 
 | ||||
| var codeStyleRegisterWebAPI = styles.Props{ | ||||
| 	styles.Display:         "block", | ||||
| 	styles.Padding:         "20px", | ||||
| 	styles.Border:          "1px solid #bbb", | ||||
| 	styles.BackgroundColor: "#eee", | ||||
| } | ||||
| 
 | ||||
| func RegisterWeb(key string) *elem.Element { | ||||
| 	return HtmlStructure( | ||||
| 		elem.Title(nil, elem.Text("Registration - Headscale")), | ||||
| 		elem.Body(attrs.Props{ | ||||
| 			attrs.Style: styles.Props{ | ||||
| 				styles.FontFamily: "sans", | ||||
| 			}.ToInline(), | ||||
| 		}, | ||||
| 			elem.H1(nil, elem.Text("headscale")), | ||||
| 			elem.H2(nil, elem.Text("Machine registration")), | ||||
| 			elem.P(nil, elem.Text("Run the command below in the headscale server to add this machine to your network: ")), | ||||
| 			elem.Code(attrs.Props{attrs.Style: codeStyleRegisterWebAPI.ToInline()}, | ||||
| 				elem.Text(fmt.Sprintf("headscale nodes register --user USERNAME --key %s", key)), | ||||
| 			), | ||||
| 		), | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										38
									
								
								hscontrol/templates/windows.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								hscontrol/templates/windows.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| package templates | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/chasefleming/elem-go" | ||||
| 	"github.com/chasefleming/elem-go/attrs" | ||||
| ) | ||||
| 
 | ||||
| func Windows(url string) *elem.Element { | ||||
| 	return HtmlStructure( | ||||
| 		elem.Title(nil, | ||||
| 			elem.Text("headscale - Windows"), | ||||
| 		), | ||||
| 		elem.Body(attrs.Props{ | ||||
| 			attrs.Style : bodyStyle.ToInline(), | ||||
| 		}, | ||||
| 			headerOne("headscale: Windows configuration"), | ||||
| 			elem.P(nil, | ||||
| 				elem.Text("Download "), | ||||
| 				elem.A(attrs.Props{ | ||||
| 					attrs.Href:   "https://tailscale.com/download/windows", | ||||
| 					attrs.Rel:    "noreferrer noopener", | ||||
| 					attrs.Target: "_blank"}, | ||||
| 					elem.Text("Tailscale for Windows ")), | ||||
| 				elem.Text("and install it."), | ||||
| 			), | ||||
| 			elem.P(nil, | ||||
| 				elem.Text("Open a Command Prompt or Powershell and use Tailscale's login command to connect with headscale: "), | ||||
| 			), | ||||
| 			elem.Pre(nil, | ||||
| 				elem.Code(nil, | ||||
| 					elem.Text(fmt.Sprintf(`tailscale login --login-server %s`, url)), | ||||
| 				), | ||||
| 			), | ||||
| 		), | ||||
| 	) | ||||
| } | ||||
| @ -1,45 +0,0 @@ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>headscale - Windows</title> | ||||
|     <style> | ||||
|       body { | ||||
|         margin: 40px auto; | ||||
|         max-width: 800px; | ||||
|         line-height: 1.5; | ||||
|         font-size: 16px; | ||||
|         color: #444; | ||||
|         padding: 0 10px; | ||||
|         font-family: Sans-serif; | ||||
|       } | ||||
|       h1, | ||||
|       h2, | ||||
|       h3 { | ||||
|         line-height: 1.2; | ||||
|       } | ||||
|     </style> | ||||
|   </head> | ||||
| 
 | ||||
|   <body> | ||||
|     <h1>headscale: Windows configuration</h1> | ||||
|     <p> | ||||
|       Download | ||||
|       <a | ||||
|         href="https://tailscale.com/download/windows" | ||||
|         rel="noreferrer noopener" | ||||
|         target="_blank" | ||||
|         >Tailscale for Windows</a | ||||
|       > | ||||
|       and install it. | ||||
|     </p> | ||||
| 
 | ||||
|     <p> | ||||
|       Open a Command Prompt or Powershell and use Tailscale's login command to | ||||
|       connect with headscale: | ||||
|     </p> | ||||
|     <pre><code>tailscale login --login-server {{.URL}}</code></pre> | ||||
|   </body> | ||||
| </html> | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user