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).
|
||||
|
|
|
@ -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>
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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)),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -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