monorepo/cloud/maplepress-backend/internal/domain/tenant/entity.go

75 lines
2 KiB
Go

package tenant
import (
"errors"
"regexp"
"time"
)
var (
ErrNameRequired = errors.New("tenant name is required")
ErrNameTooShort = errors.New("tenant name must be at least 2 characters")
ErrNameTooLong = errors.New("tenant name must not exceed 100 characters")
ErrSlugRequired = errors.New("tenant slug is required")
ErrSlugInvalid = errors.New("tenant slug must contain only lowercase letters, numbers, and hyphens")
ErrTenantNotFound = errors.New("tenant not found")
ErrTenantExists = errors.New("tenant already exists")
ErrTenantInactive = errors.New("tenant is inactive")
)
// Status represents the tenant's current status
type Status string
const (
StatusActive Status = "active"
StatusInactive Status = "inactive"
StatusSuspended Status = "suspended"
)
// Tenant represents a tenant in the system
// Each tenant is a separate customer/organization
type Tenant struct {
ID string
Name string // Display name (e.g., "Acme Corporation")
Slug string // URL-friendly identifier (e.g., "acme-corp")
Status Status
CreatedAt time.Time
UpdatedAt time.Time
// CWE-359: IP address tracking for GDPR compliance (90-day expiration)
CreatedFromIPAddress string // Encrypted IP address
CreatedFromIPTimestamp time.Time // For 90-day expiration tracking
ModifiedFromIPAddress string // Encrypted IP address
ModifiedFromIPTimestamp time.Time // For 90-day expiration tracking
}
var slugRegex = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`)
// Validate validates the tenant entity
func (t *Tenant) Validate() error {
// Name validation
if t.Name == "" {
return ErrNameRequired
}
if len(t.Name) < 2 {
return ErrNameTooShort
}
if len(t.Name) > 100 {
return ErrNameTooLong
}
// Slug validation
if t.Slug == "" {
return ErrSlugRequired
}
if !slugRegex.MatchString(t.Slug) {
return ErrSlugInvalid
}
return nil
}
// IsActive returns true if the tenant is active
func (t *Tenant) IsActive() bool {
return t.Status == StatusActive
}