Skip to content

Commit

Permalink
fix: verification code issue
Browse files Browse the repository at this point in the history
  • Loading branch information
aliakkas006 committed Apr 30, 2024
1 parent 7f178bd commit 3ce2733
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 120 deletions.
37 changes: 0 additions & 37 deletions .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,7 @@ on:
- main

jobs:
# run-tests:
# runs-on: ubuntu-latest

# steps:
# - name: Checkout code
# uses: actions/checkout@v4

# - name: Set up Node.js
# uses: actions/setup-node@v4
# with:
# node-version: '21'

# - name: Test appointment service
# run: |
# cd services/appointment
# yarn install --frozen-lockfile
# yarn test

# - name: Test user service
# run: |
# cd services/user
# yarn install --frozen-lockfile
# yarn test

# - name: Test auth service
# run: |
# cd services/auth
# yarn install --frozen-lockfile
# yarn test

# - name: Test email service
# run: |
# cd services/email
# yarn install --frozen-lockfile
# yarn test

build-and-push:
# needs: run-tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand Down
77 changes: 73 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## [Basic System Desin](https://ali-akkas.notion.site/Healthcare-Appointment-Scheduling-System-cf67ead3bb1947f58f505c18fb886280?pvs=4)


## Overview

- The Healthcare Appointment Scheduling System employs a microservices architecture with Docker containers for modularization and scalability.
- CI/CD pipelines ensure automated and reliable deployment of updates and features.
- RabbitMQ enables asynchronous communication between services, enhancing system resilience and responsiveness.
Expand All @@ -22,31 +22,100 @@
- [Docker](https://www.docker.com/) - Docker is a platform designed to help developers build, share, and run container applications.
- [Redis](https://redis.io/) - Redis is a source-available, in-memory storage, used as a distributed, in-memory key–value database, cache and message broker, with optional durability.
- [RabbitMQ](https://www.rabbitmq.com/) - RabbitMQ is another widely used open source message broker, employed by several companies worldwide.
- [GitHub Actions](https://github.com/features/actions) - GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows to automate build, test, and deployment pipeline.
- [Postman](https://www.postman.com/) - Postman is an application that allows the testing of web APIs.

## File Structure

```
Healthcare-Appointment-Scheduling-System/
├── .github
└── workflows
└── cd.yaml
├── api-gateway
└── services/
├── auth
├── user
├── auth/
├── prisma
├── src/
├── controllers
├── lib
├── routes
├── app.ts
├── index.ts
├── config.ts
├── tests
├── package.json
├── Dockerfile
├── user
├── prisma
├── src/
├── controllers
├── lib
├── routes
├── app.ts
├── index.ts
├── config.ts
├── tests
├── package.json
├── Dockerfile
├── email
├── prisma
├── src/
├── controllers
├── lib
├── routes
├── app.ts
├── index.ts
├── config.ts
├── tests
├── package.json
├── Dockerfile
├── appointment
├── prisma
├── src/
├── controllers
├── lib
├── routes
├── app.ts
├── index.ts
├── config.ts
├── tests
├── package.json
├── Dockerfile
├── notification
├── prisma
├── src/
├── controllers
├── lib
├── routes
├── app.ts
├── index.ts
├── config.ts
├── tests
├── package.json
├── Dockerfile
├── docker-compose.yaml
├── README.md
```

## Setup

follow .env.example file for setup environment variables

### Run the `Microservices Dependency`

```bash
docker-compose up
```

### Run the `Tests`
```

```bash
yarn run test .\tests\**\**\*
```

### Automatic `CI/CD`

```
When push the code in the GitHub automatic run the actions
```
4 changes: 2 additions & 2 deletions services/appointment/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ router
.post('/appointments', createAppointment)
.get('/appointments', getAppointments);

router.get('/appointments/:patientId', getAppointmentsByPatientId);

router
.get('/appointments/:id', getAppointmentById)
.delete('/appointments/:id', deleteAppointmentById);

router.get('/appointments/:patientId', getAppointmentsByPatientId);

router.post('/patients', createPatient).get('/patients', getPatients);

router.post('/providers', createProvider).get('/providers', getProviders);
Expand Down
6 changes: 4 additions & 2 deletions services/auth/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
DATABASE_URL="postgresql://admin:password@localhost:5432/auth_db?schema=public"
JWT_SECRET=Secret_Key
DATABASE_URL="postgresql://admin:password@postgres:5432/auth_db?schema=public"
JWT_SECRET=Secret_Key
USER_SERVICE_URL='http://user:4000'
EMAIL_SERVICE_URL='http://email:4006'
3 changes: 2 additions & 1 deletion services/auth/src/controllers/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import loginService from '@/lib/LoginService';

const login = async (req: Request, res: Response, next: NextFunction) => {
try {
const ipAddress = (req.headers['x-forwarded-for'] as string) || req.ip || '';
const ipAddress =
(req.headers['x-forwarded-for'] as string) || req.ip || '';
const userAgent = req.headers['user-agent'] || '';

// Validate the request body
Expand Down
25 changes: 12 additions & 13 deletions services/auth/src/controllers/register.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,41 @@
import { Request, Response, NextFunction } from 'express';
import { Response, Request, NextFunction } from 'express';
import { UserCreateSchema } from '@/schemas';
import registrationService from '@/lib/RegistrationService';
import emailService from '@/lib/EmailService';

const register = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate request body
// Validate the request body
const parsedBody = UserCreateSchema.safeParse(req.body);
if (!parsedBody.success) {
return res.status(400).json({ errors: parsedBody.error });
return res.status(400).json({ errors: parsedBody.error.errors });
}

const { name, email } = parsedBody.data;

// Check if the user already exists
const existingUser = await registrationService.checkExistingUser(email);
if (existingUser) {
return res.status(400).json({ errors: 'User already exists!' });
return res.status(400).json({ message: 'User already exists' });
}

// Create the auth user
// Create the auth user and user profile
const user = await registrationService.createUser(parsedBody.data);

// Create the user profile by calling the user service
await registrationService.createUserProfile(user.id, name, email);

// Generate verification code
await registrationService.createVerificationCode(user.id);
const code = emailService.generateVerificationCode();
await emailService.createVerificationCode(user.id, code);

// Send the verification email
await registrationService.sendVerificationEmail(email);
// Send verification email
await emailService.sendVerificationEmail(email, code);

// Return the response
return res.status(201).json({
message: 'User created. Check your email for verification code',
user,
});
} catch (err) {
next(err);
} catch (error) {
next(error);
}
};

Expand Down
12 changes: 4 additions & 8 deletions services/auth/src/controllers/verifyToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,25 @@ import { Request, Response, NextFunction } from 'express';
import { AccessTokenSchema } from '@/schemas';
import tokenService from '@/lib/TokenService';

const verifyToken = (req: Request, res: Response, next: NextFunction) => {
const verifyToken = async (req: Request, res: Response, next: NextFunction) => {
try {
// Validate the request body
const parsedBody = AccessTokenSchema.safeParse(req.body);

if (!parsedBody.success) {
return res.status(400).json({ errors: parsedBody.error.errors });
}

const { accessToken } = parsedBody.data;

// Verify the access token
const decoded = tokenService.validateAccessToken(accessToken);
const user = tokenService.getUserFromToken(decoded);

const user = await tokenService.getUserFromToken(decoded);
if (!user) {
return res.status(401).json({ message: 'Unauthorized' });
}

// Return the user
return res.status(200).json({ message: 'Authorized', user });
} catch (err) {
next(err);
} catch (error) {
next(error);
}
};

Expand Down
38 changes: 35 additions & 3 deletions services/auth/src/lib/EmailService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,41 @@ import prisma from '@/prisma';
import axios from 'axios';

class EmailService {
/**
* Generates a random 5-digit verification code.
*/
public generateVerificationCode() {
const randomNum = Math.floor(10000 + Math.random() * 90000);
return randomNum.toString();
}

/**
* Create verification code and save it to the database
*/
public async createVerificationCode(userId: string, code: string) {
await prisma.verificationCode.create({
data: {
userId,
code,
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24), // 24 hours
},
});
}

/**
* Send the verification email.
*/
public async sendVerificationEmail(recipient: string, code: string) {
console.log('Sending verification email to:', recipient);

await axios.post(`${EMAIL_SERVICE}/emails/send`, {
recipient,
subject: 'Email Verification',
body: `Your verification code is ${code}`,
source: 'user-registration',
});
}

/**
* Verify user email via verification code
*/
Expand All @@ -22,8 +57,6 @@ class EmailService {
},
});

console.log('verificationCode: ', verificationCode);

if (!verificationCode) {
throw new Error('Invalid verification code');
}
Expand All @@ -35,7 +68,6 @@ class EmailService {

return { user, verificationCode };
}

/**
* Update user account status
*/
Expand Down
36 changes: 1 addition & 35 deletions services/auth/src/lib/RegistrationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ import axios from 'axios';
import bcrypt from 'bcryptjs';

class RegistrationService {
/**
* Generates a random 5-digit verification code.
*/
private generateVerificationCode() {
const randomNum = Math.floor(10000 + Math.random() * 90000);
return randomNum.toString();
}

/**
* Check if a user with the given email already exists.
*/
Expand All @@ -34,7 +26,7 @@ class RegistrationService {
}

/**
* Create a auth user.
* Create an auth user.
*/
public async createUser(userData) {
const user = await prisma.user.create({
Expand Down Expand Up @@ -67,32 +59,6 @@ class RegistrationService {
console.log('USER:', user.data);
return user;
}

/**
* Create a verification code.
*/
public async createVerificationCode(userId) {
const verificationCode = await prisma.verificationCode.create({
data: {
userId,
code: this.generateVerificationCode(),
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24), // 24 hours
},
});
return verificationCode;
}

/**
* Send the verification email.
*/
public async sendVerificationEmail(recipient: string) {
await axios.post(`${EMAIL_SERVICE}/emails/send`, {
recipient,
subject: 'Email Verification',
body: `Your verification code is ${this.generateVerificationCode()}`,
source: 'user-registration',
});
}
}

const registrationService = new RegistrationService();
Expand Down
Loading

0 comments on commit 3ce2733

Please sign in to comment.