Skip to content

Commit b45120b

Browse files
committed
feat: added new branch for 6th homework hw06-email. feat: added PATCH method for verify email
1 parent 6b70393 commit b45120b

File tree

9 files changed

+160
-3
lines changed

9 files changed

+160
-3
lines changed

controllers/auth-controller.js

+56-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HttpError } from "../helpers/index.js";
1+
import { HttpError, sendEmail } from "../helpers/index.js";
22
import { ctrlWrapper } from "../decorators/index.js";
33
import User from "../models/Users.js";
44
import bcrypt from "bcrypt";
@@ -8,10 +8,11 @@ import gravatar from "gravatar";
88
import fs from "fs/promises";
99
import path from "path";
1010
import Jimp from "jimp";
11+
import { v4 as uuid4 } from "uuid";
1112

1213
const avatarPath = path.resolve("public", "avatars");
1314

14-
const { JWT_SECRET } = process.env;
15+
const { JWT_SECRET, BASE_URL } = process.env;
1516

1617
const signUp = async (req, res) => {
1718
const { password, email } = req.body;
@@ -22,12 +23,23 @@ const signUp = async (req, res) => {
2223
}
2324
const hashPassword = await bcrypt.hash(password, 10);
2425
const avatarURL = gravatar.url(email, { s: "200", r: "pg", d: "mm" });
26+
const verificationCode = uuid4();
2527
const newUser = await User.create({
2628
email,
2729
password: hashPassword,
2830
avatarURL,
31+
verificationCode,
2932
});
3033

34+
const verifyEmail = {
35+
to: email,
36+
subject: "Verify Email",
37+
html: `<a target="_blank" href="http://${BASE_URL}/api/auth/${verificationCode}">Click to verify email</a>`,
38+
// html: `<a target="_blank" href="http://HOST/api/auth/${verificationCode}">Click to verify email</a>`,
39+
};
40+
41+
await sendEmail(verifyEmail);
42+
3143
res.status(201).json({
3244
message:
3345
"Registration successful. You can now sign in with your new account.",
@@ -47,6 +59,10 @@ const signIn = async (req, res) => {
4759
throw HttpError(401, "Incorrect login or password");
4860
}
4961

62+
if (!user.verify) {
63+
throw HttpError(401, "Email not verify");
64+
}
65+
5066
const passwordCompare = await bcrypt.compare(password, user.password);
5167
if (!passwordCompare) {
5268
throw HttpError(401, "Incorrect login or password");
@@ -118,11 +134,49 @@ const updateAvatar = async (req, res) => {
118134
});
119135
};
120136

137+
const verify = async (req, res) => {
138+
const { verificationCode } = req.params;
139+
const user = await User.findOne({ verificationCode });
140+
if (!user) {
141+
throw HttpError(400, "Email not found or already verified");
142+
}
143+
144+
await User.findByIdAndUpdate(user._id, {
145+
verify: true,
146+
verificationCode: "",
147+
});
148+
res.json({ message: "Email verify success" });
149+
};
150+
151+
const resendVerifyEmail = async (req, res) => {
152+
const { email } = req.body;
153+
const user = await User.findOne({ email });
154+
if (!user) {
155+
throw HttpError(404, "Email not found");
156+
}
157+
if (user.verify) {
158+
throw HttpError(400, "Email already verified");
159+
}
160+
161+
const verifyEmail = {
162+
to: email,
163+
subject: "Verify Email",
164+
html: `<a target="_blank" href="http://${BASE_URL}/api/auth/${verificationCode}">Click to verify email</a>`,
165+
// html: `<a target="_blank" href="http://HOST/api/auth/${verificationCode}">Click to verify email</a>`,
166+
};
167+
168+
await sendEmail(verifyEmail);
169+
170+
res.json({ message: "Verify email send success" });
171+
};
172+
121173
export default {
122174
signUp: ctrlWrapper(signUp),
123175
signIn: ctrlWrapper(signIn),
124176
getCurrent: ctrlWrapper(getCurrent),
125177
logOut: ctrlWrapper(logOut),
126178
updateSubscription: ctrlWrapper(updateSubscription),
127179
updateAvatar: ctrlWrapper(updateAvatar),
180+
verify: ctrlWrapper(verify),
181+
resendVerifyEmail: ctrlWrapper(resendVerifyEmail),
128182
};

helpers/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { HttpError } from "./HttpError.js";
2+
export { sendEmail } from "./sendEmail.js";

helpers/sendEmail.js

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import nodemailer from "nodemailer";
2+
import "dotenv/config";
3+
4+
const { MAIL_API_KEY, MAIL_FROM } = process.env;
5+
6+
const nodemailerConfig = {
7+
host: "smtp.ukr.net",
8+
port: 465,
9+
secure: true,
10+
auth: {
11+
user: MAIL_API_KEY,
12+
pass: MAIL_FROM,
13+
},
14+
};
15+
16+
const transport = nodemailer.createTransport(nodemailerConfig);
17+
18+
// const data = {
19+
// from: MAIL_API_KEY,
20+
// to: "email address for who",
21+
// subject: "topic of the letter",
22+
// html: "<stron>Hi!</stron>",
23+
// };
24+
25+
// transport
26+
// .sendMail(email)
27+
// .then(() => {
28+
// console.log("Email sent!");
29+
// })
30+
// .catch((err) => {
31+
// console.log(err.message);
32+
// });
33+
34+
const sendEmail = (data) => {
35+
const email = { ...data, from: MAIL_FROM };
36+
return transport.sendEmail(email);
37+
};
38+
export { sendEmail };

lala.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import nodemailer from "nodemailer";
2+
import "dotenv/config";
3+
4+
const { MAIL_API_KEY, MAIL_FROM } = process.env;
5+
6+
const nodemailerConfig = {
7+
host: "smtp.ukr.net",
8+
port: 465,
9+
secure: true,
10+
auth: {
11+
user: MAIL_API_KEY,
12+
pass: MAIL_FROM,
13+
},
14+
};
15+
16+
const transport = nodemailer.createTransport(nodemailerConfig);
17+
18+
const email = {
19+
from: MAIL_API_KEY,
20+
to: "email address for who",
21+
subject: "topic of the letter",
22+
html: "<stron>Hi!</stron>",
23+
};
24+
25+
transport
26+
.sendMail(email)
27+
.then(() => {
28+
console.log("Email sent!");
29+
})
30+
.catch((err) => {
31+
console.log(err.message);
32+
});

models/Users.js

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ const userSchema = new Schema(
2626
token: {
2727
type: String,
2828
},
29+
verify: {
30+
type: Boolean,
31+
default: false,
32+
},
33+
verificationToken: {
34+
type: String,
35+
// required: [true, "Verify token is required"],
36+
},
2937
},
3038
{ versionKey: false, timestamps: true }
3139
);

package-lock.json

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"mongoose": "^8.0.3",
3737
"morgan": "^1.10.0",
3838
"multer": "^1.4.5-lts.1",
39+
"nodemailer": "^6.9.8",
3940
"uuid": "^9.0.1"
4041
},
4142
"devDependencies": {

routes/api/auth-router.js

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { isEmptyBody, authenticate, upload } from "../../middleware/index.js";
55
import {
66
userAuthSchema,
77
updateSubscriptionSchema,
8+
verificationEmailSchema,
89
} from "../../schemas/auth-schema.js";
910

1011
const authRouter = express.Router();
@@ -16,6 +17,15 @@ authRouter.post(
1617
authController.signUp
1718
);
1819

20+
authRouter.get("/verify/:verificationCode", authController.verify);
21+
22+
authRouter.post(
23+
"/verify",
24+
isEmptyBody,
25+
validateBody(verificationEmailSchema),
26+
authController.resendVerifyEmail
27+
);
28+
1929
authRouter.post(
2030
"/signin",
2131
isEmptyBody,

schemas/auth-schema.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ const updateSubscriptionSchema = Joi.object({
2323
subscription: Joi.string().required(),
2424
});
2525

26-
export { userAuthSchema, updateSubscriptionSchema };
26+
const verificationEmailSchema = Joi.object({
27+
email: Joi.string().required(),
28+
});
29+
30+
export { userAuthSchema, updateSubscriptionSchema, verificationEmailSchema };

0 commit comments

Comments
 (0)