first commit
This commit is contained in:
74
internal/user/model.go
Normal file
74
internal/user/model.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SubscriptionType string
|
||||
|
||||
const (
|
||||
SubYearly SubscriptionType = "yearly"
|
||||
SubLifetime SubscriptionType = "lifetime"
|
||||
)
|
||||
|
||||
func (s SubscriptionType) Valid() bool {
|
||||
return s == SubYearly || s == SubLifetime
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64
|
||||
Pubkey string
|
||||
Username string
|
||||
SubscriptionType SubscriptionType
|
||||
ExpiresAt *time.Time
|
||||
IsActive bool
|
||||
ManualUsername bool
|
||||
LastSyncedAt *time.Time
|
||||
ExpiringReminderSentAt *time.Time
|
||||
DeactivatedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (u *User) IsLifetime() bool { return u.SubscriptionType == SubLifetime }
|
||||
|
||||
func (u *User) InGrace() bool { return !u.IsActive && u.DeactivatedAt != nil }
|
||||
|
||||
var (
|
||||
ErrInvalidUsername = errors.New("invalid username")
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrUsernameTaken = errors.New("username taken")
|
||||
)
|
||||
|
||||
// Username rules: 1-30 chars, [a-z0-9_-], lowercase, must start with alnum.
|
||||
var usernameRE = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]{0,29}$`)
|
||||
|
||||
func ValidateUsername(name string, reserved []string) error {
|
||||
name = strings.ToLower(strings.TrimSpace(name))
|
||||
if !usernameRE.MatchString(name) {
|
||||
return ErrInvalidUsername
|
||||
}
|
||||
for _, r := range reserved {
|
||||
if strings.EqualFold(name, strings.TrimSpace(r)) {
|
||||
return ErrInvalidUsername
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NormalizeUsername(name string) string {
|
||||
return strings.ToLower(strings.TrimSpace(name))
|
||||
}
|
||||
|
||||
// ProvisionalUsername returns a deterministic placeholder handle for a pubkey
|
||||
// when the caller did not supply one. The format `u_<first 16 hex>` keeps the
|
||||
// result inside the 30-char username rule and matches usernameRE.
|
||||
func ProvisionalUsername(pubkey string) string {
|
||||
hex := strings.ToLower(strings.TrimSpace(pubkey))
|
||||
if len(hex) > 16 {
|
||||
hex = hex[:16]
|
||||
}
|
||||
return "u_" + hex
|
||||
}
|
||||
Reference in New Issue
Block a user