> For the complete documentation index, see [llms.txt](https://docs.nzoni.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nzoni.app/nest.js/users.md).

# 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
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
