HbarSuite Docs
  • Welcome to HbarSuite
  • HbarSuite Developer Documentation
    • HbarSuite Smart Engine Applications
      • @hsuite/cross-chain-exchange
      • @hsuite/dao
        • DAO Application Testing
      • @hsuite/exchange
      • @hsuite/launchpad
      • @hsuite/multisig
      • @hsuite/nft-exchange
      • HSuite Smart App - Enterprise Hedera Application Framework
    • HSuite Libraries
      • @hsuite/api-key - Enterprise API Key Authentication System
      • @hsuite/auth-types
      • @hsuite/auth - Authentication Module
      • @hsuite/client-types
      • @hsuite/client - Client Service Module
      • @hsuite/dkg-types - Distributed Key Generation Type Definitions
      • @hsuite/hashgraph-types - Hedera Hashgraph Type Definitions
      • @hsuite/health - Comprehensive System Health Monitoring
      • @hsuite/helpers - Utility Library
      • @hsuite/ipfs - InterPlanetary File System Integration
      • @hsuite/shared-types - Shared Type Definitions
      • @hsuite/smart-config - Configuration Management
      • @hsuite/smart-ledgers - Multi-Ledger Management
      • @hsuite/smart-network-types - Smart Network Type Definitions
      • @hsuite/smart-transaction-types - Smart Transaction Type Definitions
      • @hsuite/smartnode-sdk - SmartNode Software Development Kit
      • @hsuite/snapshots - Multi-Ledger Token Snapshot Management
      • @hsuite/subscriptions-types - Subscription Management Type Definitions
      • @hsuite/subscriptions - Enterprise Subscription Management System
      • @hsuite/throttler-types - Rate Limiting Type Definitions
      • @hsuite/throttler - Advanced Rate Limiting for NestJS
      • @hsuite/users-types - User Type Definitions
      • @hsuite/users - User Management Module
      • @hsuite/validators-types
  • General Documentation
    • Smart Apps and Interaction
      • Subscription-Based Model
      • Token-Gate Model
    • The Smart Node Network
      • security-layer
      • Type of Validators Explained
      • Understanding Validators in Our System
      • Automating Responses to Network Changes & Key Rotation
      • Ensuring Continuous Operation and Recovery
      • Generating and Sharing Keys Collaboratively
      • Handling Node Expulsion and Replacement
      • Managing Cluster Membership
      • Protecting Secrets with Shamir's Method
      • Security Layer Integration
      • Setting Up Secure Clusters
    • Tokenomics
      • Tokenomics v1
      • Tokenomics V2
    • What is a Smart Node?
  • Restful APIs Documentation
Powered by GitBook
On this page
  • Table of Contents
  • Quick Start
  • Installation
  • Basic Setup
  • Basic Usage
  • Architecture
  • Core Components
  • Module Structure
  • API Reference
  • UsersModule
  • UsersService
  • UsersController
  • Guides
  • User Registration Guide
  • Profile Management Guide
  • Authentication Integration Guide
  • Examples
  • Complete Module Configuration
  • User Registration Service
  • Profile Management Service
  • User Administration Service
  • Integration
  • Required Dependencies
  • Environment Variables
  • Database Schema
  • 📱 Modern Features: Avatar uploads, social links, preferences, and comprehensive profile management.
  1. HbarSuite Developer Documentation
  2. HSuite Libraries

@hsuite/users - User Management Module

Previous@hsuite/users-types - User Type DefinitionsNext@hsuite/validators-types

Last updated 7 hours ago

👥 Comprehensive NestJS module for user management and profile operations

Advanced user management module providing complete user lifecycle management, profile operations, and authentication integration for HSuite applications.


Table of Contents


Quick Start

Installation

npm install @hsuite/users

Basic Setup

import { UsersModule } from '@hsuite/users';

@Module({
  imports: [
    UsersModule.forRootAsync({
      useFactory: () => ({
        enableProfilePictures: true,
        enableEmailVerification: true,
        passwordPolicy: {
          minLength: 8,
          requireUppercase: true,
          requireNumbers: true
        }
      })
    })
  ]
})
export class AppModule {}

Basic Usage

@Injectable()
export class UserService {
  constructor(private usersService: UsersService) {}

  async createUser(userData: CreateUserDto) {
    return await this.usersService.create(userData);
  }
}

Architecture

Core Components

👤 User Management

  • UsersService - Core user operations and business logic

  • UsersController - REST API endpoints for user operations

  • User Entity - Database entity with full user profile support

📧 Profile Management

  • Profile Updates - Real-time profile modification

  • Avatar Management - Profile picture upload and management

  • Preference Settings - User preferences and configuration

🔐 Authentication Integration

  • Password Management - Secure password hashing and validation

  • Email Verification - Email confirmation workflows

  • Account Status - User activation, suspension, and deletion

Module Structure

src/
├── entities/              # User database entities
├── dto/                   # Data transfer objects
├── interfaces/            # TypeScript interfaces
├── users.service.ts       # Core user service
├── users.controller.ts    # HTTP endpoints
├── users.module.ts        # Module configuration
└── index.ts              # Public API exports

API Reference

UsersModule

Static Methods

forRootAsync(options: UsersModuleAsyncOptions): DynamicModule

Configures the users module with async dependency injection.

interface UsersModuleAsyncOptions {
  imports?: any[];
  useFactory?: (...args: any[]) => Promise<IUsers.IOptions>;
  inject?: any[];
  useClass?: Type<any>;
}

UsersService

Core Methods

create(userData: CreateUserDto): Promise<User>

  • Creates a new user with validation

  • Parameters: userData - User registration information

  • Returns: Created user entity with assigned ID

findById(id: string): Promise<User>

  • Retrieves user by ID

  • Parameters: id - User unique identifier

  • Returns: User entity with full profile details

findByEmail(email: string): Promise<User>

  • Finds user by email address

  • Parameters: email - User email address

  • Returns: User entity or null if not found

update(id: string, updateData: UpdateUserDto): Promise<User>

  • Updates user profile information

  • Parameters: id, updateData - User ID and update data

  • Returns: Updated user entity

delete(id: string): Promise<void>

  • Soft deletes user account

  • Parameters: id - User ID to delete

  • Throws: Error if user has active dependencies

UsersController

Endpoints

POST /users - Create new userGET /users/:id - Get user by IDPUT /users/:id - Update user profileDELETE /users/:id - Delete user accountGET /users/:id/profile - Get user profilePUT /users/:id/avatar - Update profile picture


Guides

User Registration Guide

Learn how to implement user registration with validation and verification. Set up email confirmation, password policies, and user onboarding flows with comprehensive error handling.

Profile Management Guide

Manage user profiles, avatars, and preferences. Implement profile updates, image uploads, social links management, and privacy controls for user data.

Authentication Integration Guide

Integrate user management with authentication systems. Connect with JWT authentication, session management, and role-based access control systems.


Examples

Complete Module Configuration

import { UsersModule } from '@hsuite/users';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    UsersModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        enableProfilePictures: true,
        enableEmailVerification: true,
        passwordPolicy: {
          minLength: configService.get('PASSWORD_MIN_LENGTH', 8),
          requireUppercase: true,
          requireLowercase: true,
          requireNumbers: true,
          requireSpecialChars: false,
          maxLength: 128
        },
        profileSettings: {
          allowPublicProfiles: configService.get('ALLOW_PUBLIC_PROFILES', true),
          enableBio: true,
          enableSocialLinks: true,
          maxBioLength: 500
        },
        fileUpload: {
          maxAvatarSize: 5 * 1024 * 1024, // 5MB
          allowedImageTypes: ['jpg', 'jpeg', 'png', 'gif'],
          uploadPath: configService.get('UPLOAD_PATH', './uploads/avatars')
        }
      }),
      inject: [ConfigService]
    })
  ]
})
export class UserManagementModule {}

User Registration Service

import { Injectable, ConflictException, BadRequestException } from '@nestjs/common';
import { UsersService } from '@hsuite/users';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UserRegistrationService {
  constructor(private usersService: UsersService) {}

  async registerUser(registrationData: {
    email: string;
    password: string;
    firstName: string;
    lastName: string;
    organizationName?: string;
    acceptedTerms: boolean;
  }) {
    // Validate registration data
    await this.validateRegistrationData(registrationData);

    try {
      // Create user with pending email verification
      const user = await this.usersService.create({
        email: registrationData.email,
        password: registrationData.password,
        firstName: registrationData.firstName,
        lastName: registrationData.lastName,
        organizationName: registrationData.organizationName,
        status: 'pending_verification',
        emailVerified: false,
        termsAcceptedAt: new Date(),
        createdAt: new Date()
      });

      // Send verification email
      await this.sendVerificationEmail(user);

      return {
        success: true,
        user: {
          id: user.id,
          email: user.email,
          firstName: user.firstName,
          lastName: user.lastName,
          status: user.status
        },
        message: 'Registration successful. Please check your email for verification.'
      };
    } catch (error) {
      if (error.code === '23505') { // Unique constraint violation
        throw new ConflictException('Email address already registered');
      }
      throw error;
    }
  }

  async verifyEmail(verificationToken: string) {
    const user = await this.usersService.findByVerificationToken(verificationToken);
    
    if (!user) {
      throw new BadRequestException('Invalid verification token');
    }

    if (user.emailVerified) {
      return {
        success: true,
        message: 'Email already verified'
      };
    }

    // Update user status
    const updatedUser = await this.usersService.update(user.id, {
      emailVerified: true,
      status: 'active',
      verificationToken: null,
      verifiedAt: new Date()
    });

    return {
      success: true,
      user: {
        id: updatedUser.id,
        email: updatedUser.email,
        status: updatedUser.status,
        emailVerified: updatedUser.emailVerified
      },
      message: 'Email verified successfully'
    };
  }

  private async validateRegistrationData(data: any) {
    // Check if email is already registered
    const existingUser = await this.usersService.findByEmail(data.email);
    if (existingUser) {
      throw new ConflictException('Email address already registered');
    }

    // Validate terms acceptance
    if (!data.acceptedTerms) {
      throw new BadRequestException('Terms and conditions must be accepted');
    }

    // Additional validation logic...
  }

  private async sendVerificationEmail(user: any) {
    // Implementation for sending verification email
    console.log(`Verification email sent to ${user.email}`);
  }
}

Profile Management Service

import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { UsersService } from '@hsuite/users';
import * as path from 'path';
import * as fs from 'fs/promises';

@Injectable()
export class ProfileManagementService {
  constructor(private usersService: UsersService) {}

  async updateProfile(userId: string, profileData: {
    firstName?: string;
    lastName?: string;
    bio?: string;
    phoneNumber?: string;
    dateOfBirth?: Date;
    socialLinks?: Record<string, string>;
    preferences?: Record<string, any>;
  }) {
    const user = await this.usersService.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Validate profile data
    this.validateProfileData(profileData);

    const updatedUser = await this.usersService.update(userId, {
      ...profileData,
      updatedAt: new Date()
    });

    return {
      success: true,
      user: {
        id: updatedUser.id,
        firstName: updatedUser.firstName,
        lastName: updatedUser.lastName,
        bio: updatedUser.bio,
        phoneNumber: updatedUser.phoneNumber,
        socialLinks: updatedUser.socialLinks,
        updatedAt: updatedUser.updatedAt
      },
      message: 'Profile updated successfully'
    };
  }

  async uploadAvatar(userId: string, file: Express.Multer.File) {
    const user = await this.usersService.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Validate file
    this.validateAvatarFile(file);

    try {
      // Save file
      const fileName = `${userId}_${Date.now()}${path.extname(file.originalname)}`;
      const uploadPath = path.join('./uploads/avatars', fileName);
      
      await fs.writeFile(uploadPath, file.buffer);

      // Delete old avatar if exists
      if (user.avatarUrl) {
        await this.deleteOldAvatar(user.avatarUrl);
      }

      // Update user with new avatar URL
      const updatedUser = await this.usersService.update(userId, {
        avatarUrl: `/uploads/avatars/${fileName}`,
        updatedAt: new Date()
      });

      return {
        success: true,
        avatarUrl: updatedUser.avatarUrl,
        message: 'Avatar updated successfully'
      };
    } catch (error) {
      throw new BadRequestException('Failed to upload avatar');
    }
  }

  async getUserProfile(userId: string, requestingUserId?: string) {
    const user = await this.usersService.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    // Check if profile is public or user is viewing own profile
    const isOwnProfile = requestingUserId === userId;
    const isPublicProfile = user.profileVisibility === 'public';

    if (!isOwnProfile && !isPublicProfile) {
      throw new BadRequestException('Profile is private');
    }

    return {
      id: user.id,
      firstName: user.firstName,
      lastName: user.lastName,
      email: isOwnProfile ? user.email : null, // Only show email to owner
      bio: user.bio,
      avatarUrl: user.avatarUrl,
      socialLinks: user.socialLinks,
      joinedAt: user.createdAt,
      isVerified: user.emailVerified,
      profileVisibility: user.profileVisibility
    };
  }

  async updatePreferences(userId: string, preferences: {
    theme?: 'light' | 'dark';
    language?: string;
    notifications?: {
      email: boolean;
      push: boolean;
      sms: boolean;
    };
    privacy?: {
      profileVisibility: 'public' | 'private';
      showEmail: boolean;
      showPhoneNumber: boolean;
    };
  }) {
    const user = await this.usersService.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    const updatedUser = await this.usersService.update(userId, {
      preferences: {
        ...user.preferences,
        ...preferences
      },
      updatedAt: new Date()
    });

    return {
      success: true,
      preferences: updatedUser.preferences,
      message: 'Preferences updated successfully'
    };
  }

  private validateProfileData(data: any) {
    if (data.bio && data.bio.length > 500) {
      throw new BadRequestException('Bio must be 500 characters or less');
    }

    if (data.phoneNumber && !/^\+?[\d\s\-\(\)]+$/.test(data.phoneNumber)) {
      throw new BadRequestException('Invalid phone number format');
    }

    // Additional validation...
  }

  private validateAvatarFile(file: Express.Multer.File) {
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (!allowedTypes.includes(file.mimetype)) {
      throw new BadRequestException('Invalid file type. Only JPEG, PNG, and GIF allowed');
    }

    const maxSize = 5 * 1024 * 1024; // 5MB
    if (file.size > maxSize) {
      throw new BadRequestException('File size too large. Maximum 5MB allowed');
    }
  }

  private async deleteOldAvatar(avatarUrl: string) {
    try {
      const filePath = path.join('.', avatarUrl);
      await fs.unlink(filePath);
    } catch (error) {
      console.error('Failed to delete old avatar:', error);
    }
  }
}

User Administration Service

import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
import { UsersService } from '@hsuite/users';

@Injectable()
export class UserAdministrationService {
  constructor(private usersService: UsersService) {}

  async getAllUsers(adminUserId: string, filters?: {
    status?: string;
    role?: string;
    search?: string;
    page?: number;
    limit?: number;
  }) {
    // Verify admin permissions
    await this.verifyAdminPermissions(adminUserId);

    const users = await this.usersService.findAll({
      status: filters?.status,
      role: filters?.role,
      search: filters?.search,
      page: filters?.page || 1,
      limit: filters?.limit || 50
    });

    return {
      users: users.data.map(user => ({
        id: user.id,
        email: user.email,
        firstName: user.firstName,
        lastName: user.lastName,
        status: user.status,
        role: user.role,
        emailVerified: user.emailVerified,
        createdAt: user.createdAt,
        lastLoginAt: user.lastLoginAt
      })),
      pagination: {
        total: users.total,
        page: users.page,
        limit: users.limit,
        totalPages: Math.ceil(users.total / users.limit)
      }
    };
  }

  async suspendUser(adminUserId: string, userId: string, reason: string) {
    await this.verifyAdminPermissions(adminUserId);

    const user = await this.usersService.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    const updatedUser = await this.usersService.update(userId, {
      status: 'suspended',
      suspendedAt: new Date(),
      suspensionReason: reason,
      suspendedBy: adminUserId
    });

    // Log admin action
    await this.logAdminAction(adminUserId, 'suspend_user', userId, reason);

    return {
      success: true,
      user: {
        id: updatedUser.id,
        status: updatedUser.status,
        suspendedAt: updatedUser.suspendedAt
      },
      message: 'User suspended successfully'
    };
  }

  async reactivateUser(adminUserId: string, userId: string) {
    await this.verifyAdminPermissions(adminUserId);

    const user = await this.usersService.findById(userId);
    if (!user) {
      throw new NotFoundException('User not found');
    }

    const updatedUser = await this.usersService.update(userId, {
      status: 'active',
      suspendedAt: null,
      suspensionReason: null,
      suspendedBy: null,
      reactivatedAt: new Date(),
      reactivatedBy: adminUserId
    });

    await this.logAdminAction(adminUserId, 'reactivate_user', userId);

    return {
      success: true,
      user: {
        id: updatedUser.id,
        status: updatedUser.status,
        reactivatedAt: updatedUser.reactivatedAt
      },
      message: 'User reactivated successfully'
    };
  }

  async getUserStatistics() {
    const stats = await this.usersService.getStatistics();

    return {
      totalUsers: stats.total,
      activeUsers: stats.active,
      suspendedUsers: stats.suspended,
      pendingVerification: stats.pendingVerification,
      registrationsThisMonth: stats.registrationsThisMonth,
      lastLoginActivity: stats.lastLoginActivity
    };
  }

  private async verifyAdminPermissions(userId: string) {
    const user = await this.usersService.findById(userId);
    if (!user || user.role !== 'admin') {
      throw new ForbiddenException('Insufficient permissions');
    }
  }

  private async logAdminAction(
    adminId: string,
    action: string,
    targetUserId: string,
    reason?: string
  ) {
    // Implementation for logging admin actions
    console.log(`Admin ${adminId} performed ${action} on user ${targetUserId}`);
  }
}

Integration

Required Dependencies

{
  "@hsuite/users-types": "^2.0.9",
  "@hsuite/auth": "^2.1.2",
  "@hsuite/shared-types": "^2.0.0",
  "@nestjs/typeorm": "^10.0.0",
  "typeorm": "^0.3.0"
}

Environment Variables

# User Management
PASSWORD_MIN_LENGTH=8
ALLOW_PUBLIC_PROFILES=true
UPLOAD_PATH=./uploads/avatars

# Email Verification
EMAIL_VERIFICATION_REQUIRED=true
VERIFICATION_TOKEN_EXPIRY=24h

# File Upload
MAX_AVATAR_SIZE=5242880
ALLOWED_IMAGE_TYPES=jpg,jpeg,png,gif

Database Schema

-- Users table
CREATE TABLE users (
  id VARCHAR PRIMARY KEY,
  email VARCHAR UNIQUE NOT NULL,
  password_hash VARCHAR NOT NULL,
  first_name VARCHAR NOT NULL,
  last_name VARCHAR NOT NULL,
  bio TEXT,
  phone_number VARCHAR,
  date_of_birth DATE,
  avatar_url VARCHAR,
  social_links JSONB,
  preferences JSONB,
  status VARCHAR DEFAULT 'pending_verification',
  role VARCHAR DEFAULT 'user',
  email_verified BOOLEAN DEFAULT FALSE,
  profile_visibility VARCHAR DEFAULT 'public',
  terms_accepted_at TIMESTAMP,
  verified_at TIMESTAMP,
  suspended_at TIMESTAMP,
  suspension_reason TEXT,
  suspended_by VARCHAR,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- User sessions
CREATE TABLE user_sessions (
  id VARCHAR PRIMARY KEY,
  user_id VARCHAR REFERENCES users(id),
  token_hash VARCHAR NOT NULL,
  expires_at TIMESTAMP NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

👥 User-Centric: Complete user lifecycle management from registration to administration.

🔐 Security First: Built-in password policies, email verification, and secure session management.

📱 Modern Features: Avatar uploads, social links, preferences, and comprehensive profile management.

Built with ❤️ by the HbarSuite Team Copyright © 2024 HbarSuite. All rights reserved.

Quick Start
Architecture
API Reference
Guides
Examples
Integration