aggregate

package
v0.0.0-...-9204231 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Index

Constants

View Source
const (
	EventUserRegistered         domainevent.EventType = "user.registered"
	EventEmailVerified          domainevent.EventType = "user.email_verified"
	EventUserLoggedIn           domainevent.EventType = "user.logged_in"
	EventProfileUpdated         domainevent.EventType = "user.profile_updated"
	EventTokenRefreshed         domainevent.EventType = "user.token_refreshed"
	EventPasswordResetRequested domainevent.EventType = "user.password_reset_requested"
	EventPasswordReset          domainevent.EventType = "user.password_reset"
	EventPasswordChanged        domainevent.EventType = "user.password_changed"
	EventTelegramLinked         domainevent.EventType = "user.telegram_linked"
	EventTelegramUnlinked       domainevent.EventType = "user.telegram_unlinked"

	// Phase B: account management & RBAC events.
	EventUserInvited            domainevent.EventType = "user.invited"
	EventUserInvitationAccepted domainevent.EventType = "user.invitation_accepted"
	EventRoleAssigned           domainevent.EventType = "user.role_assigned"
	EventRoleRevoked            domainevent.EventType = "user.role_revoked"
	EventShopCreated            domainevent.EventType = "shop.created"
)

Identity-specific event types.

View Source
const (
	MinPasswordLength     = 8
	MaxDisplayNameLen     = 100
	EmailVerificationTTL  = 24 * time.Hour
	PasswordResetTTL      = 1 * time.Hour
	VerificationTokenLen  = 32 // bytes, hex-encoded = 64 chars
	PasswordResetTokenLen = 32 // bytes, hex-encoded = 64 chars

	// TelegramSyntheticEmailDomain is the domain used for synthetic emails
	// generated for Telegram-native users. These emails are never disclosed or
	// used for login — they exist solely to satisfy the globally-unique
	// email_lower constraint.
	TelegramSyntheticEmailDomain = "telegram.local"
)
View Source
const InvitationTTL = 48 * time.Hour

InvitationTTL is how long an invitation token remains valid.

View Source
const InvitationTokenLen = 32

InvitationTokenLen is the random token byte length (hex-encoded to 2x chars).

Variables

View Source
var (
	ErrPasswordTooShort      = errors.New("password must be at least 8 characters")
	ErrPasswordTooWeak       = errors.New("password must contain uppercase, lowercase, and digit characters")
	ErrDisplayNameTooLong    = errors.New("display name exceeds maximum length")
	ErrTelegramAlreadyLinked = errors.New("telegram already linked")
	ErrTelegramNotLinked     = errors.New("telegram not linked")
)

Functions

func MapToAPIError

func MapToAPIError(err error) *apierror.Error

MapToAPIError maps identity aggregate-level errors to API error codes. Returns nil if the error is not an identity aggregate error.

func ValidatePassword

func ValidatePassword(password string) error

ValidatePassword checks that the password meets minimum complexity requirements: at least MinPasswordLength characters, contains an uppercase letter, a lowercase letter, and a digit.

Types

type EmailVerification

type EmailVerification struct {
	ID        string
	UserID    string
	Email     string
	Token     string
	ExpiresAt time.Time
	CreatedAt time.Time
}

func NewEmailVerification

func NewEmailVerification(userID, email string, now time.Time) (*EmailVerification, error)

NewEmailVerification generates a new email verification record with a cryptographically random hex token and a TTL-based expiration.

func (*EmailVerification) IsExpiredAt

func (v *EmailVerification) IsExpiredAt(now time.Time) bool

IsExpiredAt returns true if the verification token has passed its expiration relative to the given time.

type EmailVerifiedPayload

type EmailVerifiedPayload struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
}

EmailVerifiedPayload is the typed payload for EventEmailVerified.

func (EmailVerifiedPayload) EventType

type Invitation

type Invitation struct {
	ID             string
	Email          string
	Token          string
	RoleKey        string
	TenantID       *string
	CommissionRate *int
	InvitedBy      string
	ExpiresAt      time.Time
	CreatedAt      time.Time
}

Invitation is a pending account-creation grant addressed to an email. The invitee may not exist yet, so there is no UserID. role_key/tenant_id carry the binding to apply on accept; commission_rate is set only for shop_owner invites.

func NewInvitation

func NewInvitation(email, roleKey string, tenantID *string, commissionRate *int, invitedBy string, now time.Time) (*Invitation, error)

NewInvitation validates the email and mints a single-use token with a TTL.

func (*Invitation) IsExpiredAt

func (i *Invitation) IsExpiredAt(now time.Time) bool

IsExpiredAt reports whether the invitation has passed its expiry.

type PasswordChangedPayload

type PasswordChangedPayload struct {
	UserID string `json:"user_id"`
}

PasswordChangedPayload is the typed payload for EventPasswordChanged.

func (PasswordChangedPayload) EventType

type PasswordReset

type PasswordReset struct {
	ID        string
	UserID    string
	Email     string
	Token     string
	ExpiresAt time.Time
	CreatedAt time.Time
}

PasswordReset represents a token-based password reset request.

func NewPasswordReset

func NewPasswordReset(userID, email string, now time.Time) (*PasswordReset, error)

NewPasswordReset generates a new password reset record with a cryptographically random hex token and a TTL-based expiration.

func (*PasswordReset) IsExpiredAt

func (pr *PasswordReset) IsExpiredAt(now time.Time) bool

IsExpiredAt returns true if the password reset token has passed its expiration relative to the given time.

type PasswordResetPayload

type PasswordResetPayload struct {
	UserID string `json:"user_id"`
}

PasswordResetPayload is the typed payload for EventPasswordReset.

func (PasswordResetPayload) EventType

type PasswordResetRequestedPayload

type PasswordResetRequestedPayload struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
	Token  string `json:"token"`
}

PasswordResetRequestedPayload is the typed payload for EventPasswordResetRequested.

func (PasswordResetRequestedPayload) EventType

type PlatformUser

type PlatformUser struct {
	domainevent.EventRecorder

	ID                 string    `json:"id"`
	Email              string    `json:"email"`
	PasswordHash       string    `json:"-"`
	DisplayName        string    `json:"display_name"`
	EmailVerified      bool      `json:"email_verified"`
	TelegramID         *int64    `json:"telegram_id,omitempty"`
	Role               vo.Role   `json:"role"`
	TenantID           *string   `json:"tenant_id,omitempty"`
	MustChangePassword bool      `json:"must_change_password"`
	CreatedAt          time.Time `json:"created_at"`
	UpdatedAt          time.Time `json:"updated_at"`
}

PlatformUser is the aggregate root for a platform identity. It embeds EventRecorder to accumulate domain events during mutations.

func NewAdminUser

func NewAdminUser(email, password string, now time.Time) (*PlatformUser, error)

NewAdminUser builds the bootstrap administrator: role=admin with a pre-verified email (the first admin has no email-verification flow). It reuses the same email/password validation and hashing as NewPlatformUser.

func NewInvitedUser

func NewInvitedUser(email, password string, now time.Time) (*PlatformUser, error)

NewInvitedUser builds an email-verified user from an accepted invitation. The legacy role column stays RoleCustomer; the real authorization is the role binding written alongside this user. email_verified=true is intentional — the invite token was delivered to that address (see spec §6).

func NewPlatformUser

func NewPlatformUser(email, password string, now time.Time) (*PlatformUser, error)

NewPlatformUser validates inputs, hashes the password, and returns a new PlatformUser with a generated UUID and RoleCustomer.

func NewTelegramUser

func NewTelegramUser(telegramID int64, tenantID, displayName string, now time.Time) (*PlatformUser, error)

NewTelegramUser builds a Telegram-native customer of a shop: a deterministic synthetic email unique per (telegramID, tenantID), a random unusable password (the user authenticates via Telegram, never the email/password form), with TelegramID and TenantID set.

func (*PlatformUser) ChangeDisplayName

func (u *PlatformUser) ChangeDisplayName(name string, now time.Time) error

ChangeDisplayName validates and sets the user's display name.

func (*PlatformUser) ChangePassword

func (u *PlatformUser) ChangePassword(newHash string, now time.Time)

ChangePassword sets a pre-hashed password on the user. Callers are responsible for hashing the raw password before invoking this method.

func (*PlatformUser) LinkTelegram

func (u *PlatformUser) LinkTelegram(telegramID int64, now time.Time) error

LinkTelegram associates a Telegram account with the user. Returns an error if a Telegram account is already linked.

func (*PlatformUser) UnlinkTelegram

func (u *PlatformUser) UnlinkTelegram(now time.Time) error

UnlinkTelegram removes the Telegram association. Returns an error if no Telegram account is currently linked.

func (*PlatformUser) VerifyEmail

func (u *PlatformUser) VerifyEmail(now time.Time)

VerifyEmail marks the user's email as verified and updates the timestamp.

type RoleAssignedPayload

type RoleAssignedPayload struct {
	UserID    string  `json:"user_id"`
	RoleKey   string  `json:"role_key"`
	TenantID  *string `json:"tenant_id,omitempty"`
	GrantedBy string  `json:"granted_by"`
}

RoleAssignedPayload is the typed payload for EventRoleAssigned.

func (RoleAssignedPayload) EventType

type RoleRevokedPayload

type RoleRevokedPayload struct {
	UserID   string  `json:"user_id"`
	RoleKey  string  `json:"role_key"`
	TenantID *string `json:"tenant_id,omitempty"`
}

RoleRevokedPayload is the typed payload for EventRoleRevoked.

func (RoleRevokedPayload) EventType

type Session

type Session struct {
	ID           string
	UserID       string
	RefreshToken string
	IPAddress    string
	UserAgent    string
	ExpiresAt    time.Time
	CreatedAt    time.Time
}

type ShopCreatedPayload

type ShopCreatedPayload struct {
	TenantID    string  `json:"tenant_id"`
	OwnerUserID *string `json:"owner_user_id,omitempty"`
}

ShopCreatedPayload is the typed payload for EventShopCreated.

func (ShopCreatedPayload) EventType

type TelegramLinkedPayload

type TelegramLinkedPayload struct {
	UserID     string `json:"user_id"`
	TelegramID int64  `json:"telegram_id"`
}

TelegramLinkedPayload is the typed payload for EventTelegramLinked.

func (TelegramLinkedPayload) EventType

type TelegramUnlinkedPayload

type TelegramUnlinkedPayload struct {
	UserID     string `json:"user_id"`
	TelegramID int64  `json:"telegram_id"`
}

TelegramUnlinkedPayload is the typed payload for EventTelegramUnlinked.

func (TelegramUnlinkedPayload) EventType

type TokenRefreshedPayload

type TokenRefreshedPayload struct {
	UserID string `json:"user_id"`
}

TokenRefreshedPayload is the typed payload for EventTokenRefreshed.

func (TokenRefreshedPayload) EventType

type UserInvitationAcceptedPayload

type UserInvitationAcceptedPayload struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
}

UserInvitationAcceptedPayload is the typed payload for EventUserInvitationAccepted.

func (UserInvitationAcceptedPayload) EventType

type UserInvitedPayload

type UserInvitedPayload struct {
	InvitationID string  `json:"invitation_id"`
	Email        string  `json:"email"`
	RoleKey      string  `json:"role_key"`
	TenantID     *string `json:"tenant_id,omitempty"`
}

UserInvitedPayload is the typed payload for EventUserInvited.

func (UserInvitedPayload) EventType

type UserLoggedInPayload

type UserLoggedInPayload struct {
	UserID string `json:"user_id"`
}

UserLoggedInPayload is the typed payload for EventUserLoggedIn.

func (UserLoggedInPayload) EventType

type UserRegisteredPayload

type UserRegisteredPayload struct {
	UserID string `json:"user_id"`
	Email  string `json:"email"`
}

UserRegisteredPayload is the typed payload for EventUserRegistered.

func (UserRegisteredPayload) EventType

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL