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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
node_modules/
.env
*.env
.idea
.vscode
1 change: 1 addition & 0 deletions United
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mongodb+srv://silvia198070:[email protected]/
51 changes: 36 additions & 15 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
const express = require('express')
const logger = require('morgan')
const cors = require('cors')
const express = require("express");
const logger = require("morgan");
const cors = require("cors");

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

const app = express()
const { connectToDb } = require("./utils/connectToDb");
const authenticate = require("./middlewares/authMiddleware");

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

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

app.use('/api/contacts', contactsRouter)
connectToDb()
.then(() => {
console.log("Database connection successful");
})
.catch((err) => {
console.error("Database connection error:", err.message);
process.exit(1);
});

app.use(express.static("public"));
app.use(logger(formatsLogger));
app.use(cors());
app.use(express.json());

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

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

const PORT = process.env.PORT || 4000;

app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

module.exports = app
module.exports = app;
204 changes: 204 additions & 0 deletions controllers/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
require("dotenv").config();
const { nanoid } = require("nanoid");
const sendVerificationEmail = require("../utils/sendVerificationEmail.js");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const gravatar = require("gravatar");
const Jimp = require("jimp");
const fs = require("fs");
const path = require("path");
const User = require("../models/users.js");

const JWT_SECRET = "secret_key_for_jwt";

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

if (!email || !password) {
return res
.status(400)
.json({ message: "Missing required fields: email or password" });
}

try {
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: "retro" }, true);

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

const verificationToken = nanoid();

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

await sendVerificationEmail(email, verificationToken);

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

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

if (!email || !password) {
return res
.status(400)
.json({ message: "Missing required fields: email or password" });
}

try {
const user = await User.findOne({ email });
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(404).json({ message: "User not found" });
}
if (!user.verify) {
return res.status(403).json({ message: "Email not verified" });
}

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

res.json({
token,
user: {
email: user.email,
subscription: user.subscription,
},
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};

const logout = async (req, res) => {
try {
const user = req.user;
user.token = null;
await user.save();
res.status(204).send();
} catch (error) {
res.status(500).json({ message: error.message });
}
};

const getCurrentUser = async (req, res) => {
try {
const user = req.user;

res.json({
email: user.email,
subscription: user.subscription,
avatarURL: user.avatarURL,
});
} catch (error) {
res.status(500).json({ message: error.message });
}
};

const updateAvatar = async (req, res) => {
const { file } = req;
const { user } = req;
console.log("File details:", file);

if (!file) {
return res.status(400).json({ message: "No file uploaded" });
}

try {
const tmpPath = file.path;
console.log("Temp path:", tmpPath);
const avatarsDir = path.join(__dirname, "../public/avatars");

await fs.mkdir(avatarsDir, { recursive: true });

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

const newFileName = `${Date.now()}-${file.originalname}`;
const newPath = path.join(avatarsDir, newFileName);

await fs.rename(tmpPath, newPath);

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

res.status(200).json({
message: "Avatar saved successfully",
avatarURL,
});
} catch (error) {
console.error(error.message);
res.status(500).json({ message: error.message });
}
};

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: "Email verified successfully" });
} catch (error) {
res.status(500).json({ message: error.message });
}
};

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

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

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: "Email already verified" });
}
const verificationToken = nanoid();
user.verificationToken = verificationToken;
await user.save();
await sendVerificationEmail(email, verificationToken);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
module.exports = {
register,
login,
logout,
getCurrentUser,
updateAvatar,
verifyEmail,
resendVerificationEmail,
};
29 changes: 29 additions & 0 deletions controllers/contactsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const Contact = require("../models/contact");

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

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

const removeContact = async (contactId) => {
return await Contact.findByIdAndDelete(contactId);
};

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

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

module.exports = {
listContacts,
getContactById,
removeContact,
addContact,
updateContact,
};
37 changes: 37 additions & 0 deletions middlewares/authMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const jwt = require("jsonwebtoken");

const User = require("../models/users.js");

const JWT_SECRET = process.env.JWT_SECRET || "secret_key_for_jwt";

const authMiddleware = async (req, res, next) => {
const authHeader = req.headers.authorization || "";
const token = authHeader.split(" ")[1];

if (!authHeader) {
return res.status(401).json({ message: "Token not provided" });
}

if (!token) {
return res.status(401).json({ message: "Token not provided" });
}

try {
const { id } = jwt.verify(token, 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 (err) {
if (err.name === "TokenExpiredError") {
return res.status(401).json({ message: "Token expired" });
}
res.status(401).json({ message: "Not authorized" });
}
};

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

const tmpDir = path.join(__dirname, "../tmp");


if (!fs.existsSync(tmpDir)) {
fs.mkdirSync(tmpDir, { recursive: true });
}



const upload = multer({
dest: tmpDir,
fileFilter: (req, file, cb) => {
console.log("File", file);
cb(null, req);
}
});


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

const contactsSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
phone: { type: String, required: true },
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
});

const Contact = mongoose.model("Contact", contactsSchema);

module.exports = Contact;
Loading