[ST-P0-001] feat: Add User entity and fix auth relationships
- Create user.entity.ts with all fields from erp-core - Update index.ts to export User and UserStatus - Add bidirectional ManyToMany relationship in role.entity.ts - Add bidirectional ManyToMany relationship in company.entity.ts - Fix session.entity.ts import to use local user.entity - Fix password-reset.entity.ts import to use local user.entity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
99fadef0ba
commit
06c48678b1
@ -14,9 +14,11 @@ import {
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
ManyToMany,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { Tenant } from '../../core/entities/tenant.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'companies' })
|
||||
@Index('idx_companies_tenant_id', ['tenantId'])
|
||||
@ -59,6 +61,9 @@ export class Company {
|
||||
@JoinColumn({ name: 'parent_company_id' })
|
||||
parentCompany: Company | null;
|
||||
|
||||
@ManyToMany(() => User, (user) => user.companies)
|
||||
users: User[];
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamp' })
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ export { RefreshToken } from './refresh-token.entity';
|
||||
export { Role } from './role.entity';
|
||||
export { Permission } from './permission.entity';
|
||||
export { UserRole } from './user-role.entity';
|
||||
export { User, UserStatus } from './user.entity';
|
||||
export { Session, SessionStatus } from './session.entity';
|
||||
export { ApiKey } from './api-key.entity';
|
||||
export { PasswordReset } from './password-reset.entity';
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from '../../core/entities/user.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'password_resets' })
|
||||
@Index('idx_password_resets_user_id', ['userId'])
|
||||
@ -40,7 +40,7 @@ export class PasswordReset {
|
||||
@Column({ type: 'inet', nullable: true, name: 'ip_address' })
|
||||
ipAddress: string | null;
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(() => User, (user) => user.passwordResets, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
import { Permission } from './permission.entity';
|
||||
import { UserRole } from './user-role.entity';
|
||||
import { Tenant } from '../../core/entities/tenant.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity({ schema: 'auth', name: 'roles' })
|
||||
@Index('idx_roles_tenant_id', ['tenantId'])
|
||||
@ -69,6 +70,9 @@ export class Role {
|
||||
@OneToMany(() => UserRole, (userRole) => userRole.role)
|
||||
userRoles: UserRole[];
|
||||
|
||||
@ManyToMany(() => User, (user) => user.roles)
|
||||
users: User[];
|
||||
|
||||
// Audit trail
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from '../../core/entities/user.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
export enum SessionStatus {
|
||||
ACTIVE = 'active',
|
||||
@ -70,7 +70,7 @@ export class Session {
|
||||
@Column({ type: 'jsonb', nullable: true, name: 'device_info' })
|
||||
deviceInfo: Record<string, any> | null;
|
||||
|
||||
@ManyToOne(() => User, { onDelete: 'CASCADE' })
|
||||
@ManyToOne(() => User, (user) => user.sessions, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user: User;
|
||||
|
||||
|
||||
167
src/modules/auth/entities/user.entity.ts
Normal file
167
src/modules/auth/entities/user.entity.ts
Normal file
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* User Entity
|
||||
* Usuarios del sistema con autenticacion y permisos
|
||||
* Compatible con erp-core user.entity
|
||||
*
|
||||
* @module Auth
|
||||
*/
|
||||
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
ManyToMany,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { Tenant } from '../../core/entities/tenant.entity';
|
||||
import { Role } from './role.entity';
|
||||
import { Company } from './company.entity';
|
||||
import { Session } from './session.entity';
|
||||
import { PasswordReset } from './password-reset.entity';
|
||||
|
||||
export enum UserStatus {
|
||||
ACTIVE = 'active',
|
||||
INACTIVE = 'inactive',
|
||||
SUSPENDED = 'suspended',
|
||||
PENDING_VERIFICATION = 'pending_verification',
|
||||
}
|
||||
|
||||
@Entity({ schema: 'auth', name: 'users' })
|
||||
@Index('idx_users_tenant_id', ['tenantId'])
|
||||
@Index('idx_users_email', ['email'])
|
||||
@Index('idx_users_status', ['status'], { where: 'deleted_at IS NULL' })
|
||||
@Index('idx_users_email_tenant', ['tenantId', 'email'])
|
||||
@Index('idx_users_created_at', ['createdAt'])
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'uuid', nullable: false, name: 'tenant_id' })
|
||||
tenantId: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false })
|
||||
email: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false, name: 'password_hash' })
|
||||
passwordHash: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: false, name: 'full_name' })
|
||||
fullName: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 500, nullable: true, name: 'avatar_url' })
|
||||
avatarUrl: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: UserStatus,
|
||||
default: UserStatus.ACTIVE,
|
||||
nullable: false,
|
||||
})
|
||||
status: UserStatus;
|
||||
|
||||
@Column({ type: 'boolean', default: false, nullable: false, name: 'is_superuser' })
|
||||
isSuperuser: boolean;
|
||||
|
||||
@Column({ name: 'is_superadmin', default: false })
|
||||
isSuperadmin: boolean;
|
||||
|
||||
@Column({ name: 'mfa_enabled', default: false })
|
||||
mfaEnabled: boolean;
|
||||
|
||||
@Column({ name: 'mfa_secret_encrypted', type: 'text', nullable: true })
|
||||
mfaSecretEncrypted: string;
|
||||
|
||||
@Column({ name: 'mfa_backup_codes', type: 'text', array: true, nullable: true })
|
||||
mfaBackupCodes: string[];
|
||||
|
||||
@Column({ name: 'oauth_provider', length: 50, nullable: true })
|
||||
oauthProvider: string;
|
||||
|
||||
@Column({ name: 'oauth_provider_id', length: 255, nullable: true })
|
||||
oauthProviderId: string;
|
||||
|
||||
@Column({
|
||||
type: 'timestamp',
|
||||
nullable: true,
|
||||
name: 'email_verified_at',
|
||||
})
|
||||
emailVerifiedAt: Date | null;
|
||||
|
||||
@Column({ type: 'timestamp', nullable: true, name: 'last_login_at' })
|
||||
lastLoginAt: Date | null;
|
||||
|
||||
@Column({ type: 'inet', nullable: true, name: 'last_login_ip' })
|
||||
lastLoginIp: string | null;
|
||||
|
||||
@Column({ type: 'integer', default: 0, name: 'login_count' })
|
||||
loginCount: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 10, default: 'es' })
|
||||
language: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50, default: 'America/Mexico_City' })
|
||||
timezone: string;
|
||||
|
||||
@Column({ type: 'jsonb', default: {} })
|
||||
settings: Record<string, any>;
|
||||
|
||||
// Relaciones
|
||||
@ManyToOne(() => Tenant, (tenant) => tenant.users, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'tenant_id' })
|
||||
tenant: Tenant;
|
||||
|
||||
@ManyToMany(() => Role, (role) => role.users)
|
||||
@JoinTable({
|
||||
name: 'user_roles',
|
||||
schema: 'auth',
|
||||
joinColumn: { name: 'user_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' },
|
||||
})
|
||||
roles: Role[];
|
||||
|
||||
@ManyToMany(() => Company, (company) => company.users)
|
||||
@JoinTable({
|
||||
name: 'user_companies',
|
||||
schema: 'auth',
|
||||
joinColumn: { name: 'user_id', referencedColumnName: 'id' },
|
||||
inverseJoinColumn: { name: 'company_id', referencedColumnName: 'id' },
|
||||
})
|
||||
companies: Company[];
|
||||
|
||||
@OneToMany(() => Session, (session) => session.user)
|
||||
sessions: Session[];
|
||||
|
||||
@OneToMany(() => PasswordReset, (passwordReset) => passwordReset.user)
|
||||
passwordResets: PasswordReset[];
|
||||
|
||||
// Auditoria
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamp' })
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'created_by' })
|
||||
createdBy: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
name: 'updated_at',
|
||||
type: 'timestamp',
|
||||
nullable: true,
|
||||
})
|
||||
updatedAt: Date | null;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'updated_by' })
|
||||
updatedBy: string | null;
|
||||
|
||||
@Column({ type: 'timestamp', nullable: true, name: 'deleted_at' })
|
||||
deletedAt: Date | null;
|
||||
|
||||
@Column({ type: 'uuid', nullable: true, name: 'deleted_by' })
|
||||
deletedBy: string | null;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user