## Entities (DDL↔Backend sync): - user.entity: 8 missing fields added - role.entity: 4 missing fields added - tenant.entity: 5 missing fields added ## Documentation: - PROXIMA-ACCION.md: Rewritten with 2026-01-27 status - PROJECT-STATUS.md: Added MLM, Goals, Portfolio phases - docs/01-modulos/_INDEX.md: Updated module states to Completado ## Inventories: - DATABASE_INVENTORY.yml: Added mlm/goals schemas (10 tables) - BACKEND_INVENTORY.yml: Added mlm/goals modules (10 entities) - MASTER_INVENTORY.yml: MLM/Goals marked as completado - tareas/_INDEX.yml: Registered TASK-007, SAAS-021, SAAS-022 Metrics: 23 modules, 17 schemas, 48 tables, 260 SP (100%) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
110 lines
2.7 KiB
TypeScript
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(' ');
|
|
}
|
|
}
|