package user import ( "errors" "strings" "time" ) var ErrUserNotFound = errors.New("user not found") var ErrInvalidCredentials = errors.New("invalid credentials") var ErrRegistrationClosed = errors.New("registration closed") var ErrInvalidVerificationCode = errors.New("invalid verification code") var ErrVerificationCodeExpired = errors.New("verification code expired") var ErrAlreadyVerified = errors.New("user already verified") var ErrInvalidPasswordResetCode = errors.New("invalid password reset code") var ErrPasswordResetCodeExpired = errors.New("password reset code expired") var ErrInvalidDisplayName = errors.New("invalid display name") var ErrUsernameTaken = errors.New("username taken") var ErrInvalidLookup = errors.New("invalid user lookup") var ErrVerificationEmailNotSent = errors.New("verification email not sent") var ErrPasswordTooLong = errors.New("password too long") var ErrRoleChanged = errors.New("user role changed concurrently") var ErrInvalidTransfer = errors.New("invalid ownership transfer") var ErrConfirmationMismatch = errors.New("confirmation email does not match") const MaxLookupIDs = 100 const MaxPasswordBytes = 72 func ValidatePassword(password string) error { if len(password) > MaxPasswordBytes { return ErrPasswordTooLong } return nil } type User struct { ID uint32 `json:"id"` Username string `json:"username"` Password string `json:"password"` PasswordResetCode string `json:"password_reset_code"` Email string `json:"email"` DisplayName string `json:"display_name"` IsVerified bool `json:"is_verified"` RoleID uint32 `json:"role_id"` VerificationCode string `json:"verification_code"` VerificationExpiration time.Time `json:"verification_expiration"` PasswordResetExpiration time.Time `json:"password_reset_expiration"` TokenVersion uint32 `json:"token_version"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } const UnverifiedRetention = 7 * 24 * time.Hour func (u User) IsStaleUnverifiedAt(now time.Time) bool { if u.IsVerified { return false } deadline := u.VerificationExpiration if deadline.IsZero() { deadline = u.CreatedAt } if deadline.IsZero() { return false } return now.After(deadline.Add(UnverifiedRetention)) } func (u User) NameOrUsername() string { if trimmed := strings.TrimSpace(u.DisplayName); trimmed != "" { return trimmed } return u.Username }