# Users

### Users module path

<pre><code>├── node_modules
<strong>├── mail-templates
</strong>├── migrations
├── src
│   ├── configs
│   ├── domains
│   │   ├── users             # Users module
</code></pre>

### User entity

```typescript
@Entity()
export class User extends BaseEntity {
  // before insert and update hooks
  @BeforeInsert()
  @BeforeUpdate()
  async hashPassword() {
    if (!!this.password) {
      if (!this.password.includes('$2b$10')) {
        this.password = bcrypt.hashSync(this.password, 10);
      }
    }
  }

  @BeforeInsert()
  @BeforeUpdate()
  lowerCaseEmail() {
    this.email = this.email.toLowerCase();
  }

  @PrimaryGeneratedColumn()
  id: number;

  @Column({ default: false })
  isAdmin: boolean;

  @Column({ nullable: true })
  firstName: string;

  @Column({ nullable: true })
  lastName: string;

  @Index({ unique: true })
  @Column()
  email: string;

  @Column({ nullable: true })
  password: string;

  @Column({ default: null })
  resetPasswordToken: string;

  @Column({ default: false })
  confirmPayment: boolean;

  @Column({ default: false })
  errorPayment: boolean;

  @Column({ nullable: true })
  customerStripeId: string;

  @Column({ nullable: true })
  @IsOptional()
  endSubscription: Date;

  @Column({ default: false })
  canceledSubscription: boolean;

  @Column({ nullable: true})
  magicToken: string;

  @Column({ nullable: true })
  planId: number;

  @ManyToOne(() => Plan, plan => plan.users)
  @JoinColumn()
  plan: Plan;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}


export function password(length): string {
    let pass = '';
    for (let l=0; l < length; l++) {
        const rand = Math.random() * (126 - 33) + 33;
        pass += String.fromCharCode(~~rand);
    }
    return pass;
}
```

### Users routes

```typescript
@Controller('users')
export class UsersController implements CrudController<User> {
  private readonly logger = new Logger(UsersController.name);

  constructor(
    public service: UsersService,
    private readonly shared: SharedService,
    private readonly authService: AuthService,
  ) {}

  @Delete('/:id')
  @ApiOperation({
    operationId: 'Deactivate user',
    description: 'Deactivate an existing user',
  })
  @UseGuards(AdminGuard)
  async softDelete(@Param('id') id) {
    await this.service.softDelete(id);
    return null;
  }

  @Get('list')
  @UseGuards(AdminGuard)
  async getList(@Query('page') page: number, @Query('limit') limit: number, @Query('nameOrEmail') nameOrEmail: string = null) {
     return await this.service.getUsers(page, limit, nameOrEmail);
  }

  @Get('/me')
  @ApiOperation({
    operationId: 'Get current user',
    description: 'Get user information for logged user',
  })
  @ApiOkResponse({ type: UserResponse })
  @UseGuards(ConnectedUserGuard)
  async getCurrentUser(
    @CurrentUser() userPayload: TokenPayload,
  ): Promise<UserResponse> {
    const user = await this.service.findOne({ where: { id: userPayload.id }});
    delete user.password;
    delete user.resetPasswordToken;
    return user;
  }

  @Get('stats') 
  @UseGuards(AdminGuard)
  async getStats(@Query('from') from: Date, @Query('to') to: Date) {
      return await this.service.getStats(from, to);
  }

  @Put('/me')
  @ApiOperation({
    operationId: 'Update current user',
    description: 'Update user information for logged user',
  })
  @ApiOkResponse({ type: UserResponse })
  @UseGuards(ConnectedUserGuard)
  async updateCurrentUser(
    @Body() updatedUserInfo: UserUpdateRequest,
    @CurrentUser() userPayload: TokenPayload
  ) {
    const user = await this.service.findOne({ where: {id: userPayload.id }});
    let paymentUrl = null;

    if (typeof updatedUserInfo.canceledSubscription !== 'undefined') {
      if (updatedUserInfo.canceledSubscription) {
        // cancel subscription
        await this.shared.cancelSubscription(user.customerStripeId);
      } else {
        // reactivate subscription
        paymentUrl = await this.shared.getSubscriptionUrl(user, false, user.subscriptionId);
        updatedUserInfo.canceledSubscription = true;
      }
    }

    const resp = await this.service.repo.update({id: userPayload.id}, updatedUserInfo);

    // changer de plan
    if (!!updatedUserInfo.subscriptionId) {
      const subscription: Subscription = await this.service.repoSubscription.findOne({where: {id: updatedUserInfo.subscriptionId}});
      const subscriptions = await this.shared.getSubscriptions(user.customerStripeId);
      await this.shared.updateSubscription(subscriptions[subscriptions.length - 1].id, subscription.stripePlanId);
    }

    if (!resp) {
      throw new Error('Error updating user');
    }

    if (paymentUrl) {
      return {resp, paymentUrl};
    } else {
      return resp;
    }
  }


  @Put('/password')
  @ApiOperation({
    operationId: 'Update current user',
    description: 'Update user information for logged user',
  })
  @ApiOkResponse({ type: UserResponse })
  @UseGuards(ConnectedUserGuard)
  async updateUserPassword(
    @Body() updatedUserInfo: UserUpdateRequest,
    @CurrentUser() userPayload: TokenPayload
  ) {
    const validate = await this.authService.validateUser(userPayload.email, updatedUserInfo.oldPassword);
    if (!validate) {
      throw new Error('Incorrect Old Password');
    }
    return await this.service.repo.update({ id: userPayload.id }, { password: updatedUserInfo.password });
  }

}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.nzoni.app/nest.js/users.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
