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
49 changes: 29 additions & 20 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
const express = require('express')
const logger = require('morgan')
const cors = require('cors')

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

const app = express()

const formatsLogger = app.get('env') === 'development' ? 'dev' : 'short'

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

app.use('/api/contacts', contactsRouter)
require("dotenv").config();

const express = require("express");
const logger = require("morgan");
const cors = require("cors");
const mongoose = require("mongoose");
const connectDB = require("./db");
const authRoutes = require("./routes/auth");
const path = require("path");
const usersRouter = require("./routes/api/users");
const contactsRouter = require("./routes/api/contacts");
const app = express();
const formatsLogger = app.get("env") === "development" ? "dev" : "short";

connectDB();

app.use(express.static(path.join(__dirname, "public")));
app.use(logger(formatsLogger));
app.use(cors());
app.use(express.json());

app.use("/api/auth", authRoutes);
app.use("/api/users", usersRouter);
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 })
})
res.status(500).json({ message: err.message });
});

module.exports = app
module.exports = app;
233 changes: 233 additions & 0 deletions controllers/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/user");
const gravatar = require("gravatar");
const uuid = require("uuid");
const sgMail = require("@sendgrid/mail");

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

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

const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(409).json({ message: "Email in use" });
}

const hashedPassword = await bcrypt.hash(password, 10);
const avatarURL = gravatar.url(email, { s: "250", d: "retro" }, true);

const verificationToken = uuid.v4();

const newUser = new User({
email,
password: hashedPassword,
subscription: "starter",
avatarURL,
verificationToken,
});

await newUser.save();

const verificationLink = `${process.env.BASE_URL}/api/users/verify/${verificationToken}`;
const msg = {
to: email,
from: process.env.SENDER_EMAIL,
subject: "Verify your email",
html: `<p>To verify your email, click the link below:</p>
<a href="${verificationLink}">Verify Email</a>`,
};

await sgMail.send(msg);

res.status(201).json({
user: {
email: newUser.email,
subscription: newUser.subscription,
avatarURL: newUser.avatarURL,
},
});
} catch (error) {
console.error("Error during user registration:", error);
res.status(500).json({ message: "Server error", error: error.message });
}
};

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

if (!email || !password) {
return res.status(400).json({ message: "Email and password are required" });
}

try {
console.log("Login attempt with email:", email);

const user = await User.findOne({ email });
console.log("User found:", user);

if (!user) {
console.log("No user found with that email.");
return res.status(401).json({ message: "Email or password is wrong" });
}

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

if (!isPasswordCorrect) {
return res.status(401).json({ message: "Email or password is wrong" });
}

const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
expiresIn: "1h",
});

user.token = token;
await user.save();

res.status(200).json({
token,
user: {
email: user.email,
subscription: user.subscription,
},
});
} catch (error) {
console.error("Error during login process:", error);
res.status(500).json({ message: error.message });
}
};

const logout = async (req, res) => {
try {
const userId = req.user._id;

const user = await User.findByIdAndUpdate(userId, { token: null });
if (!user) {
return res.status(401).json({ message: "Not authorized" });
}

res.status(204).send();
} catch (error) {
console.error("Error during logout:", error);
res.status(500).json({ message: "Server error" });
}
};

const getCurrent = async (req, res) => {
try {
const userId = req.user._id;

const user = await User.findById(userId);
if (!user) {
return res.status(401).json({ message: "Not authorized" });
}

res.status(200).json({
email: user.email,
subscription: user.subscription,
});
} catch (error) {
console.error("Error fetching current user:", error);
res.status(500).json({ message: "Server error" });
}
};

const updateSubscription = async (req, res) => {
const { subscription } = req.body;

const validSubscriptions = ["starter", "pro", "business"];
if (!subscription) {
return res.status(400).json({ message: "Subscription is required" });
}
if (!validSubscriptions.includes(subscription)) {
return res.status(400).json({ message: "Invalid subscription value" });
}

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

if (!updatedUser) {
return res.status(404).json({ message: "User not found" });
}

res.status(200).json({
email: updatedUser.email,
subscription: updatedUser.subscription,
});
} catch (error) {
return res.status(500).json({ message: "Server error" });
}
};

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

try {
const user = await User.findOne({ verificationToken });
if (!user) {
return res.status(404).json({ message: "User not found" });
}

user.verify = true;
user.verificationToken = null;
await user.save();

res.status(200).json({ message: "Verification successful" });
} catch (error) {
console.error("Error during email verification:", error);
res.status(500).json({ message: "Server error" });
}
};

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

if (!email) {
return res.status(400).json({ message: "Email is required" });
}

try {
const user = await User.findOne({ email });
if (!user) {
return res.status(404).json({ message: "User not found" });
}

if (user.verify) {
return res
.status(400)
.json({ message: "Verification has already been passed" });
}

const verificationLink = `${process.env.BASE_URL}/api/users/verify/${user.verificationToken}`;
const msg = {
to: email,
from: process.env.SENDER_EMAIL,
subject: "Verify your email",
html: `<p>To verify your email, click the link below:</p>
<a href="${verificationLink}">Verify Email</a>`,
};

await sgMail.send(msg);

res.status(200).json({ message: "Verification email sent" });
} catch (error) {
console.error("Error during resending verification email:", error);
res.status(500).json({ message: "Server error" });
}
};

module.exports = {
signup,
login,
updateSubscription,
logout,
getCurrent,
verifyEmail,
resendVerificationEmail,
};
49 changes: 49 additions & 0 deletions controllers/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const gravatar = require("gravatar");
const Jimp = require("jimp");
const path = require("path");
const fs = require("fs");
const User = require("../models/user");
const multer = require("multer");

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

const updateAvatar = async (req, res) => {
try {
const { path: tempPath, originalname } = req.file;
const { _id: userId } = req.user;
const newFileName = `${userId}_${originalname}`;
const resultPath = path.join(avatarsDir, newFileName);

const image = await Jimp.read(tempPath);
await image.resize(250, 250).writeAsync(resultPath);

await fs.promises.unlink(tempPath);

const avatarURL = `/avatars/${newFileName}`;
await User.findByIdAndUpdate(userId, { avatarURL });

res.status(200).json({ avatarURL });
} catch (error) {
console.error("Error updating avatar:", error);
res.status(500).json({ message: "Failed to update avatar" });
}
};

const verifyUser = async (req, res) => {
try {
const { verificationToken } = req.params;
const user = await User.findOne({ verificationToken });
if (!user) {
return res.status(404).json({ message: "User not found" });
}
user.verificationToken = null;
user.verify = true;
await user.save();
res.status(200).json({ message: "Verification successful" });
} catch (error) {
console.error("Error verifying user:", error);
res.status(500).json({ message: "Server error" });
}
};

module.exports = { updateAvatar, verifyUser };
14 changes: 14 additions & 0 deletions db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require("dotenv").config();
const mongoose = require("mongoose");

const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI);
console.log(`MongoDB connected: ${conn.connection.host}`);
} catch (error) {
console.error(`Error connecting to MongoDB: ${error.message}`);
process.exit(1);
}
};

module.exports = connectDB;
35 changes: 35 additions & 0 deletions middleware/authMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const jwt = require("jsonwebtoken");
const User = require("../models/user");

const authMiddleware = async (req, res, next) => {
const authHeader = req.headers.authorization;

if (!authHeader) {
return res.status(401).json({ message: "Not authorized" });
}

const [type, token] = authHeader.split(" ");

if (type !== "Bearer" || !token) {
return res.status(401).json({ message: "Not authorized" });
}

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);

const user = await User.findById(decoded.id);
if (!user || user.token !== token) {
return res.status(401).json({ message: "Not authorized" });
}

req.user = user;
next();
} catch (error) {
if (error.name === "TokenExpiredError") {
return res.status(401).json({ message: "Token expired" });
}
return res.status(401).json({ message: "Not authorized" });
}
};

module.exports = authMiddleware;
Loading