Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PORT=
DB_HOST=
SECRET_KEY=
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')
const express = require("express");
const logger = require("morgan");
const cors = require("cors");
require("dotenv").config();

const contactsRouter = require('./routes/api/contacts')
const authRouter = require("./routes/api/auth");
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(express.static('public'));

app.use(logger(formatsLogger))
app.use(cors())
app.use(express.json())

app.use('/api/contacts', contactsRouter)
app.use("/api/auth", authRouter,);
app.use("/api/contacts", contactsRouter);

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

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

module.exports = app
res.status(status).json({ message });
});

module.exports = app;
125 changes: 125 additions & 0 deletions controller/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
const bcrypt = require('bcryptjs');
const jwt = require("jsonwebtoken");
const gravatar = require('gravatar');
const path = require('path');
const fs = require('fs/promises');
const jimp = require("jimp");
require("dotenv").config();
const { SECRET_KEY } = process.env;

const avatarsDir = path.join(__dirname, '../', 'public', 'avatars');

const { User } = require("../models/user");
const { HttpError, ctrlWrapper } = require("../helpers");

const register = async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user) throw HttpError(409, "Email already in use");

const avatarURL = gravatar.url(email);
const hashPassword = await bcrypt.hash(password, 10);

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

res.status(201).json({
user: {
email: newUser.email,
subscription: newUser.subscription,
avatarURL: newUser.avatarURL,
},
});
};

const login = async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });

if (!user) {
throw HttpError(401, "Email or password invalid");
}

const passwordCompare = await bcrypt.compare(password, user.password);

if (!passwordCompare) {
throw HttpError(401, "Email or password invalid");
}

const payload = {
id: user._id,
};

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

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

// const decodeToken = jwt.decode(token);
// console.log(decodeToken);

// try {
// const { id } = jwt.verify(token, SECRET_KEY);
// console.log(id);
// } catch (error) {
// console.log(error.message);
// }

// res.json({
// token,
// });

res.json({
token: token,
user: {
email,
subscription: "starter",
},
});
};

const getCurrent = async (req, res) => {
const { email, name } = req.user;
res.json({ email, name });
};

const logout = async (req, res, next) => {
const { _id } = req.user;
await User.findByIdAndUpdate(_id, { token: "" });

res.json({ message: "Logout seccess" });
};


const updateAvater = async ( req, res, next) => {
const { file } = req;
const {_id} = req.user;
const {mimetype} = req.file;

if(mimetype !== 'image/jpeg'){
throw HttpError(403, "File has wrong format");
}

const {path: tempUpload, originalname} = req.file;
const filename = `${_id}_${originalname}`;

const image = await jimp.read(file.path);
image.resize(250, 250);
await image.writeAsync(file.path);

const resultUpload = path.join(avatarsDir, filename);

await fs.rename(tempUpload, resultUpload);
const avatarURL = path.join('avatars', filename);
await User.findByIdAndUpdate(_id, {avatarURL})

res.json({
avatarURL,
})
}

module.exports = {
register: ctrlWrapper(register),
login: ctrlWrapper(login),
getCurrent: ctrlWrapper(getCurrent),
logout: ctrlWrapper(logout),
updateAvater: ctrlWrapper(updateAvater),
};
80 changes: 80 additions & 0 deletions controller/contact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const { Contact } = require("../models/contacts");
const { HttpError, ctrlWrapper } = require("../helpers");

const listContacts = async (req, res, next) => {
const { _id: owner } = req.user;

try {
const result = await Contact.find({ owner }).populate("owner", "name email");
res.json(result);
} catch (err) {
next(err);
}
};

const getContactById = async (req, res, next) => {
const { contactId } = req.params;

try {
const result = await Contact.findById(contactId);
if (!result) {
throw HttpError(404, "Not found");
}
res.status(200).json({ status: "success", data: result });
} catch (err) {
next(err);
}
};

const addContact = async (req, res, next) => {
const { email } = req.body;
const { _id: owner } = req.user;

try {
const isContactExist = await Contact.findOne({ email });
if (isContactExist) {
return res.status(400).json({ message: "Email is already taken" });
}

const result = await Contact.create({ ...req.body, owner });
res.status(201).json(result);
} catch (err) {
next(err);
}
};

const removeContact = async (req, res, next) => {
const { contactId } = req.params;

try {
const result = await Contact.findByIdAndRemove(contactId);
if (!result) {
throw HttpError(404, "Not found");
}
res.status(200).json({ message: "Contact deleted." });
} catch (err) {
next(err);
}
};

const updateStatusContact = async (req, res, next) => {
const { contactId } = req.params;

try {
const result = await Contact.findByIdAndUpdate(contactId, req.body, { new: true });
if (!result) {
throw HttpError(404, "Not found");
}
res.json(result);
} catch (err) {
next(err);
}
};

module.exports = {
listContacts: ctrlWrapper(listContacts),
getContactById: ctrlWrapper(getContactById),
addContact: ctrlWrapper(addContact),
removeContact: ctrlWrapper(removeContact),
updateStatusContact: ctrlWrapper(updateStatusContact),
};
15 changes: 15 additions & 0 deletions helpers/HttpErrors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const errorMessageList = {
400: "Bad Requesr",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
409: "Conflict",
};

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

module.exports = HttpError;
13 changes: 13 additions & 0 deletions helpers/ctrlWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const ctrlWrapper = (ctrl) => {
const func = async (req, res, next) => {
try {
await ctrl(req, res, next);
} catch (error) {
next(error);
}
};

return func;
};

module.exports = ctrlWrapper;
7 changes: 7 additions & 0 deletions helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const HttpError = require("./HttpErrors");
const ctrlWrapper = require("./ctrlWrapper");

module.exports = {
HttpError,
ctrlWrapper,
};
28 changes: 28 additions & 0 deletions middlewares/authenticate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const jwt = require("jsonwebtoken");

const { User } = require("../models/user");
const { HttpError } = require("../helpers");
const { SECRET_KEY } = process.env;

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

if (bearer !== "Bearer") {
next(HttpError(401));
}
try {
const { id } = jwt.verify(token, SECRET_KEY);
const user = await User.findById(id);

if (!user || !user.token || user.token !== token) {
next(HttpError(401));
}
req.user = user;
next();
} catch (error) {
next(HttpError(401));
}
};

module.exports = authenticate;
8 changes: 8 additions & 0 deletions middlewares/handleMongooseError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const handleMongooseError = (error, data, next) => {
const { name, code } = error;
const status = (name === "MongoServerError" && code === 11000) ? 409 : 400;
error.status = status;
next();
};

module.exports = handleMongooseError;
7 changes: 7 additions & 0 deletions middlewares/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const isValidId = require("./isValidId");
const validateBody = require("./validateBody");
const handleMongooseError = require("./handleMongooseError");
const authenticate = require("./authenticate");
const upload = require('./upload');

module.exports = { isValidId, validateBody, handleMongooseError, authenticate, upload };
13 changes: 13 additions & 0 deletions middlewares/isValidId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { isValidObjectId } = require("mongoose");
const HttpError = require("../helpers/HttpErrors");

const isValidId = (req, res, next) => {
const { contactId } = req.params;

if (!isValidObjectId(contactId.slice(1))) {
next(HttpError(400, `${contactId.slice(1)} is not valid id`));
}
next();
};

module.exports = isValidId;
20 changes: 20 additions & 0 deletions middlewares/upload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const multer = require('multer');
const path = require('path');

const tempDir = path.join(__dirname, '../', 'temp');

const multerCongig = multer.diskStorage({
destination: tempDir,
filename: (req, file, cb) => {
cb(null, file.originalname);
},
limits: {
fileSize: 1048576,
}
});

const upload = multer({
storage: multerCongig,
})

module.exports = upload;
Loading