Files

61 lines
1.7 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package api
import (
"context"
"errors"
"strings"
"time"
"unicode"
"unicode/utf8"
"github.com/google/uuid"
)
var (
ErrAccountNotFound = errors.New("account not found")
ErrAccountInactive = errors.New("account is already inactive")
ErrAccountNameConflict = errors.New("an active account already uses this name")
ErrAccountNameInvalid = errors.New("account name must be 1-128 characters without control characters")
)
// Account is an operator-defined record, not a login or authentication principal.
type Account struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
DeactivatedAt *time.Time `json:"deactivated_at,omitempty"`
Active bool `json:"active"`
}
type AccountList struct {
Items []Account `json:"items"`
}
// AccountStore is separate from public Reader/cache and ingest write interfaces.
type AccountStore interface {
CreateAccount(context.Context, string) (Account, error)
ListAccounts(context.Context) ([]Account, error)
GetAccount(context.Context, uuid.UUID) (Account, error)
DeactivateAccount(context.Context, uuid.UUID) error
}
// NormalizeAccountName applies the same validation to every account writer.
func NormalizeAccountName(name string) (string, error) {
if !utf8.ValidString(name) {
return "", ErrAccountNameInvalid
}
name = strings.TrimSpace(name)
if length := utf8.RuneCountInString(name); length < 1 || length > 128 {
return "", ErrAccountNameInvalid
}
for _, r := range name {
if unicode.IsControl(r) {
return "", ErrAccountNameInvalid
}
}
return name, nil
}