package models import ( "time" "github.com/google/uuid" ) // User represents a user account in the system. // This is the core user entity containing authentication and profile information. // // What is a user? // - Account with unique email // - Associated with a tenant (multi-tenancy) // - Has roles for authorization // - Contains profile information // - Tracks authentication state // // User lifecycle: // 1. Created: During registration // 2. Pending: Email verification required (optional) // 3. Active: Can log in and use system // 4. Suspended: Temporarily disabled // 5. Deleted: Soft deleted (deleted_at set) // // Multi-tenancy: // - Each user belongs to one tenant (organization) // - Tenant ID used for data isolation // - Cross-tenant access not allowed // - Important for SaaS applications // // Security considerations: // - Password never exposed (json:"-" tag) // - Soft delete preserves data integrity // - Email is unique identifier // - Role-based access control type User struct { // ID is the unique identifier for this user // Type: UUID v4 (128-bit random identifier) // Generated: Automatically by database on insert // Primary key: Used for all lookups and relationships ID uuid.UUID `json:"id" bson:"id"` // TenantID identifies which organization this user belongs to // Type: UUID v4 (foreign key to tenants table) // Multi-tenancy: Isolates data between organizations // Required: Every user must belong to a tenant // Used for: Filtering queries, enforcing data isolation TenantID uuid.UUID `json:"tenant_id" bson:"tenant_id"` // Email is the user's email address (unique identifier for login) // Type: String (validated format, max 254 chars) // Unique: Within system (can't have duplicate accounts) // Normalized: Stored as lowercase for consistent matching // Used for: Login, communication, uniqueness // Privacy: PII, must be protected Email string `json:"email" db:"email"` // PasswordHash is the bcrypt hash of the user's password // Type: Optional string (can be null for social login) // Format: "$2a$10$..." (bcrypt hash with salt) // Security: NEVER expose in API responses (json:"-" means omit) // Hashing: Bcrypt with cost factor 10 // Why optional: Users with social login may not have password PasswordHash *string `json:"_" db:"password_hash"` // FirstName is the user's first name // Type: Optional string (can be null) // Used for: Personalization, display // Privacy: PII, must be protected FirstName *string `json:"first_name" db:"first_name"` // LastName is the user's last name // Type: Optional string (can be null) // Used for: Personalization, display // Privacy: PII, must be protected LastName *string `json:"last_name" db:"last_name"` // FullName is the computed full name (first + last) // Type: String (computed by database trigger or application) // Generated: From first_name and last_name // Used for: Display purposes, searching // Database: Might be a generated column or manually updated FullName string `json:"full_name" db:"full_name"` // AvatarURL is the URL to the user's profile picture // Type: Optional string (can be null) // Storage: URL points to file in object storage (MinIO/S3) // Default: System can provide default avatar if null // Privacy: Publicly accessible or requires auth AvatarURL *string `json:"avatar_url" db:"avatar_url"` // Phone is the user's phone number // Type: Optional string (can be null) // Format: Should be E.164 format (+1234567890) // Used for: 2FA, notifications, contact // Privacy: PII, must be protected // Verification: Should require phone verification Phone *string `json:"phone" db:"phone"` // Role defines the user's permissions level // Type: String (enum-like values) // Common values: "admin", "user", "manager", "viewer" // Used for: Authorization checks, feature access // Default: Usually "user" for new accounts // Important: Always check role before allowing operations Role string `json:"role" db:"role"` // Status indicates the current state of the account // Type: String (enum-like values) // Possible values: "active", "pending", "suspended", "deleted" // Active: Can log in normally // Pending: Awaiting email verification // Suspended: Temporarily disabled (can't log in) // Deleted: Soft deleted (should have deleted_at set) Status string `json:"status" db:"status"` // EmailVerified indicates if email has been verified // Type: Boolean (default false) // Purpose: Confirm user owns the email address // Workflow: User clicks link in verification email // Requirement: Some systems require verification before full access EmailVerified bool `json:"email_verified" db:"email_verified"` // EmailVerifiedAt is when the email was verified // Type: Optional timestamp (null if not verified) // Set when: User clicks verification link // Used for: Audit trail, resend logic EmailVerifiedAt *time.Time `json:"email_verified_at" db:"email_verified_at"` // IsOnboarded indicates if user completed onboarding // Type: Boolean (default false) // Purpose: Track if user saw welcome/tutorial // Used for: Showing onboarding flow // Set to true: After user completes onboarding steps IsOnboarded bool `json:"is_onboarded" db:"is_onboarded"` // LastLoginAt is when the user last logged in // Type: Optional timestamp (null if never logged in) // Updated: After successful authentication // Used for: Security monitoring, activity tracking // Display: "Last login: 2 hours ago" LastLoginAt *time.Time `json:"last_login_at" db:"last_login_at"` // LastLoginIP is the IP address of last login // Type: Optional string (null if never logged in) // Format: IPv4 or IPv6 // Used for: Security monitoring, anomaly detection // Privacy: PII under GDPR, may need anonymization LastLoginIP *string `json:"last_login_ip" db:"last_login_ip"` // CreatedAt is when the user account was created // Type: Timestamp (automatically set by database) // Used for: Account age, analytics, sorting // Database: Set by DEFAULT NOW() in schema CreatedAt time.Time `json:"created_at" db:"created_at"` // UpdatedAt is when the user record was last modified // Type: Timestamp (automatically updated) // Updated: Any time user record changes // Used for: Audit trail, change tracking // Database: Updated by trigger or application UpdatedAt time.Time `json:"updated_at" db:"updated_at"` // DeletedAt is when the user was soft deleted // Type: Optional timestamp (null if not deleted) // Soft delete: Record preserved but marked as deleted // Used for: Data integrity, audit trail // Omitted from JSON if null (json:"deleted_at,omitempty") // Queries: Filter WHERE deleted_at IS NULL DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"` } // CreateUserInput contains the data needed to create a new user account. // This is a DTO (Data Transfer Object) for the registration/user creation flow. // // Why separate input struct? // - Separates client input from database-generated fields // - Clear interface for what's required vs what's generated // - Type safety (can't set ID, timestamps, etc.) // - Validation happens on this struct // // When used: // - User registration // - Admin creating user // - Invitation flow type CreateUserInput struct { // TenantID is which organization the user belongs to // Required: Every user must belong to a tenant // Set by: System (based on signup domain, invitation, etc.) TenantID uuid.UUID // Email is the user's email address // Required: Used for login and communication // Validation: Must be valid email format, must be unique // Normalized: Will be lowercased before storage Email string // Password is the plaintext password // Required: Must meet strength requirements // Security: Will be hashed with bcrypt before storage // Validation: Checked for length, complexity, common patterns // NEVER logged or stored plaintext Password string // FirstName is the user's first name // Optional: Can be null (but recommended) // Used for: Personalization, display FirstName *string // LastName is the user's last name // Optional: Can be null (but recommended) // Used for: Personalization, display LastName *string // Role is the user's permission level // Required: Must be set (often defaults to "user") // Values: "admin", "user", "manager", etc. // Set by: System (based on registration type) or admin Role string // Status is the initial account status // Required: Usually "pending" or "active" // "pending": Requires email verification // "active": Can log in immediately // Set by: System based on email verification policy Status string } // UserResponse is a sanitized user object for API responses. // This DTO removes sensitive fields before sending to client. // // Why separate response struct? // - Security: Never expose password_hash or internal IDs // - API contract: Clear definition of what clients receive // - Flexibility: Can add computed fields without changing User model // - Versioning: Can have different responses for API versions // // What's excluded from User: // - PasswordHash: NEVER expose password hashes // - DeletedAt: Internal field, not relevant to client // - Internal IDs: Some may be excluded depending on use case // // When used: // - Login response // - Profile endpoint // - User list endpoints // - Any API response containing user data type UserResponse struct { // ID is the user's unique identifier ID uuid.UUID `json:"id"` // TenantID for multi-tenancy TenantID uuid.UUID `json:"tenant_id"` // Email for display and communication Email string `json:"email"` // FirstName for personalization // Note: JSON tag shows "name" but field is FirstName (might be typo) FirstName *string `json:"name"` // LastName for full name display LastName *string `json:"last_name"` // FullName computed from first + last FullName string `json:"full_name"` // AvatarURL for profile picture AvatarURL *string `json:"avatar_url"` // Phone for contact Phone *string `json:"phone"` // Role for client-side permission checks Role string `json:"role"` // Status to show account state Status string `json:"status"` // EmailVerified to prompt verification if needed EmailVerified bool `json:"email_verified"` // IsOnboarded to show onboarding if needed IsOnboarded bool `json:"is_onboarded"` // LastLoginAt for security awareness LastLoginAt *time.Time `json:"last_login_at"` // CreatedAt for account age CreatedAt time.Time `json:"created_at"` // Note: Excludes PasswordHash, DeletedAt, UpdatedAt, LastLoginIP } // ToResponse converts a User model to a UserResponse DTO. // This method sanitizes the user object before sending to client. // // Why this method? // - Encapsulation: Conversion logic lives with the model // - Reusability: Can be called anywhere needed // - Type safety: Returns correct response type // - Maintainability: One place to update response structure // // Usage: // // user := getUserFromDB() // response := user.ToResponse() // return c.JSON(http.StatusOK, response) // // What it does: // - Copies public fields from User to UserResponse // - Excludes sensitive fields (password_hash) // - Excludes internal fields (deleted_at, updated_at, last_login_ip) // // Returns: // - UserResponse with safe fields populated func (u *User) ToResponse() *UserResponse { return &UserResponse{ ID: u.ID, TenantID: u.TenantID, Email: u.Email, FirstName: u.FirstName, LastName: u.LastName, FullName: u.FullName, AvatarURL: u.AvatarURL, Phone: u.Phone, Role: u.Role, Status: u.Status, EmailVerified: u.EmailVerified, IsOnboarded: u.IsOnboarded, LastLoginAt: u.LastLoginAt, CreatedAt: u.CreatedAt, // Intentionally excluded: // - PasswordHash (security) // - LastLoginIP (privacy) // - DeletedAt (internal) // - UpdatedAt (internal) // - EmailVerifiedAt (redundant with EmailVerified boolean) } } // Note about database migration: // The comment at the end of the original file mentions: // "Current DB structure need to updated the migration script to reflect the current UserEntity" // // This suggests the database schema may be out of sync with this model. // The old structure mentioned was: // id | tenant_id | email | password_hash | name | avatar_url | role | is_active | // email_verified_at | last_login_at | created_at | updated_at | deleted_at // // Differences from current model: // 1. "name" field vs "first_name" + "last_name" + "full_name" // 2. "is_active" field vs "status" field (enum) // 3. Missing "phone" field // 4. Missing "email_verified" boolean // 5. Missing "is_onboarded" boolean // 6. Missing "last_login_ip" field // // Action required: // - Create database migration to update schema // - Or update model to match current database (then migrate later) // - Ensure model and database stay in sync