Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
8a3a971
installed dotenv
valeeriiiee Sep 23, 2024
81fd8e8
installed mongoose
valeeriiiee Sep 23, 2024
9902643
refactor using ESM
valeeriiiee Sep 23, 2024
31f5cc4
Update package.json
valeeriiiee Sep 23, 2024
987e68b
Update server.js
valeeriiiee Sep 23, 2024
4006531
refactor code using ESM
valeeriiiee Sep 23, 2024
44d650b
refactor code using ESM
valeeriiiee Sep 23, 2024
c71a5d4
Update app.js
valeeriiiee Sep 23, 2024
e15faf2
Update server.js
valeeriiiee Sep 23, 2024
8bb7765
Create httpError.js
valeeriiiee Sep 23, 2024
6ba6427
Delete contacts.js
valeeriiiee Sep 23, 2024
9f103bf
Delete contacts.json
valeeriiiee Sep 23, 2024
5ea2d65
Create contactsModel.js
valeeriiiee Sep 23, 2024
04050ba
renamed contacts.js to contactsRouter.js
valeeriiiee Sep 23, 2024
47d0c65
Update app.js
valeeriiiee Sep 23, 2024
a36da85
Update contactsRouter.js
valeeriiiee Sep 23, 2024
00dc02e
Update contactsModel.js
valeeriiiee Sep 23, 2024
019021b
Create contactsController.js
valeeriiiee Sep 24, 2024
4d8dac4
Create ctrlWrapper.js
valeeriiiee Sep 24, 2024
26d083f
Create validation.js
valeeriiiee Sep 24, 2024
8052bdc
installed joi
valeeriiiee Sep 24, 2024
ea10067
Update contactsRouter.js
valeeriiiee Sep 24, 2024
0e22633
Update server.js
valeeriiiee Sep 24, 2024
14295fd
Update app.js
valeeriiiee Sep 24, 2024
864e79e
added images of testing
valeeriiiee Sep 24, 2024
a0f5ad6
delete images
valeeriiiee Sep 29, 2024
203c594
installed packages
valeeriiiee Sep 29, 2024
111106e
Update app.js
valeeriiiee Sep 29, 2024
80e8015
Create usersRouter.js
valeeriiiee Sep 29, 2024
ca72ca4
Create usersModel.js
valeeriiiee Sep 29, 2024
0de9d21
Create authenticateToken.js
valeeriiiee Sep 29, 2024
53e5a01
Update validation.js
valeeriiiee Sep 29, 2024
21654b3
Create usersController.js
valeeriiiee Sep 29, 2024
bff0015
added test images
valeeriiiee Sep 29, 2024
66ddda7
add authentication to contacts router
valeeriiiee Sep 29, 2024
b788ac5
Update contactsController.js
valeeriiiee Sep 29, 2024
0d31fb8
pagination and favorites filter
valeeriiiee Sep 29, 2024
51a4f6c
added test images for pagination and favorites filter
valeeriiiee Sep 29, 2024
50ad5ae
delete images
valeeriiiee Sep 29, 2024
c793f55
added avatar image
valeeriiiee Sep 29, 2024
444d2eb
Create loginUser.test.js
valeeriiiee Sep 29, 2024
b6bff20
Update server.js
valeeriiiee Sep 29, 2024
5b91696
Update contactsController.js
valeeriiiee Sep 29, 2024
c8eb015
Update usersController.js
valeeriiiee Sep 29, 2024
a5f0e39
Create upload.js
valeeriiiee Sep 29, 2024
55b2dd1
Update contactsModel.js
valeeriiiee Sep 29, 2024
2a9b73f
Update usersModel.js
valeeriiiee Sep 29, 2024
bd03a42
Update package-lock.json
valeeriiiee Sep 29, 2024
6d6cc68
Update package.json
valeeriiiee Sep 29, 2024
b75bd3b
Update contactsRouter.js
valeeriiiee Sep 29, 2024
dfd8db5
Update usersRouter.js
valeeriiiee Sep 29, 2024
325a12f
Update app.js
valeeriiiee Sep 29, 2024
ce2408d
Create .gitkeep
valeeriiiee Sep 30, 2024
423c319
Update contactsRouter.js
valeeriiiee Sep 30, 2024
f9568e8
Create jasper.jpg
valeeriiiee Sep 30, 2024
ab02b4c
Delete 0d1c02ee48d48f5797cb54f15d1d6c34.jpg
valeeriiiee Sep 30, 2024
3f63f4c
Create .gitkeep
valeeriiiee Sep 30, 2024
f5d98e6
Update usersController.js
valeeriiiee Sep 30, 2024
43415ea
Update package.json
valeeriiiee Sep 30, 2024
7a79514
Update loginUser.test.js
valeeriiiee Sep 30, 2024
22d529a
uninstalled bcrypt
valeeriiiee Sep 30, 2024
6e775fb
install bcryptjs
valeeriiiee Sep 30, 2024
fcc343c
change import to bcryptjs
valeeriiiee Sep 30, 2024
fb4761d
installed nodemailer and uuid
valeeriiiee Sep 30, 2024
d1d432e
Create sendEmail.js
valeeriiiee Sep 30, 2024
a440299
Update usersModel.js
valeeriiiee Sep 30, 2024
0d6129c
Update validation.js
valeeriiiee Sep 30, 2024
db7679d
Update usersController.js
valeeriiiee Sep 30, 2024
4adad38
Update usersRouter.js
valeeriiiee Sep 30, 2024
4073453
Create Dockerfile
valeeriiiee Sep 30, 2024
d066aea
added test images
valeeriiiee Sep 30, 2024
244db4d
Create .dockerignore
valeeriiiee Sep 30, 2024
b6f4d29
Update package.json
valeeriiiee Sep 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
.env
.git
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# What enviorment are we creating
FROM node:18

# Where is all the code going to lie in this enviroment
WORKDIR /app

# What is needed for this enviroment to run properly and where are we storing it in our folder
COPY package*.json ./

# What command is needed to run for this app to work
RUN npm install

# What code am I copying, and where is it going (first . is copy everything, second . is put it in the root of the directory)
COPY . .

# Send over any .env variables to the enviroment
ENV PORT=3000

# What Port is our enviroment listening for
EXPOSE 3000

# How do we start the app
CMD ["npm", "run", "start:dev" ]
40 changes: 23 additions & 17 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
const express = require('express')
const logger = require('morgan')
const cors = require('cors')
import express from "express";
import logger from "morgan";
import cors from "cors";
import { router as contactsRouter } from "./routes/api/contactsRouter.js";
import { router as usersRouter } from "./routes/api/usersRouter.js";

const contactsRouter = require('./routes/api/contacts')
const app = express();

const app = express()
const formatsLogger = app.get("env") === "development" ? "dev" : "short";

const formatsLogger = app.get('env') === 'development' ? 'dev' : 'short'
app.use(logger(formatsLogger));
app.use(cors());
app.use(express.json());

app.use(logger(formatsLogger))
app.use(cors())
app.use(express.json())
// tells Express to serve static files from the public directory)
// open http://localhost:3000/avatars/665c98dca10f7f28dc9eb8b2.jpeg on browser
app.use(express.static("public"));

app.use('/api/contacts', contactsRouter)
app.use("/api/contacts", contactsRouter);
app.use("/api/users", usersRouter);

app.use((req, res) => {
res.status(404).json({ message: 'Not found' })
})
app.use((_req, res) => {
res.status(404).json({ message: "Not found" });
});

app.use((err, req, res, next) => {
res.status(500).json({ message: err.message })
})
app.use((err, _req, res, _next) => {
const { status = 500, message = "Server error" } = err;
res.status(status).json({ message });
});

module.exports = app
export { app };
93 changes: 93 additions & 0 deletions controllers/contactsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Contact } from "../models/contactsModel.js";
// prettier-ignore
import { contactValidation, favoriteValidation } from "../validations/validation.js";
import { httpError } from "../helpers/httpError.js";

const getAllContacts = async (req, res) => {
const { page = 1, limit = 20, favorite } = req.query;
const query = favorite ? { favorite: true } : {};

const result = await Contact.find(query)
.skip((page - 1) * limit)
.limit(parseInt(limit));

res.json(result);
};

const getContactById = async (req, res) => {
const { contactId } = req.params;
const result = await Contact.findById(contactId);

if (!result) {
throw httpError(404, "Contact ID Not Found");
}

res.json(result);
};

const addContact = async (req, res) => {
// Preventing lack of necessary data for contacts (check validations folder)
const { error } = contactValidation.validate(req.body);

if (error) {
throw httpError(400, "missing required fields");
}

const result = await Contact.create(req.body);

res.status(201).json(result);
};

const deleteContactById = async (req, res) => {
const { contactId } = req.params;
const result = await Contact.findByIdAndDelete(contactId);

if (!result) {
throw httpError(404);
}

res.json({
message: "Contact deleted",
});
};

const updateContactById = async (req, res) => {
// Preventing lack of necessary data for contacts (check validations folder)
const { error } = contactValidation.validate(req.body);
if (error) {
throw httpError(400, "missing fields");
}

const { contactId } = req.params;
const result = await Contact.findByIdAndUpdate(contactId, req.body, {
new: true,
});

if (!result) {
throw httpError(404);
}

res.json(result);
};

const updateStatusContact = async (req, res) => {
// Preventing lack of necessary data for favorite (check validations folder)
const { error } = favoriteValidation.validate(req.body);
if (error) {
throw httpError(400, "missing field favorite");
}

const { contactId } = req.params;
const result = await Contact.findByIdAndUpdate(contactId, req.body, {
new: true,
});

if (!result) {
throw httpError(404);
}

res.json(result);
};

// prettier-ignore
export { getAllContacts, getContactById, addContact, deleteContactById, updateContactById, updateStatusContact};
212 changes: 212 additions & 0 deletions controllers/usersController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import bcrypt from "bcryptjs";
import gravatar from "gravatar";
import jwt from "jsonwebtoken";
import "dotenv/config";
import * as Jimp from "jimp";
import path from "path";
import fs from "fs/promises";
import { User } from "../models/usersModel.js";
// prettier-ignore
import { emailValidation, signupValidation, subscriptionValidation } from "../validations/validation.js";
import { httpError } from "../helpers/httpError.js";
import { sendEmail } from "../helpers/sendEmail.js";
import { v4 as uuid4 } from "uuid";

const { SECRET_KEY, PORT } = process.env;

const signupUser = async (req, res) => {
const { email, password } = req.body;

// Registration validation error
const { error } = signupValidation.validate(req.body);
if (error) {
throw httpError(400, error.message);
}

// Registration conflict error
const user = await User.findOne({ email });
if (user) {
throw httpError(409, "Email in Use");
}

const hashPassword = await bcrypt.hash(password, 10);

// Create a link to the user's avatar with gravatar
const avatarURL = gravatar.url(email, { protocol: "http" });

// Create a verificationToken for the user
const verificationToken = uuid4();

const newUser = await User.create({
email,
password: hashPassword,
avatarURL,
verificationToken,
});

// Send an email to the user's mail and specify a link to verify the email (/users/verify/:verificationToken) in the message
await sendEmail({
to: email,
subject: "Action Required: Verify Your Email",
html: `<a target="_blank" href="http://localhost:${PORT}/api/users/verify/${verificationToken}">Click to verify email</a>`,
});

// Registration success response
res.status(201).json({
user: {
email: newUser.email,
subscription: newUser.subscription,
avatarURL: newUser.avatarURL,
verificationToken,
},
});
};

const loginUser = async (req, res) => {
const { email, password } = req.body;

// Login validation error
const { error } = signupValidation.validate(req.body);
if (error) {
throw httpError(401, error.message);
}

// Login auth error (email)
const user = await User.findOne({ email });
if (!user) {
throw httpError(401, "Email or password is wrong");
}

// Login auth error (password)
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw httpError(401, "Email or password is wrong");
}

const payload = { id: user._id };
const token = jwt.sign(payload, SECRET_KEY, { expiresIn: "23h" });

await User.findByIdAndUpdate(user._id, { token });

// Login success response
res.status(200).json({
token: token,
user: {
email: user.email,
subscription: user.subscription,
},
});
};

const logoutUser = async (req, res) => {
const { _id } = req.user;

// Logout unauthorized error (setting token to empty string will remove token -> will logout)
await User.findByIdAndUpdate(_id, { token: "" });

// Logout success response
res.status(204).send();
};

const getCurrentUsers = async (req, res) => {
const { email, subscription } = req.user;

res.json({
email,
subscription,
});
};

const updateUserSubscription = async (req, res) => {
const { error } = subscriptionValidation.validate(req.body);
if (error) {
throw httpError(400, error.message);
}

const { _id } = req.user;

const updatedUser = await User.findByIdAndUpdate(_id, req.body, {
new: true,
});

res.json({
email: updatedUser.email,
subscription: updatedUser.subscription,
});
};

const updateAvatar = async (req, res) => {
const { _id } = req.user;
const { path: oldPath, originalname } = req.file;

await Jimp.read(oldPath).then((image) =>
// image.resize(250, 250).write(oldPath)
image.cover(250, 250).write(oldPath)
);

const extension = path.extname(originalname);
const filename = `${_id}${extension}`;

const newPath = path.join("public", "avatars", filename);
await fs.rename(oldPath, newPath);

let avatarURL = path.join("/avatars", filename);
avatarURL = avatarURL.replace(/\\/g, "/");

await User.findByIdAndUpdate(_id, { avatarURL });
res.status(200).json({ avatarURL });
};

const verifyEmail = async (req, res) => {
const { verificationToken } = req.params;

const user = await User.findOne({ verificationToken });

// Verification user Not Found
if (!user) {
throw httpError(400, "User not found");
}

await User.findByIdAndUpdate(user._id, {
verify: true,
verificationToken: null,
});

// Verification success response
res.json({
message: "Verification successful",
});
};

const resendVerifyEmail = async (req, res) => {
const { email } = req.body;

// Resending a email validation error
const { error } = emailValidation.validate(req.body);
if (error) {
throw httpError(400, error.message);
}

const user = await User.findOne({ email });

if (!user) {
throw httpError(404, "The provided email address could not be found");
}

// Resend email for verified user
if (user.verify) {
throw httpError(400, "Verification has already been passed");
}

await sendEmail({
to: email,
subject: "Action Required: Verify Your Email",
html: `<a target="_blank" href="http://localhost:${PORT}/api/users/verify/${user.verificationToken}">Click to verify email</a>`,
});

// Resending a email success response
res.json({ message: "Verification email sent" });
};

// prettier-ignore
export { signupUser, loginUser, logoutUser, getCurrentUsers, updateUserSubscription, updateAvatar, verifyEmail, resendVerifyEmail};
12 changes: 12 additions & 0 deletions helpers/ctrlWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const ctrlWrapper = (ctrl) => {
const func = async (req, res, next) => {
try {
await ctrl(req, res, next);
} catch (error) {
next(error);
}
};
return func;
};

export { ctrlWrapper };
Loading