Skip to content

Commit

Permalink
[delivers #187584915] Added user login (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
ProgrammerDATCH authored May 29, 2024
1 parent 80e32f4 commit f8e8970
Show file tree
Hide file tree
Showing 13 changed files with 347 additions and 161 deletions.
28 changes: 23 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
PORT=
NODE_ENV=
NODE_EN=
DOCKER_DATABASE_HOST_AUTH_METHOD=
DOCKER_DATABASE_NAME=
DOCKER_DATABASE_PASSWORD=
JWT_SECRET=
NODE_ENV=

SMTP_HOST_PORT=
MP=
SMTP_HOST=
MAIL_ID=

CLOUD_NAME=
API_KEY=
API_SECRET=

SERVER_URL_DEV=
SERVER_URL_PRO=

DATABASE_URL_DEV=
DATABASE_URL_TEST=
DATABASE_URL_PRO=
DB_HOST_TYPE=



DB_HOST_TYPE=
DOCKER_DATABASE_USER=
DOCKER_DATABASE_HOST_AUTH_METHOD=
DOCKER_DATABASE_NAME=
DOCKER_DATABASE_PASSWORD=
57 changes: 10 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Team Ninjas Backend
# TEAM NINJAS BACKEND

This is the backend for E-Commerce-Ninjas, written in Node.js with TypeScript.

Expand All @@ -23,26 +23,28 @@ This is the backend for E-Commerce-Ninjas, written in Node.js with TypeScript.
[https://github.com/atlp-rwanda/e-commerce-ninjas-bn](https://github.com/atlp-rwanda/e-commerce-ninjas-bn)


## Completed Features
## COMPLETED FEATURES

- Welcome Endpoint
- Register Endpoint
- Verification Email Endpoint
- Resend verification Endpoint
- Login Endpoint

## TABLE OF API ENDPOINTS SPECIFICATION AND DESCRIPTION


| No | VERBS | ENDPOINTS | STATUS | ACCESS | DESCRIPTION |
|----|-------|------------------------------|-------------|--------|---------------------------|
| 1 | GET | / | 200 OK | public | Show welcome message |
| 2 | GET | /api/auth/verify-email/:token| 200 OK | public | Verifying email |
| 3 | POST | /api/auth/register | 201 CREATED | public | create user account |
| 2 | POST | /api/auth/register | 201 CREATED | public | create user account |
| 3 | GET | /api/auth/verify-email/:token| 200 OK | public | Verifying email |
| 4 | POST | /api/auth/send-verify-email | 200 OK | public | Resend verification email |
| 4 | POST | /api/auth/login | 200 OK | public | Login with Email and Password |



## Installation
## INSTALLATION

1. Clone the repository:

Expand All @@ -63,7 +65,7 @@ This is the backend for E-Commerce-Ninjas, written in Node.js with TypeScript.
npm run dev
```

## Folder Structure
## FOLDER STRUCTURE

- `.env`: Secure environment variables.
- `src/`: Source code directory.
Expand All @@ -84,7 +86,7 @@ This is the backend for E-Commerce-Ninjas, written in Node.js with TypeScript.
- `services/`: Service functions like sendEmails.
- `index.ts`: Startup file for all requests.

## Initialize Sequelize CLI
## INITILIAZE SEQUELIZE CLI

1. Initialize Sequelize CLI:
```sh
Expand Down Expand Up @@ -117,43 +119,4 @@ This is the backend for E-Commerce-Ninjas, written in Node.js with TypeScript.
9. Delete the Migration:
```sh
npm run deleteAllTables
```



## Initialize Sequelize CLI

1. Initialize Sequelize CLI:
```sh
npx sequelize-cli init
```
2. Generate Seeder:
```sh
npx sequelize-cli seed:generate --name name-of-your-seeder
```
3. Generate Migrations:
```sh
npx sequelize-cli migration:generate --name name-of-your-migration
```
4. Define Migration:
Edit the generated migration file to include the tables you want to create.
5. Define Seeder Data:
Edit the generated seeder file to include the data you want to insert.
6. Run the Seeder:
```sh
npm run createAllSeeders
```
7. Run the Migration:
```sh
npm run createAllTables
```
8. Delete the Seeder:
```sh
npm run deleteAllSeeders
```
9. Delete the Migration:
```sh
npm run deleteAllTables
```


```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"start": "ts-node-dev src/index.ts",
"dev": "ts-node-dev src/index.ts",
"test": "cross-env NODE_ENV=test npm run deleteAllTables && cross-env NODE_ENV=test npm run createAllTables && cross-env NODE_ENV=test npm run createAllSeeders && nyc cross-env NODE_ENV=test mocha --require ts-node/register 'src/**/*.spec.ts' --timeout 600000 --exit",
"test-dev": "cross-env NODE_ENV=development npm run deleteAllTables && cross-env NODE_ENV=development npm run createAllTables && cross-env NODE_ENV=development npm run createAllSeeders && nyc cross-env NODE_ENV=development mocha --require ts-node/register 'src/**/*.spec.ts' --timeout 600000 --exit",
"coveralls": "nyc --reporter=lcov --reporter=text-lcov npm test | coveralls",
"coverage": "cross-env NODE_ENV=test nyc mocha --require ts-node/register 'src/**/*.spec.ts' --timeout 600000 --exit",
"lint": "eslint . --ext .ts",
Expand Down
7 changes: 6 additions & 1 deletion src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import jwt,{JwtPayload} from "jsonwebtoken"
import dotenv from "dotenv"
import bcrypt from "bcrypt"

dotenv.config

Expand All @@ -12,4 +13,8 @@ dotenv.config
;
};

export { generateToken, decodeToken}
const comparePassword = async (password: string, hashedPassword: string) =>{
return await bcrypt.compare(password, hashedPassword);
}

export { generateToken, decodeToken, comparePassword }
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,4 @@ app.listen(PORT, () => {
console.log(`Server is running on the port ${PORT}`);
});


export default app;
51 changes: 40 additions & 11 deletions src/middlewares/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import authRepositories from "../modules/auth/repository/authRepositories";
import { UsersAttributes } from "../databases/models/users";
import Joi from "joi";
import httpStatus from "http-status";
import { decodeToken } from "../helpers";
import { comparePassword, decodeToken } from "../helpers";
import { IRequest } from "../types";

const validation = (schema: Joi.ObjectSchema | Joi.ArraySchema) => async (req: Request, res: Response, next: NextFunction) => {
try {
Expand All @@ -20,22 +21,33 @@ const validation = (schema: Joi.ObjectSchema | Joi.ArraySchema) => async (req: R
}
};


const isUserExist = async (req: Request, res: Response, next: NextFunction) => {
try {
const userExists: UsersAttributes = await authRepositories.findUserByAttributes("email", req.body.email);
if (userExists && userExists.isVerified === true) {
return res.status(httpStatus.BAD_REQUEST).json({ status: httpStatus.BAD_REQUEST, message: "Account already exists." });
let userExists: UsersAttributes | null = null;

if (req.body.email) {
userExists = await authRepositories.findUserByAttributes("email", req.body.email);
if (userExists) {
if (userExists.isVerified) {
return res.status(httpStatus.BAD_REQUEST).json({ status: httpStatus.BAD_REQUEST, message: "Account already exists." });
}
return res.status(httpStatus.BAD_REQUEST).json({ status: httpStatus.BAD_REQUEST, message: "Account already exists. Please verify your account" });
}
}
if (userExists && userExists.isVerified === false) {
return res.status(httpStatus.BAD_REQUEST).json({ status: httpStatus.BAD_REQUEST, message: "Account already exists. Please verify your account" });

if (req.params.id) {
userExists = await authRepositories.findUserByAttributes("id", req.params.id);
if (userExists) {
return next();
}
return res.status(httpStatus.BAD_REQUEST).json({ status: httpStatus.BAD_REQUEST, message: "User not found" });
}

return next();
} catch (error) {
res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ status: httpStatus.INTERNAL_SERVER_ERROR, message: error.message })
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ status: httpStatus.INTERNAL_SERVER_ERROR, message: error.message });
}

}
};

const isAccountVerified = async (req: any, res: Response, next: NextFunction) => {
try {
Expand Down Expand Up @@ -69,5 +81,22 @@ const isAccountVerified = async (req: any, res: Response, next: NextFunction) =>
}
}

const verifyUserCredentials = async (req: Request, res: Response, next: NextFunction) => {
try {
const user: UsersAttributes = await authRepositories.findUserByAttributes("email", req.body.email);
if (!user) {
return res.status(httpStatus.BAD_REQUEST).json({ message: "Invalid Email or Password", data: null });
}
const passwordMatches = await comparePassword(req.body.password, user.password)
if (!passwordMatches) return res.status(httpStatus.BAD_REQUEST).json({ message: "Invalid Email or Password", data: null });
(req as IRequest).loginUserId = user.id;
return next();
} catch (error) {
res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ message: "Server error", data: error.message })
}

}



export { validation, isUserExist, isAccountVerified };
export { validation, isUserExist, isAccountVerified, verifyUserCredentials };
25 changes: 21 additions & 4 deletions src/modules/auth/controller/authControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@ import userRepositories from "../repository/authRepositories";
import { generateToken } from "../../../helpers";
import httpStatus from "http-status";
import { UsersAttributes } from "../../../databases/models/users";
import { IRequest } from "../../../types";

import authRepositories from "../repository/authRepositories";
import { sendVerificationEmail } from "../../../services/sendEmail";

const registerUser = async (req: Request, res: Response): Promise<void> => {
try {
const register: UsersAttributes = await userRepositories.createUser(req.body);
const token: string = generateToken(register.id);
const session = {userId: register.id, device: req.headers["user-device"] , token: token, otp: null };
const session = { userId: register.id, device: req.headers["user-device"], token: token, otp: null };
await authRepositories.createSession(session);
await sendVerificationEmail(register.email, "Verification Email", `${process.env.SERVER_URL_PRO}/api/auth/verify-email/${token}`);
res.status(httpStatus.CREATED).json({ message: "Account created successfully. Please check email to verify account.", data: register })
res.status(httpStatus.CREATED).json({ message: "Account created successfully. Please check email to verify account.", data: { user: register } })
} catch (error) {
res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ status: httpStatus.INTERNAL_SERVER_ERROR, message: error.message });
}

}

const sendVerifyEmail = async(req:any, res:Response) =>{
const sendVerifyEmail = async (req: any, res: Response) => {
try {
await sendVerificationEmail(req.user.email, "Verification Email", `${process.env.SERVER_URL_PRO}/api/auth/verify-email/${req.session.token}`);
res.status(httpStatus.OK).json({ status: httpStatus.OK, message: "Verification email sent successfully." });
Expand All @@ -40,4 +42,19 @@ const verifyEmail = async (req: any, res: Response) => {
}
}

export default { registerUser, sendVerifyEmail, verifyEmail }

const loginUser = async (req: Request, res: Response) => {
try {
const userId = (req as IRequest).loginUserId;
const token = generateToken(userId);
const session = { userId, device: req.headers["user-device"], token: token, otp: null };
await userRepositories.createSession(session);
res.status(httpStatus.OK).json({ message: "Logged in successfully", data: { token } });
}
catch (err) {
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ message: "Server error", data: err.message });
}
}


export default { registerUser, sendVerifyEmail, verifyEmail, loginUser }
3 changes: 2 additions & 1 deletion src/modules/auth/repository/authRepositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const createUser = async (body:any) =>{
return await Users.create(body)
}


const findUserByAttributes = async (key:string, value:any) =>{
return await Users.findOne({ where: { [key]: value} })
}
Expand All @@ -26,4 +27,4 @@ const destroySession = async (userId: number, token:string) =>{
return await Session.destroy({ where: {userId, token } });
}

export default {createUser, createSession, findUserByAttributes, destroySession, UpdateUserByAttributes, findSessionByUserId}
export default { createUser, createSession, findUserByAttributes, destroySession, UpdateUserByAttributes, findSessionByUserId }
Loading

0 comments on commit f8e8970

Please sign in to comment.