From 339cb392a9db846f2f42cdaed968e55a025148ec Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 20 Jun 2026 20:16:51 +0000 Subject: [PATCH] types: add Node StringID and UserView accessors Add StringID() on Node/NodeView and Username/Display/CreatedAt on UserView, so the HTTP read paths render ids and read users through view accessors instead of cloning with AsStruct(). --- hscontrol/types/node.go | 20 ++++++++++++++++++++ hscontrol/types/users.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index af29bd4b..dd3dc92d 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -92,6 +92,16 @@ func (id NodeID) String() string { return strconv.FormatUint(id.Uint64(), util.Base10) } +// StringID returns the node's id as a decimal string, the form the HTTP APIs +// render it as. +func (node *Node) StringID() string { + if node == nil { + return "" + } + + return node.ID.String() +} + func ParseNodeID(s string) (NodeID, error) { id, err := strconv.ParseUint(s, util.Base10, 64) return NodeID(id), err @@ -852,6 +862,16 @@ func (nv NodeView) RequestTagsSlice() views.Slice[string] { return nv.Hostinfo().RequestTags() } +// StringID returns the node's id as a decimal string, the form the HTTP APIs +// render it as. +func (nv NodeView) StringID() string { + if !nv.Valid() { + return "" + } + + return nv.ID().String() +} + // IsTagged reports if a device is tagged // and therefore should not be treated as a // user owned device. diff --git a/hscontrol/types/users.go b/hscontrol/types/users.go index cd55d7d7..cc194aa4 100644 --- a/hscontrol/types/users.go +++ b/hscontrol/types/users.go @@ -10,6 +10,7 @@ import ( "net/url" "strconv" "strings" + "time" "github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util/zlog/zf" @@ -133,6 +134,33 @@ func (u *User) Display() string { return cmp.Or(u.DisplayName, u.Username()) } +// Username returns the user's login name via the view; see [User.Username]. +func (v UserView) Username() string { + if !v.Valid() { + return "" + } + + return v.ж.Username() +} + +// Display returns the user's display name via the view; see [User.Display]. +func (v UserView) Display() string { + if !v.Valid() { + return "" + } + + return v.ж.Display() +} + +// CreatedAt returns when the user was created. +func (v UserView) CreatedAt() time.Time { + if !v.Valid() { + return time.Time{} + } + + return v.ж.CreatedAt +} + func (u *User) TailscaleUser() tailcfg.User { return tailcfg.User{ ID: tailcfg.UserID(u.ID), //nolint:gosec // UserID is bounded