- Introduction
- Technologies
- Installation
- Running the Application
- Modules
- Entities
- Guards
- RabbitMQ
- Environment File
- License
- Authors
The primary purpose of this project is to develop a common notification service (CNS) as well as a notification API service that meets the specific business and technical requirements of SPTel.
- NodeJS Version: 20.10.0
- Pnpm Version: 8.12.1
- RabbitMQ Version: 3.12-management (Docker Image)
- MongoDB
- Postgres
- NestJS Version: 10.3.1
$ pnpm install
# watch mode
$ pnpm run start:dev
# notification service
$ pnpm run start:notification
# CNS
$ pnpm run start:cns
CNS-User Modules (Link to the Swagger locally and UAT)
File Directory:
CNS-Notification-Record Module (Link to the Swagger locally and UAT)
File Directory:
- CNS architecture mainly comprise of the Notification API service, Message Queues and Workers.
Responsible for consuming message that are stuck in the queue or unconsume from the worker service
File Directory:
File Directory:
-
Firstly, go to my cns-user swagger to generate the API-Key --> localhost:3070
-
In Postman, set up the secret key and post to
http://localhost:3000/v1/api/notification-api/email
(Email) orhttp://localhost:3000/v1/api/notification-api/SMS
(SMS) -
Secondly, set up your message body to be like this
Responsible for consuming messages from the message queues and process the notifications
File Directory:
- Gurads
- ApiKey
- Authentication
- Authorisation
- Common
- Constant
- Entity
- DTOs
- Exception-Filter
- Interceptors
- RabbitMQ
- Config
- Mongoose Configuration
- Postgres Configuration
-
The purpose of the local strategy is to verify and authenticate the credentials of a user during the sign-in process.
-
Example of using local Strategy Guard throughout the application:
// apps/cns-user/src/user-auth/user-auth.controller.ts @Post('signIn') @UseGuards(UserAuthGuard) async signIn(@Request() req: any) {}
-
The Jwt Strategy is implemented to validate the authentication of users accessing an API, ensuring that only authenticated users are authorized to make API calls.
-
Example of using Jwt Strategy Guard throughout the application:
// Can be seen across the different controllers @Get('listRoles') @UseGuards(JwtAuthGuard) async listRoles() {}
-
The Refresh Token strategy is designed to handle the renewal of authentication tokens for users, allowing them to obtain new access tokens without requiring them to re-enter their credentials.
-
Example of using Refresh Token Strategy Guard:
// apps/cns-user/src/user-auth/user-auth.controller.ts @Get('refreshToken') @UseGuards(RefreshTokenGuard) async refreshToken() {}
-
The Reset Password Strategy is to facilitate the process of resetting a user's password when they have forgotten it or need to change it.
-
Example of using Reset Password Strategy Guard:
// apps/cns-user/src/user-auth/user-auth.controller.ts @Patch('resetPassword') @UseGuards(ResetPasswordGuard) async resetPassword() {}
-
The purpose of the ApiKey Strategy is to authenticate whether a user has the authorization to send emails.
-
Example of using ApiKey Strategy Guard:
// apps/notification-api/src/modules/email-api/email-api.controller.ts @Post('/email') @UseGuards(ApiAuthGuard) async publishEmail() {}
- The purpose of CASL is to provide user-centric access control for application. CASL enable developers to define permissions and role for the application. To know more about CASL
- The purpose is to create the
Ability
Object for that given user - Method:
async defineAbilitiesFor(user: any)
- The purpose is to obtains the user information from the incoming request, and creates an ability object using the caslAbilityFactory. It then proceeds to validate each policy handler against the user's abilities.
- The PoliciesGuard acts as a middleware that enforces policies by checking if the user has the required abilities to access a particular route or resource.
- Method:
async canActivate(context: ExecutionContext): Promise<boolean>
- You are able to use this authorisation guard throughout the application
- Example:
// Can be seen across the different controllers @Get('GroupUsersByOrganisation') @UseGuards(PolicyGuard) @CheckPolicies((ability: AppAbility) => ability.can(Operation.Read, 'Organisation'), ) async groupUsersByOrganisation() {}
- Example:
🐰 RabbitMQ
RabbitMQ serves as a message broker, facilitating the exchange of messages between different components, systems, or services within an application or across multiple applications. To know more about RabbitMQ.
The library I use:
onModuleInit() {
const rabbitmqUri = this.configService.get<string>('RABBITMQ_URI');
this.connection = connect([rabbitmqUri]);
- By setting up the connection, you will be able to access the rabbitMQ: localhost:15672
- Username: guest
- Password: guest
The password can be easily change under the Admin page You can even add user and set its access control
this.channel = this.connection.createChannel({
json: true,
setup: async (channel) => await this.setupRabbitMQ(channel),
confirm: true,
});
The code I written will automatically create the exchange, queues and simultaneously bind the queue and exchange together
await channel.assertExchange(EX_NOTIFICATION, 'direct', {
durable: true,
});
await channel.assertQueue(QUEUE_SMS, {
durable: true,
arguments: {
'x-queue-type': 'quorum',
'x-message-ttl': 10000,
'x-dead-letter-exchange': DLX_EXCHANGE,
'x-dead-letter-routing-key': RK_NOTIFICATION_SMS,
'x-delivery-limit': 5,
},
});
You can access the queue to gain more detailed insights and information about the specific queue.
await channel.bindQueue(
QUEUE_SMS,
EX_NOTIFICATION,
RK_NOTIFICATION_SMS,
);
public async publish(routingkey: string, message: any) {
const result = await this.channel.publish(
EX_NOTIFICATION,
routingkey,
Buffer.from(JSON.stringify(message)),
);
return result;
}
public async subscribe(queue: string, onMessage: (msg) => void) {
...
}
Environment files are avaliable in here
This project is licensed under the MIT License.
The one and only legendary CM 💪