Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated #5506

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
8 changes: 7 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ const logger = require('morgan')
const cors = require('cors')

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

const app = express()

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

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

const path = require('path');
app.use('/avatars', express.static(path.join(__dirname, 'public/avatars')));


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

app.use((req, res) => {
res.status(404).json({ message: 'Not found' })
Expand Down
189 changes: 189 additions & 0 deletions controllers/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { v4: uuidv4 } = require('uuid');
const sgMail = require('@sendgrid/mail');
const gravatar = require('gravatar');
const Jimp = require('jimp');
const fs = require('fs/promises');
const path = require('path');
const User = require('../models/user');
require('dotenv').config();

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

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

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

const avatarURL = gravatar.url(email, { s: '250', d: 'identicon' });

const verificationToken = uuidv4();

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

const verificationLink = `http://localhost:3000/users/verify/${verificationToken}`;
const msg = {
to: email,
from: '[email protected]',
subject: 'Verify your email',
text: `Please verify your email by clicking on the following link: ${verificationLink}`,
html: `<a href="${verificationLink}">Verify your email</a>`,
};

await sgMail.send(msg);


res.status(201).json({
user: {
email: newUser.email,
subscription: newUser.subscription,
avatarURL: newUser.avatarURL,
},
message: 'User registered. Verification email sent.',
});
} catch (err) {
next(err);
}
};
const verifyEmail = async (req, res, next) => {
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 (err) {
next(err);
}
};

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

if (!email) {
return res.status(400).json({ message: 'Missing required field email' });
}

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 = `http://localhost:3000/users/verify/${user.verificationToken}`;

const msg = {
to: email,
from: '[email protected]',
subject: 'Verify your email',
text: `Please verify your email by clicking on the following link: ${verificationLink}`,
html: `<a href="${verificationLink}">Verify your email</a>`,
};

await sgMail.send(msg);

res.status(200).json({ message: 'Verification email sent' });
} catch (err) {
next(err);
}
};

const login = async (req, res, next) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !user.verify) {
return res.status(401).json({ message: 'Email not verified or does not exist' });
}

const match = await bcrypt.compare(password, user.password);
if (!match) {
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 (err) {
next(err);
}
};

const logout = async (req, res, next) => {
try {
const user = req.user;
user.token = null;
await user.save();
res.status(204).json();
} catch (err) {
next(err);
}
};

const currentUser = (req, res) => {
res.status(200).json({
email: req.user.email,
subscription: req.user.subscription,
avatarURL: req.user.avatarURL,
});
};

const updateAvatar = async (req, res) => {
if (!req.user) {
return res.status(401).json({ message: 'Not authorized' });
}

try {
const filePath = req.file.path;
const publicDir = path.join(__dirname, '..', 'public', 'avatars');
const fileName = `${req.user.id}-${req.file.filename}`;
const finalPath = path.join(publicDir, fileName);

const image = await Jimp.read(filePath);
await image.resize(250, 250).writeAsync(finalPath);

await fs.unlink(filePath);

const avatarURL = `/avatars/${fileName}`;
req.user.avatarURL = avatarURL;
await req.user.save();

res.json({ avatarURL });
} catch (error) {
res.status(500).json({ message: error.message });
}
};

module.exports = {
signup,
login,
logout,
currentUser,
verifyEmail,
resendVerificationEmail,
updateAvatar
};
33 changes: 33 additions & 0 deletions controllers/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const Jimp = require('jimp');
const fs = require('fs/promises');
const path = require('path');
const User = require('../models/user');
const upload = require('../middleware/uploads');

const updateAvatar = async (req, res) => {
try {
if (!req.user) {
return res.status(401).json({ message: 'Not authorized' });
}

const filePath = req.file.path;
const publicDir = path.join(__dirname, '..', 'public', 'avatars');
const fileName = `${req.user.id}-${req.file.filename}`;
const finalPath = path.join(publicDir, fileName);

const image = await Jimp.read(filePath);
await image.resize(250, 250).writeAsync(finalPath);

await fs.unlink(filePath);

const avatarURL = `/avatars/${fileName}`;
req.user.avatarURL = avatarURL;
await req.user.save();

res.json({ avatarURL });
} catch (error) {
res.status(500).json({ message: error.message });
}
};

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

const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log(`Database connection successful: ${conn.connection.host}`);
} catch (error) {
console.error(`Database connection error: ${error.message}`);
process.exit(1);
}
};

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

const auth = async (req, res, next) => {
const { authorization } = req.headers;

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

const token = authorization.replace('Bearer ', '');

try {

const { id } = jwt.verify(token, process.env.JWT_SECRET);

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

req.user = user;
next();
} catch (error) {
return res.status(401).json({ message: 'Not authorized' });
}
};

module.exports = auth;
25 changes: 25 additions & 0 deletions middleware/uploads.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const multer = require('multer');
const path = require('path');
const fs = require('fs');

const tmpDir = path.join(__dirname, '..', 'tmp', 'avatars');

fs.mkdir(tmpDir, { recursive: true }, (err) => {
if (err) {
console.error('Error creating tmp/avatars folder:', err);
}
});

const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, tmpDir);
},
filename: (req, file, cb) => {
const uniqueName = `${Date.now()}-${file.originalname}`;
cb(null, uniqueName);
},
});

const upload = multer({ storage });

module.exports = upload;
24 changes: 24 additions & 0 deletions models/contact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const mongoose = require('mongoose');

const contactSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Set name for contact'],
},
email: String,
phone: String,
favorite: {
type: Boolean,
default: false,

},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
});

const Contact = mongoose.model('Contact', contactSchema);

module.exports = Contact;
32 changes: 24 additions & 8 deletions models/contacts.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
// const fs = require('fs/promises')
const Contact = require('./contact');

const listContacts = async () => {}
const listContacts = async () => {
return await Contact.find();
};

const getContactById = async (contactId) => {}
const getContactById = async (id) => {
return await Contact.findById(id);
};

const removeContact = async (contactId) => {}
const addContact = async (body) => {
return await Contact.create(body);
};

const addContact = async (body) => {}
const removeContact = async (id) => {
return await Contact.findByIdAndDelete(id);
};

const updateContact = async (contactId, body) => {}
const updateContact = async (id, body) => {
return await Contact.findByIdAndUpdate(id, body, { new: true });
};

const updateStatusContact = async (id, { favorite }) => {
return await Contact.findByIdAndUpdate(id, { favorite }, { new: true });
};

module.exports = {
listContacts,
getContactById,
removeContact,
addContact,
removeContact,
updateContact,
}
updateStatusContact,
};

Loading