feat: backend service
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/mail"
|
||||
"strings"
|
||||
|
||||
"galaxy/backend/internal/auth"
|
||||
)
|
||||
|
||||
// ed25519PublicKeyLen is the fixed size of a raw Ed25519 public key. The
|
||||
// OpenAPI contract documents `client_public_key` as a "Standard
|
||||
// base64-encoded raw 32-byte Ed25519 public key"; the handler enforces
|
||||
// the length after decode so the auth service always operates on the
|
||||
// canonical shape.
|
||||
const ed25519PublicKeyLen = 32
|
||||
|
||||
// deviceSessionPayload is the JSON body the internal session endpoints
|
||||
// emit. It mirrors the `DeviceSession` schema in `openapi.yaml`.
|
||||
type deviceSessionPayload struct {
|
||||
DeviceSessionID string `json:"device_session_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Status string `json:"status"`
|
||||
ClientPublicKey string `json:"client_public_key,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
RevokedAt *string `json:"revoked_at,omitempty"`
|
||||
LastSeenAt *string `json:"last_seen_at,omitempty"`
|
||||
}
|
||||
|
||||
func deviceSessionToWire(s auth.Session) deviceSessionPayload {
|
||||
out := deviceSessionPayload{
|
||||
DeviceSessionID: s.DeviceSessionID.String(),
|
||||
UserID: s.UserID.String(),
|
||||
Status: s.Status,
|
||||
CreatedAt: s.CreatedAt.UTC().Format("2006-01-02T15:04:05.000Z07:00"),
|
||||
}
|
||||
if len(s.ClientPublicKey) > 0 {
|
||||
out.ClientPublicKey = base64.StdEncoding.EncodeToString(s.ClientPublicKey)
|
||||
}
|
||||
if s.RevokedAt != nil {
|
||||
formatted := s.RevokedAt.UTC().Format("2006-01-02T15:04:05.000Z07:00")
|
||||
out.RevokedAt = &formatted
|
||||
}
|
||||
if s.LastSeenAt != nil {
|
||||
formatted := s.LastSeenAt.UTC().Format("2006-01-02T15:04:05.000Z07:00")
|
||||
out.LastSeenAt = &formatted
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// validateEmail returns the trimmed value when raw parses as an
|
||||
// addr-spec, or "" when raw is malformed. Auth normalises to lowercase
|
||||
// internally; the handler only enforces the syntactic shape.
|
||||
func validateEmail(raw string) string {
|
||||
addr, err := mail.ParseAddress(strings.TrimSpace(raw))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return addr.Address
|
||||
}
|
||||
|
||||
// decodeClientPublicKey decodes the wire-format client_public_key into
|
||||
// raw bytes and validates the length. Standard base64 (with padding) is
|
||||
// the canonical encoding documented in `openapi.yaml`.
|
||||
func decodeClientPublicKey(raw string) ([]byte, bool) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(strings.TrimSpace(raw))
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if len(decoded) != ed25519PublicKeyLen {
|
||||
return nil, false
|
||||
}
|
||||
return decoded, true
|
||||
}
|
||||
|
||||
// isDecimalCodeOfLength reports whether s is a string of exactly want
|
||||
// ASCII digits.
|
||||
func isDecimalCodeOfLength(s string, want int) bool {
|
||||
if len(s) != want {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] < '0' || s[i] > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user