Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
DB_HOST =
PORT =
SENDGRID_API_KEY=
PORT =
SENDGRID_EMAIL=
SECRET_KEY=
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM node

WORKDIR /app

COPY . .

RUN npm install

EXPOSE 3000

CMD ["node", "server"]
94 changes: 94 additions & 0 deletions __test__/login.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const mongoose = require("mongoose");
const request = require("supertest");
const app = require("../app");
require("dotenv").config();

const { DB_HOST, PORT = 3000 } = process.env;

describe("test login controller", () => {
let server;
beforeAll(() => {
mongoose
.connect(DB_HOST)
.then(() => {
server = app.listen(PORT);
})
.catch((err) => {
console.log(err.message);
process.exit(1);
});
});

afterAll((done) => {
mongoose.disconnect(done);
server.close();
});

test("login success", async () => {
const {
status,
body: {
data: { user, token },
},
} = await request(app)
.post("/api/auth/login")
.set("Content-type", "application/json")
.send({
email: "mama@gmail.com",
password: "123456",
});

expect(status).toBe(200);
expect(typeof user).toBe("object");
expect(typeof user.name).toBe("string");
expect(typeof user.email).toBe("string");
expect(typeof user.subscription).toBe("string");
expect(typeof token).toBe("string");
});

test("login without email", async () => {
const {
status,
body: { message },
} = await request(app)
.post("/api/auth/login")
.set("Content-type", "application/json")
.send({
password: "123456",
});

expect(status).toBe(400);
expect(message).toBe('"email" is required');
});

test("login without password", async () => {
const {
status,
body: { message },
} = await request(app)
.post("/api/auth/login")
.set("Content-type", "application/json")
.send({
email: "mama@gmail.com",
});

expect(status).toBe(400);
expect(message).toBe('"password" is required');
});

test("login with incorrect password", async () => {
const {
status,
body: { message },
} = await request(app)
.post("/api/auth/login")
.set("Content-type", "application/json")
.send({
email: "mama@gmail.com",
password: "123123",
});

expect(status).toBe(401);
expect(message).toBe("Email or password is incorrect or not verified");
});
});
97 changes: 97 additions & 0 deletions __test__/register.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const mongoose = require("mongoose");
const request = require("supertest");
const app = require("../app");
require("dotenv").config();

const { DB_HOST } = process.env;

describe("test register controller", () => {
let server;
beforeAll(() => {
mongoose
.connect(DB_HOST)
.then(() => {
server = app.listen(3001);
})
.catch((err) => {
console.log(err.message);
process.exit(1);
});
});

afterAll((done) => {
mongoose.disconnect(done);
server.close();
});

test("register success", async () => {
const {
status,
body: {
data: { user, token },
},
} = await request(app)
.post("/api/auth/register")
.set("Content-type", "application/json")
.send({
name: "Mamam",
email: "mamam@gmail.com",
password: "123456",
});

expect(status).toBe(201);

expect(typeof user).toBe("object");
expect(typeof user.name).toBe("string");
expect(typeof user.email).toBe("string");
expect(typeof token).toBe("string");
});

test("register without name", async () => {
const {
status,
body: { message },
} = await request(app)
.post("/api/auth/register")
.set("Content-type", "application/json")
.send({
email: "mamam@gmail.com",
password: "123456",
});

expect(status).toBe(400);
expect(message).toBe('"name" is required');
});

test("register without password", async () => {
const {
status,
body: { message },
} = await request(app)
.post("/api/auth/register")
.set("Content-type", "application/json")
.send({
email: "mamam@gmail.com",
name: "Mamam",
});

expect(status).toBe(400);
expect(message).toBe('"password" is required');
});

test("register without email", async () => {
const {
status,
body: { message },
} = await request(app)
.post("/api/auth/register")
.set("Content-type", "application/json")
.send({
name: "Mamam",
password: "123456",
});

expect(status).toBe(400);
expect(message).toBe('"email" is required');
});
});
5 changes: 5 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const cors = require("cors");
require("dotenv").config();

const contactsRouter = require("./routes/api/contactsRouter");
const authRouter = require("./routes/api/authRouts");
const usersRouter = require("./routes/api/usersRouts");

const app = express();

Expand All @@ -12,7 +14,10 @@ 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("/api/users", usersRouter);
app.use("/api/auth", authRouter);
app.use("/api/contacts", contactsRouter);

app.use((req, res) => {
Expand Down
13 changes: 13 additions & 0 deletions controllers/auth/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const register = require("./register");
const login = require("./login");
const logout = require("./logout");
const verifyEmail = require("./verifyEmail");
const resendVerifyEmail = require("./resendVerifyEmail");

module.exports = {
register,
login,
logout,
verifyEmail,
resendVerifyEmail,
};
35 changes: 35 additions & 0 deletions controllers/auth/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const { User } = require("../../models/user");

const { createToken, customError } = require("../../helpers");

const login = async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
const pass = await user?.verifyPassword(password);
if (!user || !pass || !user.verify) {
throw customError("Email or password is incorrect or not verified", 401);
}

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

const token = createToken(payload);
await User.findByIdAndUpdate(user._id, { token });

res.json({
response: "success",
status: 200,
data: {
user: {
name: user.name,
email,
avatarURL: user.avatarURL,
subscription: user.subscription,
},
token,
},
});
};

module.exports = login;
9 changes: 9 additions & 0 deletions controllers/auth/logout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { User } = require("../../models/user");

const logout = async (req, res) => {
const { _id } = req.user;
await User.findByIdAndUpdate(_id, { token: null });
res.status(204).json();
};

module.exports = logout;
46 changes: 46 additions & 0 deletions controllers/auth/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const { User } = require("../../models/user");
const gravatar = require("gravatar");
const { nanoid } = require("nanoid");

const { createToken, sendMail } = require("../../helpers");

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

const verificationToken = nanoid();

const newUser = new User({ name, email, avatarURL, verificationToken });

await newUser.setPassword(password);

const mail = {
to: email,
subject: " Email`s verify",
html: `<a target='_blank' href='http://localhost:3000/api/auth/verify/${verificationToken}' >Go to verify email</a>`,
};
await sendMail(mail);

const payload = {
id: newUser._id,
};
const token = createToken(payload);
newUser.token = token;

await newUser.save();

res.status(201).json({
response: "success",
status: 201,
data: {
user: {
name,
email,
avatarURL,
},
token,
},
});
};

module.exports = register;
26 changes: 26 additions & 0 deletions controllers/auth/resendVerifyEmail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { User } = require("../../models/user");
const { sendMail, customError } = require("../../helpers");

const resendVerifyEmail = async (req, res) => {
const { email } = req.body;
const user = User.findOne({ email });
if (!user) {
const error = new Error("User not faund");
error.status = 404;
throw error;
}
if (user.verify) {
throw customError("Verification has already been passed", 400);
}
const mail = {
to: email,
subject: " Email`s verify",
html: `<a target='_blank' href='http://localhost:3000/api/auth/verify/${user.verificationToken}' >Go to verify email</a>`,
};
await sendMail(mail);
res.json({
message: "Verification email sent",
});
};

module.exports = resendVerifyEmail;
19 changes: 19 additions & 0 deletions controllers/auth/verifyEmail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const { User } = require("../../models/user");
const { customError } = require("../../helpers");

const verifyEmail = async (req, res) => {
const { verificationToken } = req.params;
const user = await User.findOne({ verificationToken });
if (!user) {
throw customError("Not found verification token", 404);
}
await User.findByIdAndUpdate(user._id, {
verify: true,
verificationToken: null,
});
res.json({
message: "Verification successful",
});
};

module.exports = verifyEmail;
3 changes: 2 additions & 1 deletion controllers/contacts/add.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const { Contact } = require("../../models/contact");

const add = async (req, res) => {
const { _id } = req.user;
const { body } = req;
const addedContact = await Contact.create(body);
const addedContact = await Contact.create({ ...body, owner: _id });
res.status(201).json({
status: "success",
code: 201,
Expand Down
Loading