cmd, templates, integration: extract shared production constants

Constants the operator/test reader benefits from centralising.
Tests stay verbatim (the .golangci.yaml goconst tune skips them);
extraction here applies only where the same literal acts as
shared vocabulary across files.

  cmd/headscale/cli/strings.go (new)
    Cobra subcommand verbs and aliases shared by every list / show
    / new / delete / expire command across api_key, nodes, policy,
    preauthkeys, users — plus the Result / Created / Expiration
    column headers used in printOutput maps.

  hscontrol/templates/design.go
    cssBorderHS, cssBreakWord, cssCenter, cssOverflowWrap — shared
    styles applied across design.go, ping.go, register_confirm.go.
    spaceS already existed; switch raw "0.5rem" literals to it.

  integration/hsic/hsic.go
    binHeadscale, flagOutput, acceptJSON — names invoked across
    hsic.go and config.go.

  integration/tsic/tsic.go
    tailscaleBin — used across docker exec call sites.
This commit is contained in:
Kristoffer Dalby 2026-05-18 18:33:40 +00:00
parent 64c398f2c2
commit e00c899219
15 changed files with 263 additions and 216 deletions

View file

@ -58,6 +58,14 @@ const (
space3XL = "4rem" //nolint:unused // 64px - 3x extra large spacing
)
// Shared CSS value constants used across templates.
const (
cssBorderHS = "1px solid var(--hs-border)" //nolint:unused // Shared HS border
cssBreakWord = "break-word" //nolint:unused // Word wrapping
cssCenter = "center" //nolint:unused // Center alignment
cssOverflowWrap = "overflow-wrap" //nolint:unused // CSS property key
)
// Typography System
// EXTRACTED FROM: https://headscale.net/stable/assets/stylesheets/main.342714a4.min.css
// Material for MkDocs typography - exact values from .md-typeset CSS.
@ -121,8 +129,8 @@ func card(title string, children ...elem.Node) *elem.Element {
return elem.Div(attrs.Props{
attrs.Style: styles.Props{
styles.Background: "var(--hs-bg)",
styles.Border: "1px solid var(--hs-border)",
styles.BorderRadius: "0.5rem",
styles.Border: cssBorderHS,
styles.BorderRadius: spaceS,
styles.Padding: "clamp(1rem, 3vw, 1.5rem)",
styles.MarginBottom: spaceL,
styles.BoxShadow: "0 1px 3px rgba(0,0,0,0.1)",
@ -147,8 +155,8 @@ func codeBlock(code string) *elem.Element {
styles.FontSize: fontSizeCode, // 0.85em
styles.LineHeight: lineHeightCode, // 1.4
styles.OverflowX: "auto", // Horizontal scroll
"overflow-wrap": "break-word", // Word wrapping
"word-wrap": "break-word", // Legacy support
cssOverflowWrap: cssBreakWord, // Word wrapping
"word-wrap": cssBreakWord, // Legacy support
styles.WhiteSpace: "pre-wrap", // Preserve whitespace
styles.MarginTop: spaceM, // 1em
styles.MarginBottom: spaceM, // 1em
@ -171,7 +179,7 @@ func baseTypesetStyles() styles.Props {
styles.LineHeight: lineHeightBase, // 1.6
styles.Color: colorTextPrimary,
styles.FontFamily: fontFamilySystem,
"overflow-wrap": "break-word",
cssOverflowWrap: cssBreakWord,
styles.TextAlign: "left",
}
}
@ -190,7 +198,7 @@ func h1Styles() styles.Props {
styles.FontWeight: "300",
"letter-spacing": "-0.01em",
styles.FontFamily: fontFamilySystem, // Roboto
"overflow-wrap": "break-word",
cssOverflowWrap: cssBreakWord,
}
}
@ -208,7 +216,7 @@ func h2Styles() styles.Props {
"letter-spacing": "-0.01em",
styles.Color: colorTextSecondary, // rgba(0, 0, 0, 0.54)
styles.FontFamily: fontFamilySystem, // Roboto
"overflow-wrap": "break-word",
cssOverflowWrap: cssBreakWord,
}
}
@ -226,7 +234,7 @@ func h3Styles() styles.Props {
"letter-spacing": "-0.01em",
styles.Color: colorTextSecondary, // rgba(0, 0, 0, 0.54)
styles.FontFamily: fontFamilySystem, // Roboto
"overflow-wrap": "break-word",
cssOverflowWrap: cssBreakWord,
}
}
@ -242,7 +250,7 @@ func paragraphStyles() styles.Props {
styles.FontSize: fontSizeBase, // 0.8rem - inherited from .md-typeset
styles.LineHeight: lineHeightBase, // 1.6 - inherited from .md-typeset
styles.Color: colorTextPrimary, // rgba(0, 0, 0, 0.87)
"overflow-wrap": "break-word",
cssOverflowWrap: cssBreakWord,
}
}
@ -260,7 +268,7 @@ func orderedListStyles() styles.Props {
styles.FontSize: fontSizeBase, // 0.8rem - inherited from .md-typeset
styles.LineHeight: lineHeightBase, // 1.6 - inherited from .md-typeset
styles.Color: colorTextPrimary, // rgba(0, 0, 0, 0.87) - inherited from .md-typeset
"overflow-wrap": "break-word",
cssOverflowWrap: cssBreakWord,
}
}
@ -278,7 +286,7 @@ func unorderedListStyles() styles.Props {
styles.FontSize: fontSizeBase, // 0.8rem - inherited from .md-typeset
styles.LineHeight: lineHeightBase, // 1.6 - inherited from .md-typeset
styles.Color: colorTextPrimary, // rgba(0, 0, 0, 0.87) - inherited from .md-typeset
"overflow-wrap": "break-word",
cssOverflowWrap: cssBreakWord,
}
}
@ -292,7 +300,7 @@ func linkStyles() styles.Props {
return styles.Props{
styles.Color: colorPrimaryAccent, // #4051b5 - var(--md-primary-fg-color)
styles.TextDecoration: "none",
"word-break": "break-word",
"word-break": cssBreakWord,
styles.FontFamily: fontFamilySystem, // Roboto - inherited from .md-typeset
}
}
@ -310,7 +318,7 @@ func inlineCodeStyles() styles.Props {
styles.FontSize: fontSizeCode, // 0.85em
styles.FontFamily: fontFamilyCode, // Roboto Mono
styles.Padding: "0 0.2941176471em",
"word-break": "break-word",
"word-break": cssBreakWord,
}
}
@ -338,7 +346,7 @@ func orDivider() *elem.Element {
return elem.Div(attrs.Props{
attrs.Style: styles.Props{
styles.Display: "flex",
styles.AlignItems: "center",
styles.AlignItems: cssCenter,
styles.Gap: spaceM,
styles.MarginTop: space2XL,
styles.MarginBottom: space2XL,
@ -369,12 +377,12 @@ func successBox(heading string, children ...elem.Node) *elem.Element {
attrs.Props{
attrs.Style: styles.Props{
styles.Display: "flex",
styles.AlignItems: "center",
styles.AlignItems: cssCenter,
styles.Gap: spaceM,
styles.Padding: spaceL,
styles.BackgroundColor: "var(--hs-success-bg)",
styles.Border: "1px solid var(--hs-success)",
styles.BorderRadius: "0.5rem",
styles.BorderRadius: spaceS,
styles.MarginBottom: spaceXL,
}.ToInline(),
attrs.Role: "status",
@ -414,12 +422,12 @@ func errorBox(heading string, children ...elem.Node) *elem.Element {
attrs.Props{
attrs.Style: styles.Props{
styles.Display: "flex",
styles.AlignItems: "center",
styles.AlignItems: cssCenter,
styles.Gap: spaceM,
styles.Padding: spaceL,
styles.BackgroundColor: "var(--hs-error-bg)",
styles.Border: "1px solid var(--hs-error)",
styles.BorderRadius: "0.5rem",
styles.BorderRadius: spaceS,
styles.MarginBottom: spaceXL,
}.ToInline(),
attrs.Role: "alert",
@ -462,7 +470,7 @@ func warningBox(title, message string) *elem.Element {
styles.Padding: spaceL,
styles.BackgroundColor: "var(--hs-warning-bg)",
styles.Border: "1px solid var(--hs-warning-border)",
styles.BorderRadius: "0.5rem",
styles.BorderRadius: spaceS,
styles.MarginTop: spaceL,
styles.MarginBottom: spaceL,
}.ToInline(),
@ -492,7 +500,7 @@ func downloadButton(href, text string) *elem.Element {
attrs.Download: "headscale_macos.mobileconfig",
attrs.Style: styles.Props{
styles.Display: "inline-flex",
styles.AlignItems: "center",
styles.AlignItems: cssCenter,
styles.Padding: "0.75rem 1.5rem",
styles.BackgroundColor: "var(--md-primary-fg-color)",
styles.Color: "#ffffff",
@ -542,8 +550,8 @@ func detailsBox(summary string, children ...elem.Node) *elem.Element {
return elem.Details(attrs.Props{
attrs.Style: styles.Props{
styles.Background: "var(--hs-bg)",
styles.Border: "1px solid var(--hs-border)",
styles.BorderRadius: "0.5rem",
styles.Border: cssBorderHS,
styles.BorderRadius: spaceS,
styles.Padding: spaceS + " " + spaceM,
styles.MarginTop: spaceL,
styles.MarginBottom: spaceL,
@ -584,7 +592,7 @@ func statusMessage(message string, isSuccess bool) *elem.Element {
styles.Padding: spaceM,
styles.BackgroundColor: bgColor,
styles.Color: textColor,
styles.BorderRadius: "0.5rem",
styles.BorderRadius: spaceS,
styles.Border: "1px solid " + textColor,
styles.MarginBottom: spaceL,
styles.FontSize: fontSizeBase,

View file

@ -17,7 +17,7 @@ type PingResult struct {
// Status is "ok", "timeout", or "error".
Status string
// Latency is the round-trip time (only meaningful when Status is "ok").
// Latency is the round-trip time (only meaningful when [PingResult.Status] is "ok").
Latency time.Duration
// NodeID is the ID of the pinged node.
@ -27,7 +27,7 @@ type PingResult struct {
Message string
}
// ConnectedNode is a node currently connected to the batcher,
// ConnectedNode is a node currently connected to the [mapper.Batcher],
// displayed as a quick-ping link on the debug ping page.
type ConnectedNode struct {
ID types.NodeID
@ -36,7 +36,7 @@ type ConnectedNode struct {
}
// PingPage renders the /debug/ping page with a form, optional result,
// and a list of connected nodes as quick-ping links.
// and a list of connected nodes ([ConnectedNode]) as quick-ping links.
func PingPage(query string, result *PingResult, nodes []ConnectedNode) *elem.Element {
children := []elem.Node{
headscaleLogo(),
@ -94,7 +94,7 @@ func pingForm(query string) *elem.Element {
attrs.Style: styles.Props{
styles.Display: "flex",
styles.Gap: spaceS,
styles.AlignItems: "center",
styles.AlignItems: cssCenter,
styles.FlexWrap: "wrap",
styles.MarginTop: spaceM,
}.ToInline(),
@ -107,7 +107,7 @@ func pingForm(query string) *elem.Element {
attrs.Autofocus: "true",
attrs.Style: styles.Props{
styles.Padding: "0.75rem " + spaceM,
styles.Border: "1px solid var(--hs-border)",
styles.Border: cssBorderHS,
styles.BorderRadius: "0.375rem",
styles.Width: "280px",
styles.MaxWidth: "100%",

View file

@ -31,11 +31,11 @@ type RegisterConfirmInfo struct {
User string
// Hostname is the hostname the registering tailscaled instance
// reported in its RegisterRequest.
// reported in its [tailcfg.RegisterRequest].
Hostname string
// OS is the operating system the registering tailscaled reported.
// May be the empty string when the client did not send Hostinfo.
// May be the empty string when the client did not send [tailcfg.Hostinfo].
OS string
// MachineKey is the short fingerprint of the registering machine
@ -110,13 +110,13 @@ func deviceTable(rows [4][2]string) *elem.Element {
styles.FontWeight: "600",
styles.WhiteSpace: "nowrap",
styles.Color: "var(--md-default-fg-color--light)",
styles.BorderBottom: "1px solid var(--hs-border)",
styles.BorderBottom: cssBorderHS,
}.ToInline(),
}, elem.Text(row[0])),
elem.Td(attrs.Props{
attrs.Style: styles.Props{
styles.Padding: "0.5rem 0",
styles.BorderBottom: "1px solid var(--hs-border)",
styles.BorderBottom: cssBorderHS,
}.ToInline(),
}, val),
))