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

Hw06 email #107

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
69c1c27
const morgan = require('morgan');
madalinagomei Sep 30, 2024
a148b61
add joi
madalinagomei Sep 30, 2024
a472435
ch
madalinagomei Sep 30, 2024
78fb20e
edit hw
madalinagomei Oct 6, 2024
a5d68a6
Completed homework 02 - Express API for contacts
madalinagomei Oct 7, 2024
c798f14
Merge pull request #1 from madalinagomei/hw02-express
madalinagomei Oct 7, 2024
032fa41
add
madalinagomei Oct 8, 2024
e973b5c
add
madalinagomei Oct 8, 2024
f357a48
del
madalinagomei Oct 8, 2024
842ecb8
add mongodb
madalinagomei Oct 8, 2024
2125229
solve errors
madalinagomei Oct 9, 2024
e2796dc
Merge pull request #2 from madalinagomei/hw03
madalinagomei Oct 9, 2024
bcf0088
1 commit
madalinagomei Oct 16, 2024
3b0e2b1
Merge pull request #3 from madalinagomei/hw04-auth
madalinagomei Oct 16, 2024
ba891fb
add hw
madalinagomei Oct 16, 2024
adb8214
change
madalinagomei Oct 17, 2024
6747b4a
Merge pull request #4 from madalinagomei/hw04-auth
madalinagomei Oct 17, 2024
3a6a6cd
add
madalinagomei Oct 23, 2024
95a4b78
import gravatar
madalinagomei Oct 23, 2024
7a4b346
Merge pull request #5 from madalinagomei/hw05-avatars
madalinagomei Oct 23, 2024
5eb34ed
add hw6
madalinagomei Nov 2, 2024
a658db0
fix error on port 3001 instead 3000
madalinagomei Nov 3, 2024
50eed2f
Resolved conflict in app.js
madalinagomei Nov 4, 2024
1ac064e
Resolved conflicts in routes/api/users.js
madalinagomei Nov 4, 2024
fe3e83b
Resolved conflicts in package.json
madalinagomei Nov 4, 2024
278b2b3
Resolved conflicts in package-lock.json
madalinagomei Nov 4, 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
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}
4 changes: 2 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ module.exports = {
es2021: true,
node: true,
},
extends: ['standard', 'prettier'],
extends: ["standard", "prettier"],
parserOptions: {
ecmaVersion: 12,
},
rules: {},
}
};
45 changes: 30 additions & 15 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
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 path from "path";
import { fileURLToPath } from "url";
import contactsRouter from "./routes/api/contacts.js";
import usersRouter from "./routes/api/users.js";
import auth from "./middlewares/auth.middleware.js"; // Middleware for authentication

const contactsRouter = require('./routes/api/contacts')
// Use __dirname in ES module (for compatibility with ES modules)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const app = express()
const app = express();

const formatsLogger = app.get('env') === 'development' ? 'dev' : 'short'
// Set logger format based on environment
const formatsLogger = app.get("env") === "development" ? "dev" : "short";

app.use(logger(formatsLogger))
app.use(cors())
app.use(express.json())
// Serve static files from the 'public/avatars' folder
app.use("/avatars", express.static(path.join(__dirname, "public/avatars")));

app.use('/api/contacts', contactsRouter)
app.use(logger(formatsLogger)); // HTTP request logger
app.use(cors()); // Enable CORS for security
app.use(express.json()); // Middleware to parse JSON payloads

// Routes for contacts and users
app.use("/api/contacts", auth, contactsRouter); // Protected with authentication middleware
app.use("/api/users", usersRouter); // Endpoint for user-related operations

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

// Error handling middleware
app.use((err, req, res, next) => {
res.status(500).json({ message: err.message })
})
res.status(500).json({ message: err.message });
});

module.exports = app
export default app;
28 changes: 28 additions & 0 deletions controllers/contacts.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import express from "express";
import Contact from "../models/contact.model.js"; // Importăm modelul Contact
import auth from "../middlewares/auth.js"; // Middleware de autentificare

const router = express.Router();

// Crearea unui nou contact (ruta POST /contacts)
router.post("/", auth, async (req, res, next) => {
try {
const { name, email, phone, favorite } = req.body;

// Crearea unui nou contact cu proprietatea `owner` setată la ID-ul utilizatorului autenticat
const newContact = new Contact({
name,
email,
phone,
favorite,
owner: req.user._id, // ID-ul utilizatorului din middleware-ul de autentificare
});

await newContact.save();
res.status(201).json(newContact); // Returnăm noul contact creat
} catch (error) {
next(error); // În caz de eroare, transmitem eroarea mai departe
}
});

export default router;
21 changes: 21 additions & 0 deletions emailService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import nodemailer from "nodemailer";

// Configure the transporter with Gmail settings
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL_USER, // Your Gmail address from .env
pass: process.env.EMAIL_PASS, // Your Gmail app password from .env
},
});

// Function to send verification email
export const sendVerificationEmail = async (userEmail, verificationToken) => {
const mailOptions = {
from: process.env.EMAIL_USER,
to: userEmail,
subject: "Verify your email",
text: `Click here to verify: http://localhost:3000/users/verify/${verificationToken}`,
};
await transporter.sendMail(mailOptions);
};
25 changes: 25 additions & 0 deletions middlewares/auth.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import jwt from "jsonwebtoken";
import User from "../models/user.model.js";

const auth = async (req, res, next) => {
try {
const token = req.headers.authorization?.split(" ")[1]; // Extrage token-ul
if (!token) {
return res.status(401).json({ message: "Not authorized" });
}

const decoded = jwt.verify(token, process.env.JWT_SECRET); // Verifică token-ul

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

req.user = user; // Adaugă utilizatorul la req
next();
} catch (error) {
res.status(401).json({ message: "Not authorized" });
}
};

export default auth;
94 changes: 81 additions & 13 deletions models/contacts.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,87 @@
// const fs = require('fs/promises')
import { error } from "console";
import e from "express";
import fs from "fs/promises";
import { nanoid } from "nanoid";

const listContacts = async () => {}
const contactsPath = "./models/contacts.json";

const getContactById = async (contactId) => {}
export const listContacts = async () => {
try {
const data = await fs.readFile(contactsPath, "utf8");
return JSON.parse(data);
} catch (error) {
console.log(error.message);
throw error;
}
};

const removeContact = async (contactId) => {}
export const getContactById = async (contactId) => {
try {
const data = await fs.readFile(contactsPath, "utf8");
const contacts = JSON.parse(data);
const contact = contacts.find((contact) => contact.id === contactId);
return contact;
} catch (error) {
console.log(error.message);
throw error;
}
};

const addContact = async (body) => {}
export const addContact = async (body) => {
try {
const data = await fs.readFile(contactsPath, "utf8");
const contacts = JSON.parse(data);
const newContact = {
id: nanoid(),
...body,
};
contacts.push(newContact);
await fs.writeFile(contactsPath, JSON.stringify(contacts, null, 2));
return newContact;
} catch (error) {
console.log(error.message);
throw error;
}
};

const updateContact = async (contactId, body) => {}
export const removeContact = async (contactId) => {
try {
const data = await fs.readFile(contactsPath, "utf8");
const contacts = JSON.parse(data);
const deleteContact = contacts.find((contact) => contact.id === contactId);
if (!deleteContact) {
return null;
}
const newContacts = contacts.filter((contact) => contact.id !== contactId);
await fs.writeFile(contactsPath, JSON.stringify(newContacts, null, 2));
return deleteContact;
} catch (error) {
console.log(error.message);
throw error;
}
};

module.exports = {
listContacts,
getContactById,
removeContact,
addContact,
updateContact,
}
export const updateContact = async (contactId, body) => {
try {
const data = await fs.readFile(contactsPath, "utf8");
const contacts = JSON.parse(data);
const contactIndex = contacts.findIndex(
(contact) => contact.id === contactId
);
if (contactIndex === -1) {
throw new Error("Contact not found");
}
const updatedContact = {
id: contactId,
name: body.name || contacts[contactIndex].name,
email: body.email || contacts[contactIndex].email,
phone: body.phone || contacts[contactIndex].phone,
};
contacts[contactIndex] = updatedContact;
await fs.writeFile(contactsPath, JSON.stringify(contacts, null, 2));
return updatedContact;
} catch (error) {
console.log(error.message);
throw error;
}
};
18 changes: 9 additions & 9 deletions models/contacts.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
[
{
"id": "AeHIrLTr6JkxGE6SN-0Rw",
"name": "Allen Raymond",
"email": "[email protected]",
"phone": "(992) 914-3792"
},
{
"id": "qdggE76Jtbfd9eWJHrssH",
"name": "Chaim Lewis",
Expand Down Expand Up @@ -37,9 +31,9 @@
},
{
"id": "Z5sbDlS7pCzNsnAHLtDJd",
"name": "Reuben Henry",
"name": "Reuben Hennry",
"email": "[email protected]",
"phone": "(715) 598-5792"
"phone": "5355792"
},
{
"id": "C9sjBfCo4UJCWjzBnOtxl",
Expand All @@ -58,5 +52,11 @@
"name": "Alec Howard",
"email": "[email protected]",
"phone": "(748) 206-2688"
},
{
"id": "NReHEq3o_vDrcrJVcTEDs",
"name": "Mada Gomei",
"email": "[email protected]",
"phone": "4567890"
}
]
]
28 changes: 28 additions & 0 deletions models/contacts.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import mongoose from "mongoose";
const { Schema } = mongoose;

const contactSchema = new Schema({
name: {
type: String,
required: [true, "Set name for contact"],
},
email: {
type: String,
},
phone: {
type: String,
},
favorite: {
type: Boolean,
default: false,
},
owner: {
type: Schema.Types.ObjectId, // Stoceaza ID-ul utilizatorului care a creat contactul
ref: "User", // Referință la modelul 'User'
required: true,
},
});

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

export default Contact;
54 changes: 54 additions & 0 deletions models/user.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import mongoose from "mongoose";
import bcrypt from "bcryptjs";
import gravatar from "gravatar";

const { Schema } = mongoose;

const userSchema = new Schema({
email: {
type: String,
required: [true, "Email is required"],
unique: true,
},
password: {
type: String,
required: [true, "Password is required"],
},
subscription: {
type: String,
enum: ["starter", "pro", "business"],
default: "starter",
},
token: {
type: String,
default: null,
},
avatarURL: {
type: String, // Adaugă câmpul avatarURL pentru a stoca URL-ul avatarului
},
});

// Criptare parola înainte de a salva
userSchema.pre("save", async function (next) {
if (this.isModified("password")) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});

// Setarea avatarului cu gravatar înainte de a salva
userSchema.pre("save", function (next) {
if (!this.avatarURL) {
this.avatarURL = gravatar.url(this.email, { s: "250", d: "retro" }, true);
}
next();
});

// Metodă pentru a compara parola introdusă cu cea criptată
userSchema.methods.comparePassword = function (password) {
return bcrypt.compare(password, this.password);
};

const User = mongoose.model("User", userSchema);

export default User;
Loading