Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed all the html into go using go-elem #2161

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
93 changes: 37 additions & 56 deletions hscontrol/handlers.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
}
}
66 changes: 5 additions & 61 deletions hscontrol/platform_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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).
Expand Down
149 changes: 149 additions & 0 deletions hscontrol/templates/apple.go
Original file line number Diff line number Diff line change
@@ -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)")),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space:

image

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)),
),
),
),
),
)
}
Loading
Loading