Skip to content

Commit 175f35a

Browse files
author
Matheus Ishiyama
committed
create register user workflow
1 parent 56e9aa7 commit 175f35a

File tree

17 files changed

+1384
-17
lines changed

17 files changed

+1384
-17
lines changed

package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
{
22
"name": "chat-app-backend",
3-
"version": "1.0.0",
3+
"version": "0.0.0",
44
"description": "",
55
"main": "src/index.js",
66
"scripts": {
77
"dev": "nodemon src/index.js",
88
"start": "node src/index.js",
99
"test": "jest"
1010
},
11+
"jest": {
12+
"testEnvironment": "node"
13+
},
1114
"repository": {
1215
"type": "git",
1316
"url": "git+https://github.com/MatheusIshiyama/chat-app-backend.git"
@@ -20,9 +23,13 @@
2023
},
2124
"homepage": "https://github.com/MatheusIshiyama/chat-app-backend#readme",
2225
"dependencies": {
26+
"bcrypt": "^5.0.1",
27+
"crypto": "^1.0.1",
2328
"dotenv": "^8.2.0",
29+
"email-validator": "^2.0.4",
2430
"express": "^4.17.1",
25-
"mongoose": "^5.12.3"
31+
"mongoose": "^5.12.3",
32+
"nodemailer": "^6.5.0"
2633
},
2734
"devDependencies": {
2835
"jest": "^26.6.3",

src/controllers/user.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const crypto = require("crypto");
2+
const User = require("../models/user");
3+
const sendMail = require("../services/sendMail");
4+
const password = require("../services/password");
5+
6+
const UserController = {
7+
async register(req, res) {
8+
const body = req.body;
9+
const hashPassword = password.hash(body.password);
10+
const verifyCode = crypto.randomBytes(128).toString("base64");
11+
const newUser = new User({
12+
username: body.username.toLowerCase(),
13+
name: body.name,
14+
email: body.email,
15+
verifyCode,
16+
verified: false,
17+
password: hashPassword,
18+
createdAt: Date.now(),
19+
});
20+
await newUser.save();
21+
22+
const user = await User.findOne(
23+
{ username: body.username },
24+
{ _id: false, password: false }
25+
);
26+
27+
await sendMail(body.email, verifyCode, "Confirm register");
28+
29+
return res.status(201).json({ message: user });
30+
},
31+
32+
async confirm(req, res) {
33+
const { verifyCode } = req.params;
34+
35+
await User.findOneAndUpdate(
36+
{ verifyCode },
37+
{ verifyCode: "verified", verified: true }
38+
);
39+
40+
return res.status(200).json({ message: "User confirmed" });
41+
},
42+
};
43+
44+
module.exports = UserController;

src/database/config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const mongoose = require("mongoose");
2+
require("dotenv").config();
3+
4+
const database = mongoose;
5+
6+
database
7+
.connect(process.env.MONGO_URI, {
8+
useCreateIndex: true,
9+
useUnifiedTopology: true,
10+
useNewUrlParser: true,
11+
useFindAndModify: false,
12+
})
13+
.then(() => console.log("[DATABASE] Connected"));
14+
15+
module.exports = database;

src/middlewares/validateCode.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = (req, res, next) => {
2+
if (!req.params.verifyCode)
3+
return res.status(400).json({ message: "No verifyCode provided" });
4+
5+
if (req.params.verifyCode.length !== 172)
6+
return res.status(400).json({ message: "No verifyCode length accept" });
7+
8+
next();
9+
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const User = require("../models/user");
2+
const EmailValidator = require("email-validator");
3+
const validateBody = require("../services/validateBody");
4+
5+
module.exports = async (req, res, next) => {
6+
if (!req.body) return res.status(400).json({ message: "No body provided" });
7+
const { username, name, email, password, checkPassword } = req.body;
8+
9+
//* validate body
10+
if (!validateBody.username(username)) {
11+
return res
12+
.status(400)
13+
.json({ message: "No username provided or no length accept" });
14+
}
15+
16+
if (!validateBody.name(name))
17+
return res
18+
.status(400)
19+
.json({ message: "No name provided or no length accept" });
20+
21+
if (!EmailValidator.validate(email))
22+
return res
23+
.status(400)
24+
.json({ message: "No email provided or invalid format" });
25+
26+
if (!validateBody.password(password))
27+
return res
28+
.status(400)
29+
.json({ message: "No password provided or doesn't match params" });
30+
31+
if (!validateBody.checkPassword(password, checkPassword))
32+
return res.status(400).json({
33+
message: "No checkPassword provided or passwords doesn't match",
34+
});
35+
36+
//* verify if user exists
37+
const userExists = await User.findOne({ username });
38+
const emailExists = await User.findOne({ email });
39+
40+
if (userExists || emailExists)
41+
return res.status(400).json({ message: "Username or Email already exists"});
42+
43+
next();
44+
};

src/models/user.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const database = require("../database/config");
2+
3+
const User = new database.Schema(
4+
{
5+
username: String,
6+
name: String,
7+
email: String,
8+
verifyCode: String,
9+
verified: Boolean,
10+
password: String,
11+
createdAt: Date,
12+
},
13+
{
14+
versionKey: false,
15+
}
16+
);
17+
18+
module.exports = new database.model("User", User);

src/routes/user.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
const express = require("express");
22
const userRoutes = express.Router();
33

4-
userRoutes.get('/', (req, res) => {
5-
res.sendStatus(200);
6-
})
4+
//* controller
5+
const UserController = require("../controllers/user");
6+
7+
//* middlewares
8+
const validateRegister = require("../middlewares/validateRegister");
9+
const validateCode = require("../middlewares/validateCode");
10+
11+
userRoutes.post("/register", validateRegister, (req, res) => {
12+
UserController.register(req, res);
13+
});
14+
15+
userRoutes.get("/confirm/:verifyCode", validateCode, (req, res) => {
16+
UserController.confirm(req, res);
17+
});
718

819
module.exports = userRoutes;

src/services/password.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const bcrypt = require("bcrypt");
2+
3+
const password = {
4+
hash(password) {
5+
const hashedPassword = bcrypt.hashSync(password, 10);
6+
return hashedPassword;
7+
},
8+
validate(password, hashPassword) {
9+
const result = bcrypt.compareSync(password, hashPassword);
10+
return result;
11+
},
12+
};
13+
14+
module.exports = password;

src/services/sendMail.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const nodeMailer = require("nodemailer");
2+
const validateBody = require("./validateBody");
3+
require("dotenv").config();
4+
5+
const transporter = nodeMailer.createTransport({
6+
service: "gmail",
7+
auth: {
8+
user: process.env.EMAIL,
9+
pass: process.env.PASSWORD,
10+
},
11+
});
12+
13+
const sendEmail = async (email, verifyCode, subject) => {
14+
if (!email) return "no email provided";
15+
if (!verifyCode) return "no verifyCode provided";
16+
if (!subject) return "no subject provided";
17+
18+
const isValid = validateBody.email(email);
19+
if (!isValid) return "invalid email type";
20+
21+
const mailOptions = {
22+
from: process.env.EMAIL,
23+
to: email,
24+
subject,
25+
text: `${process.env.CONFIRM_URL}/${verifyCode}`,
26+
};
27+
28+
await transporter.sendMail(mailOptions);
29+
30+
return "email sent";
31+
};
32+
33+
module.exports = sendEmail;

src/services/validateBody.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const EmailValidator = require("email-validator");
2+
3+
const validateBody = {
4+
username(username) {
5+
if (!username) return false;
6+
const minMaxLength = /^[\s\S]{4,16}$/;
7+
return minMaxLength.test(username);
8+
},
9+
name(name) {
10+
if (!name) return false;
11+
const minMaxLength = /^[\s\S]{3,16}$/;
12+
return minMaxLength.test(name);
13+
},
14+
email(email) {
15+
if (!email) return false;
16+
return EmailValidator.validate(email);
17+
},
18+
password(password) {
19+
if (!password) return false;
20+
const minMaxLength = /^[\s\S]{8,16}$/,
21+
upper = /[A-Z]/,
22+
lower = /[a-z]/,
23+
number = /[0-9]/,
24+
special = /[ !"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/;
25+
26+
return (
27+
minMaxLength.test(password) &&
28+
upper.test(password) &&
29+
lower.test(password) &&
30+
number.test(password) &&
31+
special.test(password)
32+
);
33+
},
34+
checkPassword(password, checkPassword) {
35+
if (!password || !checkPassword) return false;
36+
if (password !== checkPassword) return false;
37+
return true;
38+
},
39+
};
40+
41+
module.exports = validateBody;

0 commit comments

Comments
 (0)