mirror of
https://github.com/juanfont/headscale.git
synced 2026-07-16 04:50:45 +09:00
fe08d43978
Add the v2 foundation — Basic/Bearer auth, scope scaffolding, the tailnet check, and the Tailscale error shape — plus the auth-key endpoints mapped onto Headscale pre-auth keys.
70 lines
1.7 KiB
Go
70 lines
1.7 KiB
Go
package apiv2
|
|
|
|
import (
|
|
"math"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/danielgtaylor/huma/v2"
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
|
"github.com/juanfont/headscale/hscontrol/util"
|
|
)
|
|
|
|
// emptyIfNil ensures a slice serializes as [] rather than null, which the
|
|
// Tailscale clients expect for empty list fields.
|
|
func emptyIfNil(s []string) []string {
|
|
if s == nil {
|
|
return []string{}
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// parseID parses a decimal entity id from a path segment. A non-numeric id is
|
|
// simply an unknown entity, surfaced as a 404 naming subject (e.g. "auth key"),
|
|
// so the Tailscale SDK's IsNotFound behaves.
|
|
func parseID(rawID, subject string) (uint64, error) {
|
|
id, err := strconv.ParseUint(rawID, util.Base10, util.BitSize64)
|
|
if err != nil {
|
|
return 0, huma.Error404NotFound(subject + " not found")
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
// parseNodeID parses a device id path segment into a [types.NodeID].
|
|
func parseNodeID(rawID string) (types.NodeID, error) {
|
|
id, err := parseID(rawID, "device")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return types.NodeID(id), nil
|
|
}
|
|
|
|
// timeOrZero dereferences a time pointer, returning the zero time for nil.
|
|
func timeOrZero(t *time.Time) time.Time {
|
|
if t == nil {
|
|
return time.Time{}
|
|
}
|
|
|
|
return *t
|
|
}
|
|
|
|
// expirySeconds reports the lifetime between created and expires in whole
|
|
// seconds, the unit the Tailscale spec documents for the response field. The
|
|
// lifetime is rounded because the stored expiration is stamped a hair before
|
|
// CreatedAt, so an 86400s request would otherwise read back as 86399.
|
|
func expirySeconds(created, expires *time.Time) int64 {
|
|
if created == nil || expires == nil {
|
|
return 0
|
|
}
|
|
|
|
secs := int64(math.Round(expires.Sub(*created).Seconds()))
|
|
if secs < 0 {
|
|
return 0
|
|
}
|
|
|
|
return secs
|
|
}
|