import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, ManyToMany, JoinTable, OneToMany, BeforeInsert, BeforeUpdate, } from 'typeorm'; import type { Relation } from 'typeorm'; import { Exclude } from 'class-transformer'; import * as bcrypt from 'bcrypt'; import { Role } from './role.entity'; import { Session } from './session.entity'; import { PasswordReset } from './password-reset.entity'; import { TwoFactorAuth } from './two-factor-auth.entity'; export enum UserStatus { ACTIVE = 'active', INACTIVE = 'inactive', SUSPENDED = 'suspended', PENDING = 'pending', } @Entity('users') export class User { @PrimaryGeneratedColumn({ type: 'bigint', unsigned: true, name: 'user_id' }) userId: number; @Column({ unique: true, length: 100 }) email: string; @Column({ name: 'password_hash', length: 255 }) @Exclude() passwordHash: string; @Column({ name: 'first_name', length: 50 }) firstName: string; @Column({ name: 'last_name', length: 50 }) lastName: string; @Column({ length: 20, nullable: true }) phone?: string; @Column({ name: 'profile_picture_url', type: 'text', nullable: true }) profilePictureUrl?: string; @Column({ type: 'text', nullable: true }) bio?: string; @Column({ name: 'social_links', type: 'json', nullable: true }) socialLinks?: Record; @Column({ name: 'academic_interests', type: 'json', nullable: true }) academicInterests?: string[]; @Column({ name: 'skills', type: 'json', nullable: true }) skills?: string[]; @Column({ name: 'campus_id', nullable: true }) campusId?: number; @Column({ type: 'enum', enum: UserStatus, default: UserStatus.PENDING, }) status: UserStatus; @Column({ name: 'email_verified', default: false }) emailVerified: boolean; @Column({ name: 'last_login_at', type: 'datetime', nullable: true }) lastLoginAt?: Date; @CreateDateColumn({ name: 'created_at' }) createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; @DeleteDateColumn({ name: 'deleted_at', nullable: true }) deletedAt?: Date; // Relationships @ManyToMany(() => Role, (role) => role.users) @JoinTable({ name: 'user_roles', joinColumn: { name: 'user_id', referencedColumnName: 'userId' }, inverseJoinColumn: { name: 'role_id', referencedColumnName: 'roleId' }, }) roles: Relation[]; @OneToMany(() => Session, (session) => session.user) sessions: Relation[]; @OneToMany(() => PasswordReset, (passwordReset) => passwordReset.user) passwordResets: Relation[]; @OneToMany(() => TwoFactorAuth, (twoFactorAuth) => twoFactorAuth.user) twoFactorAuths: Relation[]; // Hooks for password hashing @BeforeInsert() @BeforeUpdate() async hashPassword() { if (this.passwordHash && !this.passwordHash.startsWith('$2b$')) { this.passwordHash = await bcrypt.hash(this.passwordHash, 10); } } // Method to validate password async validatePassword(password: string): Promise { return bcrypt.compare(password, this.passwordHash); } // Computed property for full name get fullName(): string { return `${this.firstName} ${this.lastName}`; } }