Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 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
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
36 changes: 21 additions & 15 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 from "./routes/api/contactsRouter.js"; // Import your routes correctly
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())
// Use proper route for contacts API
app.use("/api/contacts", router);

app.use('/api/contacts', contactsRouter)
// http://localhost:3000/api/users
app.use("/api/users", usersRouter);

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

// Central error handler
app.use((err, req, res, next) => {
res.status(500).json({ message: err.message })
})
console.error(err.stack);
res.status(500).json({ message: err.message });
});

module.exports = app
export { app };
94 changes: 94 additions & 0 deletions controllers/contactsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
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) => {
const { error } = contactValidation.validate(req.body);

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

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) => {
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) => {
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};
113 changes: 113 additions & 0 deletions controllers/usersController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import "dotenv/config";
import { User } from "../models/usersModel.js";
// prettier-ignore
import { signupValidation, subscriptionValidation } from "../validations/validation.js";
import { httpError } from "../helpers/httpError.js";

const { SECRET_KEY } = 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);

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

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

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,
});
};

// prettier-ignore
export { signupUser, loginUser, logoutUser, getCurrentUsers, updateUserSubscription };
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 };
15 changes: 15 additions & 0 deletions helpers/httpError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const messages = {
400: "Bad request",
401: "Unauthorized",
403: "Forbidden",
404: "Not found",
409: "Conflict",
};

const httpError = (status, message = messages[status]) => {
const error = new Error(message);
error.status = status;
return error;
};

export { httpError };
Binary file added images/CURRENTUSER.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/FILTER_FAVORITE.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/LOGIN_autherror.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/LOGIN_success.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/LOGIN_successmongodb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/LOGOUT.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/LOGOUT_mongodb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/PAGINATION.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/REGISTRATION.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/REGISTRATION_mongodb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/UPDATE_SUBSCRIPTION.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/UPDATE_SUBSCRIPTION_mongodb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions middlewares/authenticateToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import jwt from "jsonwebtoken";
import { User } from "../models/usersModel.js";
import { httpError } from "../helpers/httpError.js";
import "dotenv/config";
const { SECRET_KEY } = process.env;

const authenticateToken = async (req, _res, next) => {
const { authorization = "" } = req.headers;
const [bearer, token] = authorization.split(" ");

if (bearer !== "Bearer") {
next(httpError(401, "Not authorized"));
}

try {
const { id } = jwt.verify(token, SECRET_KEY);
const user = await User.findById(id);

if (!user || user.token !== token || !user.token) {
next(httpError(401, "Not authorized"));
}

req.user = user;
next();
} catch {
next(httpError(401, "Not authorized"));
}
};

export { authenticateToken };
19 changes: 0 additions & 19 deletions models/contacts.js

This file was deleted.

62 changes: 0 additions & 62 deletions models/contacts.json

This file was deleted.

Loading