template-saas-backend-v2/src/modules/auth/entities/user.entity.ts
Adrian Flores Cortes a881c5cc2b [TASK-007] fix: Sync entities with DDL - add missing fields
- user.entity: Add display_name, phone_verified, is_owner, password_changed_at,
  failed_login_attempts, locked_until, preferences, last_activity_at
- role.entity: Add slug, permissions, parent_role_id, level for hierarchy
- tenant.entity: Add subscription_status, stripe_customer_id,
  stripe_subscription_id, subscription_ends_at, deleted_at

Closes DDL↔Backend coherence gaps identified in TASK-007 analysis.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 12:30:39 -06:00

110 lines
2.7 KiB
TypeScript

import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
Index,
ManyToOne,
JoinColumn,
} from 'typeorm';
@Entity({ schema: 'users', name: 'users' })
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ type: 'uuid' })
@Index()
tenant_id: string;
@Column({ type: 'varchar', length: 255 })
@Index()
email: string;
@Column({ type: 'varchar', length: 255 })
password_hash: string;
@Column({ type: 'varchar', length: 100, nullable: true })
first_name: string | null;
@Column({ type: 'varchar', length: 100, nullable: true })
last_name: string | null;
@Column({ type: 'varchar', length: 200, nullable: true })
display_name: string | null;
@Column({ type: 'varchar', length: 255, nullable: true })
avatar_url: string | null;
@Column({ type: 'varchar', length: 20, nullable: true })
phone: string | null;
@Column({ type: 'boolean', default: false })
phone_verified: boolean;
@Column({
type: 'enum',
enum: ['active', 'inactive', 'suspended', 'pending_verification'],
enumName: 'users.user_status',
default: 'pending_verification',
})
status: string;
@Column({ type: 'boolean', default: false })
is_owner: boolean;
@Column({ type: 'boolean', default: false })
email_verified: boolean;
@Column({ type: 'timestamp with time zone', nullable: true })
email_verified_at: Date | null;
@Column({ type: 'boolean', default: false })
mfa_enabled: boolean;
@Column({ type: 'varchar', length: 255, nullable: true })
mfa_secret: string | null;
@Column({ type: 'text', array: true, nullable: true })
mfa_backup_codes: string[] | null;
@Column({ type: 'timestamp with time zone', nullable: true })
mfa_enabled_at: Date | null;
@Column({ type: 'timestamp with time zone', nullable: true })
password_changed_at: Date | null;
@Column({ type: 'int', default: 0 })
failed_login_attempts: number;
@Column({ type: 'timestamp with time zone', nullable: true })
locked_until: Date | null;
@Column({ type: 'timestamp with time zone', nullable: true })
last_login_at: Date | null;
@Column({ type: 'varchar', length: 45, nullable: true })
last_login_ip: string | null;
@Column({ type: 'jsonb', nullable: true })
metadata: Record<string, any> | null;
@Column({ type: 'jsonb', nullable: true })
preferences: Record<string, any> | null;
@Column({ type: 'timestamp with time zone', nullable: true })
last_activity_at: Date | null;
@CreateDateColumn({ type: 'timestamp with time zone' })
created_at: Date;
@UpdateDateColumn({ type: 'timestamp with time zone' })
updated_at: Date;
// Computed property
get fullName(): string {
return [this.first_name, this.last_name].filter(Boolean).join(' ');
}
}