From 75432f2bcf87d0b0bf60d8c4b883ee87419815b3 Mon Sep 17 00:00:00 2001 From: Hoie Kim Date: Sun, 27 Sep 2020 12:53:32 +0000 Subject: [PATCH 01/12] added temp database creating feature --- database/postgres/pg.js | 7 ++--- database/postgres/pg.test.js | 12 ++++----- lib/users.js | 40 ++++++++++++++++------------- sequelize/db.js | 3 +-- src/routes/userRoutes.js | 46 ++++++++++++++++++++------------- src/server.js | 2 ++ views/postgres.ejs | 50 +++++++++++++++++++++++++++++++----- 7 files changed, 106 insertions(+), 54 deletions(-) diff --git a/database/postgres/pg.js b/database/postgres/pg.js index 87666a1..337bf40 100644 --- a/database/postgres/pg.js +++ b/database/postgres/pg.js @@ -20,8 +20,9 @@ pgModule.closePGDB = () => { return client.end(); }; -pgModule.createPgAccount = async (username, password) => { - if (!username || !password) return; +pgModule.createPgAccount = async (user) => { + const { username, dbPassword } = user; + if (!username || !dbPassword) return; try { // Could not escape user input by using $1 $2 // https://github.com/brianc/node-postgres/issues/539 @@ -30,7 +31,7 @@ pgModule.createPgAccount = async (username, password) => { const sqlQuery2 = escape( `create user %s with encrypted password %Q`, username, - password + dbPassword ); const sqlQuery3 = escape( diff --git a/database/postgres/pg.test.js b/database/postgres/pg.test.js index 096bc24..0391f44 100644 --- a/database/postgres/pg.test.js +++ b/database/postgres/pg.test.js @@ -47,7 +47,8 @@ describe("Test PG DB", () => { }); describe("Test createPgAccount", () => { it("it should execute all queries if required arguments are passed into createPgAccount", async () => { - await createPgAccount("username", "password"); + const user = { username: "username", dbPassword: "password" }; + await createPgAccount(user); expect(mockClient.query).toHaveBeenCalledTimes(3); expect(mockClient.query.mock.calls[0]).toEqual([ `CREATE DATABASE username;`, @@ -65,16 +66,15 @@ describe("Test PG DB", () => { ]); }); it("it should not execute any queries in createPgAccount if required arguments are not passed in", async () => { - await createPgAccount(); + const user = {}; + await createPgAccount(user); expect(mockClient.query).toHaveBeenCalledTimes(0); }); it("it should check if logger.error is called at throw of createPgAccount", async () => { try { await mockClient.query.mockReturnValue(Promise.reject()); - const resCreatePgAccount = await createPgAccount( - "username", - "password" - ); + const user = { username: "username", dbPassword: "password" }; + const resCreatePgAccount = await createPgAccount(user); expect(resCreatePgAccount).rejects.toThrow(); } catch (err) { expect(logger.error).toHaveBeenCalledTimes(1); diff --git a/lib/users.js b/lib/users.js index 742f344..bf0d2b2 100644 --- a/lib/users.js +++ b/lib/users.js @@ -14,7 +14,7 @@ const { const users = {}; const schema = yup.object().shape({ - email: yup.string().required().email(), + email: yup.string().email(), }); users.sendPasswordResetEmail = async (userAccount) => { @@ -38,35 +38,39 @@ users.sendPasswordResetEmail = async (userAccount) => { }; users.signUp = async (userInfo) => { - try { - await schema.validate(userInfo); - } catch (err) { - throw new Error(err); - } - const { email } = userInfo; const { Accounts } = db.getModels(); - const userAccount = await Accounts.findOne({ - where: { - email: email, - }, - }); + if (email) { + try { + await schema.validate(userInfo); + } catch (err) { + throw new Error(err); + } + + const userAccount = await Accounts.findOne({ + where: { + email: email, + }, + }); + if (userAccount) { + logger.info("this account already exists", email); + throw new Error("this account already exists"); + } + } const username = uniqueNamesGenerator({ length: 2, - dictionaries: [adjectives, colors, animals], + dictionaries: [adjectives, animals], separator: "", }); const dbPassword = genPw(); - if (userAccount) { - logger.info("this account already exists", email); - throw new Error("this account already exists"); - } const newAccount = await Accounts.create({ email: email, username: username, dbPassword: dbPassword, }); - await users.sendPasswordResetEmail(newAccount); + if (email) { + await users.sendPasswordResetEmail(newAccount); + } return newAccount; }; diff --git a/sequelize/db.js b/sequelize/db.js index d903bf6..521e4cb 100644 --- a/sequelize/db.js +++ b/sequelize/db.js @@ -28,7 +28,6 @@ dbModule.start = async () => { username: { type: DataTypes.STRING, unique: true, - allowNull: true, }, password: { type: DataTypes.STRING, @@ -36,7 +35,7 @@ dbModule.start = async () => { email: { type: DataTypes.STRING, unique: true, - allowNull: false, + allowNull: true, }, dbPassword: { type: DataTypes.STRING, diff --git a/src/routes/userRoutes.js b/src/routes/userRoutes.js index d9c4600..c3d41d7 100644 --- a/src/routes/userRoutes.js +++ b/src/routes/userRoutes.js @@ -53,6 +53,27 @@ routes.createUser = async (req, res) => { } }; +routes.createAnonUser = async (req, res) => { + try { + const account = await signUp({ email: null }); + logger.info("Succeded creating anonymous user account", account.id); + if (req.params.database === "Postgres") { + await pgModule.createPgAccount(account); + logger.info("Creating anonymous postgres account suceeded"); + return res.json(account); + } + if (req.params.database === "Elasticsearch") { + await es.createAccount(account); + logger.info("Creating anonymous elasticsearch account suceeded"); + return res.json(account); + } + return res.json({ ...account.dataValues, password: null }); + } catch (err) { + logger.error("Creating anonymous user failed", err); + return res.status(400).json({ error: { message: err.message } }); + } +}; + routes.deleteUser = async (req, res) => { if (!req.params.id) { logger.info("user id was not provided"); @@ -140,32 +161,21 @@ routes.userResetPassword = async (req, res) => { }; routes.createDatabase = async (req, res) => { - if (!req.session.email) { + let user; + if (!req.session.userid) { logger.info("User must be signed in to create database"); return res.status(403).json({ error: { message: "You must be signed in to create a database" }, }); - } - - const { Accounts } = db.getModels(); - const user = await Accounts.findOne({ - where: { id: req.session.userid }, - }); - - const { username, dbPassword } = user; - - if (!dbPassword) { - logger.info("User must use password to create database"); - return res.status(400).json({ - error: { - message: "You must use your database password to create a database", - }, + } else { + const { Accounts } = db.getModels(); + const user = await Accounts.findOne({ + where: { id: req.session.userid }, }); } - try { if (req.params.database === "postgres") { - await pgModule.createPgAccount(username, dbPassword); + await pgModule.createPgAccount(user); return res .status(200) .json({ success: { message: "Create Postgres Database success" } }); diff --git a/src/server.js b/src/server.js index 9018d4f..c7f32f4 100644 --- a/src/server.js +++ b/src/server.js @@ -6,6 +6,7 @@ const pgModule = require("../database/postgres/pg"); const { resetPasswordEmail, createUser, + createAnonUser, deleteUser, loginUser, logoutUser, @@ -65,6 +66,7 @@ const startServer = async (portNumber) => { app.get("/elasticsearch", elastic); app.post("/api/notifications", resetPasswordEmail); app.post("/api/users", createUser); + app.post("/api/anonymous/:database", createAnonUser); app.delete("/api/users/:id", deleteUser); app.post("/api/session", loginUser); app.delete("/api/session", logoutUser); diff --git a/views/postgres.ejs b/views/postgres.ejs index 3620912..32a744a 100644 --- a/views/postgres.ejs +++ b/views/postgres.ejs @@ -25,11 +25,14 @@ database: ${username}` const introduction = document.getElementById("introduction") if (!username) { introduction.innerHTML = ` -

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Please login first to create your database. If you want, you may keep reading this tutorial without login.

` +

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Simply click on the button below to create your temporary Postgres database. Credentials for your database will appear after it is done being created.

+

Temporary database credentials will expire in 5 days. If you want, you can signup with us and get a non-expiring Postgres database!

+

+ ` } else if (!dbExists) { introduction.innerHTML = `

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Simply click on the button below to create your Postgres database. Credentials for your database will appear after it is done being created.

-

+

` } else { introduction.innerHTML = ` @@ -39,10 +42,10 @@ database: ${username}` } let disabled - const createDb = () => { + const createdb = () => { if (disabled) return disabled = true - const button = document.getElementById("button") + const button = document.getElementById("createdb_button") button.classList.add("disabled") button.innerText = "Creating..." fetch('/api/createDatabase/postgres', { method: "POST" }).then(r => r.json()).then((r) => { @@ -65,6 +68,39 @@ database: ${username}` } }) } + const anon_createdb = () => { + if (disabled) return + disabled = true + const button = document.getElementById("anon_createdb_button") + button.classList.add("disabled") + button.innerText = "Creating..." + fetch("/api/anonymous/Postgres", { method: "POST" }).then((r) => r.json()).then((r) => { + console.log(r) + if (r.error) { + const message = res.error.message; + alert(message); + disabled = false + button.classList.remove("disabled") + button.innerText = "Create my database" + } else { + credentials.innerHTML = `host: learndatabases.dev +username: ${r.username} +password: ${r.dbPassword} +database: ${r.username}` + alert("Temporary database is created. Since credentials are provided only once, please keep the information somewhere safe :)"); + button.style.opacity = 0 + setTimeout(() => { + button.innerText = "Complete!" + button.style.background = "rgb(10, 170, 75)" + button.style.opacity = 1 + setTimeout(() => { + introduction.append(credentials) + }, 200) + }, 200) + } + }); + } + const content = document.getElementById("content") fetch('/lessons/postgres.md').then(r => r.text()).then((r) => { marked.setOptions({ @@ -79,13 +115,13 @@ database: ${username}` \ No newline at end of file + From e9e57b7503b0f0ef0cf94dd517ed2cc2264938cc Mon Sep 17 00:00:00 2001 From: Hoie Kim Date: Sun, 27 Sep 2020 14:55:15 +0000 Subject: [PATCH 02/12] finish test --- lib/users.js | 27 +---- lib/users.test.js | 43 ++++--- src/routes/userRoutes.js | 21 +++- src/routes/userRoutes.test.js | 110 +++++++++++++++--- src/server.test.js | 7 +- .../__snapshots__/welcome.test.js.snap | 51 ++++++-- 6 files changed, 190 insertions(+), 69 deletions(-) diff --git a/lib/users.js b/lib/users.js index bf0d2b2..df03efe 100644 --- a/lib/users.js +++ b/lib/users.js @@ -14,7 +14,7 @@ const { const users = {}; const schema = yup.object().shape({ - email: yup.string().email(), + email: yup.string().nullable().email(), }); users.sendPasswordResetEmail = async (userAccount) => { @@ -38,37 +38,20 @@ users.sendPasswordResetEmail = async (userAccount) => { }; users.signUp = async (userInfo) => { - const { email } = userInfo; - const { Accounts } = db.getModels(); - if (email) { - try { - await schema.validate(userInfo); - } catch (err) { - throw new Error(err); - } - - const userAccount = await Accounts.findOne({ - where: { - email: email, - }, - }); - if (userAccount) { - logger.info("this account already exists", email); - throw new Error("this account already exists"); - } - } + await schema.validate(userInfo); const username = uniqueNamesGenerator({ length: 2, dictionaries: [adjectives, animals], separator: "", }); const dbPassword = genPw(); + const { Accounts } = db.getModels(); const newAccount = await Accounts.create({ - email: email, + email: userInfo.email, username: username, dbPassword: dbPassword, }); - if (email) { + if (userInfo.email) { await users.sendPasswordResetEmail(newAccount); } return newAccount; diff --git a/lib/users.test.js b/lib/users.test.js index ee3be7e..afe433c 100644 --- a/lib/users.test.js +++ b/lib/users.test.js @@ -58,42 +58,47 @@ describe("Sign up", () => { }); it("should throw an error if accounts already exists", () => { - mockFindOne.mockImplementation((query) => { - if (query.where.email) return { email: "1234@gmail.com" }; - if (query.where.username) return false; + mockCreateAccount.mockImplementation(() => { + throw new Error("This account already exists"); }); const obj = { email: "1234@gmail.com", }; - return expect(signUp(obj)).rejects.toThrow("this account already exists"); + return expect(signUp(obj)).rejects.toThrow("This account already exists"); }); it("should create a user account", async () => { - const dataValues = await genUserInfoWithPw("abcd1234"); - mockFindOne.mockImplementation((query) => { - if (query.where.email) return undefined; - if (query.where.username) return false; + const obj = { email: "test.user@databases.com" }; + const dataValues = { ...obj, username: "testuser", dbPassword: "database" }; + mockCreateAccount.mockReturnValue({ + dataValues: dataValues, + update: () => {}, }); + const data = await signUp(obj); + expect(data.dataValues).toEqual(dataValues); + }); + + it("should create a anonymous user account", async () => { + const obj = {}; + const dataValues = { username: "testuser", dbPassword: "database" }; mockCreateAccount.mockReturnValue({ dataValues: dataValues, update: () => {}, }); - const obj = { - email: "test.user@databases.com", - }; const data = await signUp(obj); expect(data.dataValues).toEqual(dataValues); + expect(email.sendPasswordResetEmail).not.toHaveBeenCalled(); }); it("should send a user set Password email", async () => { - const obj = { - id: 4, - email: "em@i.l", - update: jest.fn(), - }; - - await sendPasswordResetEmail(obj); - expect(email.sendPasswordResetEmail.mock.calls.length).toEqual(1); + const obj = { email: "test.user@databases.com" }; + const dataValues = { ...obj, username: "testuser", dbPassword: "database" }; + mockCreateAccount.mockReturnValue({ + dataValues: dataValues, + update: () => {}, + }); + await signUp(obj); + expect(email.sendPasswordResetEmail).toHaveBeenCalled(); }); }); diff --git a/src/routes/userRoutes.js b/src/routes/userRoutes.js index c3d41d7..9284c41 100644 --- a/src/routes/userRoutes.js +++ b/src/routes/userRoutes.js @@ -40,6 +40,9 @@ routes.resetPasswordEmail = async (req, res) => { }; routes.createUser = async (req, res) => { + if (!req.body.email) { + return res.status(400).json({ error: { message: "Email is required" } }); + } const userInfo = { email: req.body.email, }; @@ -49,7 +52,13 @@ routes.createUser = async (req, res) => { return res.status(200).json({ ...account.dataValues }); } catch (err) { logger.error("Creating user failed", userInfo.email, err); - return res.status(400).json({ error: { message: err.message } }); + let message; + if (err.toString().includes("SequelizeUniqueConstraintError")) { + message = "This account already exists."; + } else { + message = err.toString(); + } + return res.status(400).json({ error: { message: message } }); } }; @@ -60,14 +69,16 @@ routes.createAnonUser = async (req, res) => { if (req.params.database === "Postgres") { await pgModule.createPgAccount(account); logger.info("Creating anonymous postgres account suceeded"); - return res.json(account); + return res.json({ ...account.dataValues, password: null }); } if (req.params.database === "Elasticsearch") { await es.createAccount(account); logger.info("Creating anonymous elasticsearch account suceeded"); - return res.json(account); + return res.json({ ...account.dataValues, password: null }); } - return res.json({ ...account.dataValues, password: null }); + return res + .status(400) + .json({ error: { message: "Database parameter value is required" } }); } catch (err) { logger.error("Creating anonymous user failed", err); return res.status(400).json({ error: { message: err.message } }); @@ -162,7 +173,7 @@ routes.userResetPassword = async (req, res) => { routes.createDatabase = async (req, res) => { let user; - if (!req.session.userid) { + if (!req.session.email) { logger.info("User must be signed in to create database"); return res.status(403).json({ error: { message: "You must be signed in to create a database" }, diff --git a/src/routes/userRoutes.test.js b/src/routes/userRoutes.test.js index 1455561..d756833 100644 --- a/src/routes/userRoutes.test.js +++ b/src/routes/userRoutes.test.js @@ -8,6 +8,7 @@ const db = require("../../sequelize/db"); const { resetPasswordEmail, createUser, + createAnonUser, deleteUser, loginUser, logoutUser, @@ -133,6 +134,31 @@ describe("Testing createUser function", () => { beforeEach(() => { jest.clearAllMocks(); }); + it("should send error if email is not provided", async () => { + const req = { + body: {}, + }; + await createUser(req, res); + expect(res.status.mock.calls[0][0]).toEqual(400); + return expect(res.json.mock.calls[0][0].error.message).toEqual( + "Email is required" + ); + }); + it("should send error if email already exists", async () => { + signUp.mockImplementation(() => { + throw new Error("SequelizeUniqueConstraintError"); + }); + const req = { + body: { + email: "em@i.l", + }, + }; + await createUser(req, res); + expect(res.status.mock.calls[0][0]).toEqual(400); + return expect(res.json.mock.calls[0][0].error.message).toEqual( + "This account already exists." + ); + }); it("should send error if sign up fails", async () => { signUp.mockImplementation(() => { throw new Error("Error"); @@ -144,7 +170,9 @@ describe("Testing createUser function", () => { }; await createUser(req, res); expect(res.status.mock.calls[0][0]).toEqual(400); - return expect(res.json.mock.calls[0][0].error.message).toEqual("Error"); + return expect(res.json.mock.calls[0][0].error.message).toEqual( + "Error: Error" + ); }); it("should create user account", async () => { signUp.mockImplementation(() => { @@ -165,6 +193,73 @@ describe("Testing createUser function", () => { }); }); +describe("Testing createAnonUser function", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it("should send error if sign up fails", async () => { + signUp.mockImplementation(() => { + throw new Error("Error"); + }); + const req = { + params: { + database: "Postgres", + }, + }; + await createAnonUser(req, res); + expect(res.status.mock.calls[0][0]).toEqual(400); + return expect(res.json.mock.calls[0][0].error.message).toEqual("Error"); + }); + it("should create anonymous postgres account", async () => { + signUp.mockImplementation(() => { + return { + dataValues: { + username: "testuser", + }, + }; + }); + const req = { + params: { + database: "Postgres", + }, + }; + await createAnonUser(req, res); + return expect(res.json.mock.calls[0][0].username).toEqual("testuser"); + }); + it("should create anonymous elasticsearch account", async () => { + signUp.mockImplementation(() => { + return { + dataValues: { + username: "testuser", + }, + }; + }); + const req = { + params: { + database: "Elasticsearch", + }, + }; + await createAnonUser(req, res); + return expect(res.json.mock.calls[0][0].username).toEqual("testuser"); + }); + it("should response with error when database params value is not provided", async () => { + signUp.mockImplementation(() => { + return { + dataValues: { + username: "testuser", + }, + }; + }); + const req = { + params: {}, + }; + await createAnonUser(req, res); + return expect(res.json.mock.calls[0][0].error.message).toEqual( + "Database parameter value is required" + ); + }); +}); + describe("Testing deleteUser function", () => { beforeEach(() => { jest.clearAllMocks(); @@ -369,19 +464,6 @@ describe("test creating database", () => { "You must be signed in to create a database" ); }); - it("should return error if there is not a password", async () => { - const req = { - session: { - email: "testm@i.l", - dbPassword: null, - }, - }; - - await createDatabase(req, res); - return expect(res.json.mock.calls[0][0].error.message).toEqual( - "You must use your database password to create a database" - ); - }); it("should return error if database name is not provided", async () => { const req = { diff --git a/src/server.test.js b/src/server.test.js index 61749a4..4fce3da 100644 --- a/src/server.test.js +++ b/src/server.test.js @@ -11,6 +11,7 @@ const userRoutes = require("./routes/userRoutes"); const renderRoutes = require("./routes/renderRoutes"); userRoutes.createUser = jest.fn(); +userRoutes.createAnonUser = jest.fn(); userRoutes.loginUser = jest.fn(); userRoutes.logoutUser = jest.fn(); userRoutes.deleteUser = jest.fn(); @@ -83,12 +84,14 @@ describe("Testing user routes", () => { await app.delete.mock.calls[0][1](); expect(userRoutes.deleteUser).toHaveBeenCalled(); await app.post.mock.calls[2][1](); + expect(userRoutes.createAnonUser).toHaveBeenCalled(); + await app.post.mock.calls[3][1](); expect(userRoutes.loginUser).toHaveBeenCalled(); await app.delete.mock.calls[1][1](); expect(userRoutes.logoutUser).toHaveBeenCalled(); - await app.post.mock.calls[3][1](); - expect(userRoutes.userResetPassword).toHaveBeenCalled(); await app.post.mock.calls[4][1](); + expect(userRoutes.userResetPassword).toHaveBeenCalled(); + await app.post.mock.calls[5][1](); expect(userRoutes.createDatabase).toHaveBeenCalled(); }); }); diff --git a/tests/integration/__snapshots__/welcome.test.js.snap b/tests/integration/__snapshots__/welcome.test.js.snap index 954024b..1613cb2 100644 --- a/tests/integration/__snapshots__/welcome.test.js.snap +++ b/tests/integration/__snapshots__/welcome.test.js.snap @@ -83,11 +83,14 @@ database: \${username}\` const introduction = document.getElementById(\\"introduction\\") if (!username) { introduction.innerHTML = \` -

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Please login first to create your database. If you want, you may keep reading this tutorial without login.

\` +

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Simply click on the button below to create your temporary Postgres database. Credentials for your database will appear after it is done being created.

+

Temporary database credentials will expire in 5 days. If you want, you can signup with us and get a non-expiring Postgres database!

+

+ \` } else if (!dbExists) { introduction.innerHTML = \`

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Simply click on the button below to create your Postgres database. Credentials for your database will appear after it is done being created.

-

+

\` } else { introduction.innerHTML = \` @@ -97,10 +100,10 @@ database: \${username}\` } let disabled - const createDb = () => { + const createdb = () => { if (disabled) return disabled = true - const button = document.getElementById(\\"button\\") + const button = document.getElementById(\\"createdb_button\\") button.classList.add(\\"disabled\\") button.innerText = \\"Creating...\\" fetch('/api/createDatabase/postgres', { method: \\"POST\\" }).then(r => r.json()).then((r) => { @@ -123,6 +126,39 @@ database: \${username}\` } }) } + const anon_createdb = () => { + if (disabled) return + disabled = true + const button = document.getElementById(\\"anon_createdb_button\\") + button.classList.add(\\"disabled\\") + button.innerText = \\"Creating...\\" + fetch(\\"/api/anonymous/Postgres\\", { method: \\"POST\\" }).then((r) => r.json()).then((r) => { + console.log(r) + if (r.error) { + const message = res.error.message; + alert(message); + disabled = false + button.classList.remove(\\"disabled\\") + button.innerText = \\"Create my database\\" + } else { + credentials.innerHTML = \`host: learndatabases.dev +username: \${r.username} +password: \${r.dbPassword} +database: \${r.username}\` + alert(\\"Temporary database is created. Since credentials are provided only once, please keep the information somewhere safe :)\\"); + button.style.opacity = 0 + setTimeout(() => { + button.innerText = \\"Complete!\\" + button.style.background = \\"rgb(10, 170, 75)\\" + button.style.opacity = 1 + setTimeout(() => { + introduction.append(credentials) + }, 200) + }, 200) + } + }); + } + const content = document.getElementById(\\"content\\") fetch('/lessons/postgres.md').then(r => r.text()).then((r) => { marked.setOptions({ @@ -137,16 +173,17 @@ database: \${username}\` " + +" `; exports[`test welcome page should render resetPassword page correctly 1`] = ` From 392f76fe627444043760d99db33790313bf19eea Mon Sep 17 00:00:00 2001 From: Ulisseus Date: Sun, 27 Sep 2020 18:20:57 +0300 Subject: [PATCH 03/12] closes #221 - Update Readme.md Added step by step installation guide, Added comments to env.example variables. DIALECT variable was removed since it's unnecessary, added new NODE_ENV variable (String: "production"/"development"). With NODE_ENV set to "development" confirmation emails will be send to localhost. Removed redundant getEnv function and moved its test to mailer.test.js, updated mailer snapshot. --- README.md | 39 ++++++---------------- env.example | 37 +++++++++++++++----- lib/getEnvVar.js | 8 ----- lib/getEnvVar.test.js | 16 --------- lib/users.js | 2 +- services/__snapshots__/mailer.test.js.snap | 1 + services/mailer.js | 15 ++++++--- services/mailer.test.js | 16 +++++++-- 8 files changed, 65 insertions(+), 69 deletions(-) delete mode 100644 lib/getEnvVar.js delete mode 100644 lib/getEnvVar.test.js diff --git a/README.md b/README.md index 9e0b738..010f7b2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,14 @@ -# databases +# Learn Databases -Fork the repository -Go to https://c0d3.com -Create a username and account +A free site to learn and try different databases. -Ssh into c0d3 server -`ssh [c0d3.com username]@c0d.com -p 221` +**Installation** -Clone your fork on the server -`git clone https://github.com/[github username]/databases.git` - -To run server -1. install nodemon -2. run npm start:dev - -To install nodemon -`npm i -g nodemon` - -To start the server -`npm run start:dev` - -If you need to make changes to the Database run: -`ALTER_DB=true npm run start:dev` +1. Fork the repository. If you're new to github refer to this [guide](https://github.com/garageScript/curriculum/wiki/Engineering-Workflow). +2. Download the fork into your local machine. +3. Install required node modules with `npm i`. +4. Create new .env file, it should look like env.example but with valid credentials instead of `***`. Ask for passwords and api keys on our [chat](https://chat.c0d3.com/c0d3/channels/). +5. Start server with `npm run start:dev` and register a new user. ### Production Phases @@ -34,13 +21,9 @@ Phase 3: API to power user interactions (backend) Phase 4: UI (aka frontend) **We are here!** -What needs to worked on -https://github.com/garageScript/databases/issues +* [Issues](https://github.com/garageScript/databases/issues) -Wiki -https://github.com/garageScript/databases/wiki +* [Wiki](https://github.com/garageScript/databases/wiki) -If you have any questions message us on our chat: -https://chat.c0d3.com/c0d3/channels/ +* If you have any questions message us on our [chat](https://chat.c0d3.com/c0d3/channels/) -__Sequelize credentials are in the chat for privacy__ diff --git a/env.example b/env.example index edf8144..2df5def 100644 --- a/env.example +++ b/env.example @@ -1,9 +1,28 @@ -HOST=databases.com -PORT=3000 -PG_USER=username -PASSWORD=password -DATABASE=database -DIALECT=postgres -ALTER_DB=true -MAILGUN_API_KEY=01234567890123456789 -MAILGUN_DOMAIN=databases.com \ No newline at end of file +#api key for our mailing service +MAILGUN_API_KEY=*** +#mailgun domain +MAILGUN_DOMAIN=code3scape.com +#testing port for jest +TEST_PORT=30300 +#sequelize host adress +HOST=104.168.169.204 +#sequelize postgres username +PG_USER=*** +#sequelize postgres database name +DATABASE=*** +#sequelize database password +PASSWORD=*** +#sequelize allow to alter database: true/false +ALTER_DB=true +#password for session-express +SECRET=*** +#Neo4j graph database url +NEO4J_URL=neo4j://104.168.169.204 +#Neo4j username +NEO4J_USER=*** +#Neo4j password +NEO4J_PASSWORD=*** +#production/development flag, with development mode on confirmations emails will redirect to localhost +NODE_ENV = development +#local port, default value 3052 +PORT = 4000 \ No newline at end of file diff --git a/lib/getEnvVar.js b/lib/getEnvVar.js deleted file mode 100644 index 08af531..0000000 --- a/lib/getEnvVar.js +++ /dev/null @@ -1,8 +0,0 @@ -require('dotenv').config() - -module.exports = (type) => { - if (type === 'mailgun') return { - apiKey: process.env.MAILGUN_API_KEY || '123', - domain: process.env.MAILGUN_DOMAIN - } -} \ No newline at end of file diff --git a/lib/getEnvVar.test.js b/lib/getEnvVar.test.js deleted file mode 100644 index 0eafe3e..0000000 --- a/lib/getEnvVar.test.js +++ /dev/null @@ -1,16 +0,0 @@ -describe('test getEnvVar', () => { - it('should return mailgun env var', () => { - process.env.MAILGUN_API_KEY = 'test key' - const getEnvVar = require('./getEnvVar') - expect(getEnvVar('mailgun').apiKey).toEqual('test key') - }) - it('should return default mailgun env var', () => { - delete process.env.MAILGUN_API_KEY - const getEnvVar = require('./getEnvVar') - expect(getEnvVar('mailgun').apiKey).toEqual('123') - }) - it('should return nothing without argument', () => { - const getEnvVar = require('./getEnvVar') - expect(getEnvVar()).toEqual(undefined) - }) -}) \ No newline at end of file diff --git a/lib/users.js b/lib/users.js index 742f344..9d1ff83 100644 --- a/lib/users.js +++ b/lib/users.js @@ -29,7 +29,7 @@ users.sendPasswordResetEmail = async (userAccount) => { const accountJSON = JSON.stringify(account); const encodedToken = Buffer.from(accountJSON).toString("base64"); - await email.sendPasswordResetEmail(userAccount.email, encodedToken); + await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.NODE_ENV, process.env.PORT); const updatedAccount = await userAccount.update({ passwordReset: randomToken, tokenExpiration: Date.now() + 1000 * 60 * 60 * 24, diff --git a/services/__snapshots__/mailer.test.js.snap b/services/__snapshots__/mailer.test.js.snap index fa77bf8..59f14d9 100644 --- a/services/__snapshots__/mailer.test.js.snap +++ b/services/__snapshots__/mailer.test.js.snap @@ -10,6 +10,7 @@ Object {

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

Set my Password +

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.js b/services/mailer.js index c5c18ee..73c40d2 100644 --- a/services/mailer.js +++ b/services/mailer.js @@ -1,13 +1,18 @@ const mailgun = require("mailgun-js"); const logger = require("../lib/log")(__filename); -const getEnvVar = require("../lib/getEnvVar"); +require("dotenv").config(); -const mg = mailgun(getEnvVar("mailgun")); +const mg = mailgun({ + apiKey: process.env.MAILGUN_API_KEY, + domain: process.env.MAILGUN_DOMAIN, +}); const mgModule = {}; -mgModule.sendPasswordResetEmail = (receiver, token) => { - const link = `https://learndatabases.dev/setPassword/${token}`; +mgModule.sendPasswordResetEmail = (receiver, token, mode = "production", port = 3052) => { + const link = mode === "development" ? + `http://localhost:${port}/setPassword/${token}` : + `https://learndatabases.dev/setPassword/${token}`; const data = { from: "admin@learndatabases.dev", to: receiver, @@ -20,6 +25,8 @@ mgModule.sendPasswordResetEmail = (receiver, token) => {

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

Set my Password + ${mode === "development" ? + "

DEVELOPMENT MODE IS ON. This link will redirect you to your local server

" : ""}

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.test.js b/services/mailer.test.js index 6d33473..ff6378a 100644 --- a/services/mailer.test.js +++ b/services/mailer.test.js @@ -1,4 +1,5 @@ jest.mock('../lib/log') +require("dotenv").config(); const logGen = require('../lib/log') const logger = { info: jest.fn(), @@ -19,14 +20,14 @@ mailgun.mockImplementation(() => { const email = require('./mailer'); -describe('Test mailgun', ()=>{ +describe('Test mailgun', () => { beforeEach(() => { jest.clearAllMocks() }) it('should test if mocksend and mailgun is called', async () => { messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) - await email.sendPasswordResetEmail('paul@github.com', 'token123') + await email.sendPasswordResetEmail('paul@github.com', 'token123', true) expect(messages.send).toHaveBeenCalledTimes(1) expect(messages.send.mock.calls[0][0]).toMatchSnapshot() expect(logger.info).toHaveBeenCalledTimes(1) @@ -39,5 +40,14 @@ describe('Test mailgun', ()=>{ expect(logger.error).toHaveBeenCalledTimes(1) expect(logger.error.mock.calls[0][0]).toEqual('Confirmation Email Error:') }) - + it('should send email to learndatabases with ENV_MODE set to production', async () => { + messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) + await email.sendPasswordResetEmail('paul@github.com', 'token123', "production", 4000) + expect(messages.send.mock.calls[0][0].html.includes('https://learndatabases.dev/setPassword/token123')).toEqual(true) + }) + it('should notify that development mode is on in confirmation email', async () => { + messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) + await email.sendPasswordResetEmail('paul@github.com', 'token123', "development", 4000) + expect(messages.send.mock.calls[0][0].html.includes('DEVELOPMENT MODE IS ON')).toEqual(true) + }) }) From 94f8918efca77002a5e75e94aa9ca131dea89d56 Mon Sep 17 00:00:00 2001 From: Hoie Kim Date: Sun, 27 Sep 2020 15:41:18 +0000 Subject: [PATCH 04/12] fix grammar --- tests/integration/__snapshots__/welcome.test.js.snap | 4 ++-- views/postgres.ejs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/__snapshots__/welcome.test.js.snap b/tests/integration/__snapshots__/welcome.test.js.snap index 1613cb2..ce71bbb 100644 --- a/tests/integration/__snapshots__/welcome.test.js.snap +++ b/tests/integration/__snapshots__/welcome.test.js.snap @@ -84,7 +84,7 @@ database: \${username}\` if (!username) { introduction.innerHTML = \`

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Simply click on the button below to create your temporary Postgres database. Credentials for your database will appear after it is done being created.

-

Temporary database credentials will expire in 5 days. If you want, you can signup with us and get a non-expiring Postgres database!

+

Temporary database credentials will expire in 5 days. If you want, you can sign up with us and get a non-expiring Postgres database!

\` } else if (!dbExists) { @@ -145,7 +145,7 @@ database: \${username}\` username: \${r.username} password: \${r.dbPassword} database: \${r.username}\` - alert(\\"Temporary database is created. Since credentials are provided only once, please keep the information somewhere safe :)\\"); + alert(\\"Your temporary database is created. Since credentials are provided only once, please keep the information somewhere safe :)\\"); button.style.opacity = 0 setTimeout(() => { button.innerText = \\"Complete!\\" diff --git a/views/postgres.ejs b/views/postgres.ejs index 32a744a..cae19db 100644 --- a/views/postgres.ejs +++ b/views/postgres.ejs @@ -26,7 +26,7 @@ database: ${username}` if (!username) { introduction.innerHTML = `

In this module, you can learn how to use Postgres. Don't have Postgres installed on your local machine yet? Fear not! We can make a Postgres database for you. Simply click on the button below to create your temporary Postgres database. Credentials for your database will appear after it is done being created.

-

Temporary database credentials will expire in 5 days. If you want, you can signup with us and get a non-expiring Postgres database!

+

Temporary database credentials will expire in 5 days. If you want, you can sign up with us and get a non-expiring Postgres database!

` } else if (!dbExists) { @@ -87,7 +87,7 @@ database: ${username}` username: ${r.username} password: ${r.dbPassword} database: ${r.username}` - alert("Temporary database is created. Since credentials are provided only once, please keep the information somewhere safe :)"); + alert("Your temporary database is created. Since credentials are provided only once, please keep the information somewhere safe :)"); button.style.opacity = 0 setTimeout(() => { button.innerText = "Complete!" From 59d6816c4fe19d8b8d95eac4077b1c79d7b46a3f Mon Sep 17 00:00:00 2001 From: Hoie Kim Date: Mon, 28 Sep 2020 15:33:43 +0000 Subject: [PATCH 05/12] remove postgres.ejs --- views/postgres.ejs | 103 --------------------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 views/postgres.ejs diff --git a/views/postgres.ejs b/views/postgres.ejs deleted file mode 100644 index eb4e1ec..0000000 --- a/views/postgres.ejs +++ /dev/null @@ -1,103 +0,0 @@ -<%- include("./partials/head") %> -

Learn PostgreSQL

-
-
-
- - - - - - From ec8758f9221f32367b384342ab6af6cd9c78ff61 Mon Sep 17 00:00:00 2001 From: Hoie Kim Date: Mon, 28 Sep 2020 15:43:08 +0000 Subject: [PATCH 06/12] minor change --- src/server.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server.test.js b/src/server.test.js index 0df16f8..a739a4f 100644 --- a/src/server.test.js +++ b/src/server.test.js @@ -11,7 +11,6 @@ const userRoutes = require("./routes/userRoutes"); const renderRoutes = require("./routes/renderRoutes"); userRoutes.createUser = jest.fn(); -userRoutes.createAnonUser = jest.fn(); userRoutes.loginUser = jest.fn(); userRoutes.logoutUser = jest.fn(); userRoutes.deleteUser = jest.fn(); From dc1d5696728d8d011da492589dceb4cbef3b5344 Mon Sep 17 00:00:00 2001 From: Ulisseus Date: Sun, 27 Sep 2020 18:20:57 +0300 Subject: [PATCH 07/12] closes #221 - Update Readme.md Added step by step installation guide, Added comments to env.example variables. DIALECT variable was removed since it's unnecessary, added new NODE_ENV variable (String: "production"/"development"). With NODE_ENV set to "development" confirmation emails will be send to localhost. Removed redundant getEnv function and moved its test to mailer.test.js, updated mailer snapshot. --- README.md | 39 ++++++--------------- env.example | 40 +++++++++++++++++----- lib/getEnvVar.js | 8 ----- lib/getEnvVar.test.js | 16 --------- lib/users.js | 2 +- services/__snapshots__/mailer.test.js.snap | 2 +- services/mailer.js | 14 +++++--- services/mailer.test.js | 9 +++-- 8 files changed, 60 insertions(+), 70 deletions(-) delete mode 100644 lib/getEnvVar.js delete mode 100644 lib/getEnvVar.test.js diff --git a/README.md b/README.md index 9e0b738..010f7b2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,14 @@ -# databases +# Learn Databases -Fork the repository -Go to https://c0d3.com -Create a username and account +A free site to learn and try different databases. -Ssh into c0d3 server -`ssh [c0d3.com username]@c0d.com -p 221` +**Installation** -Clone your fork on the server -`git clone https://github.com/[github username]/databases.git` - -To run server -1. install nodemon -2. run npm start:dev - -To install nodemon -`npm i -g nodemon` - -To start the server -`npm run start:dev` - -If you need to make changes to the Database run: -`ALTER_DB=true npm run start:dev` +1. Fork the repository. If you're new to github refer to this [guide](https://github.com/garageScript/curriculum/wiki/Engineering-Workflow). +2. Download the fork into your local machine. +3. Install required node modules with `npm i`. +4. Create new .env file, it should look like env.example but with valid credentials instead of `***`. Ask for passwords and api keys on our [chat](https://chat.c0d3.com/c0d3/channels/). +5. Start server with `npm run start:dev` and register a new user. ### Production Phases @@ -34,13 +21,9 @@ Phase 3: API to power user interactions (backend) Phase 4: UI (aka frontend) **We are here!** -What needs to worked on -https://github.com/garageScript/databases/issues +* [Issues](https://github.com/garageScript/databases/issues) -Wiki -https://github.com/garageScript/databases/wiki +* [Wiki](https://github.com/garageScript/databases/wiki) -If you have any questions message us on our chat: -https://chat.c0d3.com/c0d3/channels/ +* If you have any questions message us on our [chat](https://chat.c0d3.com/c0d3/channels/) -__Sequelize credentials are in the chat for privacy__ diff --git a/env.example b/env.example index edf8144..932e9c0 100644 --- a/env.example +++ b/env.example @@ -1,9 +1,31 @@ -HOST=databases.com -PORT=3000 -PG_USER=username -PASSWORD=password -DATABASE=database -DIALECT=postgres -ALTER_DB=true -MAILGUN_API_KEY=01234567890123456789 -MAILGUN_DOMAIN=databases.com \ No newline at end of file +#api key for our mailing service +MAILGUN_API_KEY=*** +#mailgun domain +MAILGUN_DOMAIN=code3scape.com +#testing port for jest +TEST_PORT=30300 +#sequelize host adress +HOST=104.168.169.204 +#sequelize postgres username +PG_USER=*** +#sequelize postgres database name +DATABASE=*** +#sequelize database password +PASSWORD=*** +#sequelize allow to alter database: true/false +ALTER_DB=true +#password for session-express +SECRET=*** +#Neo4j graph database url +NEO4J_URL=neo4j://104.168.169.204 +#Neo4j username +NEO4J_USER=*** +#Neo4j password +NEO4J_PASSWORD=*** +#production/development flag +NODE_ENV = development +#local port, default value 3052 +PORT = 3052 +#hostname adress, default is https://learndatabases.dev, +#if you want to use localhost you need to specify port, for example http://localhost:4000 +HOSTNAME = http://localhost:3052 \ No newline at end of file diff --git a/lib/getEnvVar.js b/lib/getEnvVar.js deleted file mode 100644 index 08af531..0000000 --- a/lib/getEnvVar.js +++ /dev/null @@ -1,8 +0,0 @@ -require('dotenv').config() - -module.exports = (type) => { - if (type === 'mailgun') return { - apiKey: process.env.MAILGUN_API_KEY || '123', - domain: process.env.MAILGUN_DOMAIN - } -} \ No newline at end of file diff --git a/lib/getEnvVar.test.js b/lib/getEnvVar.test.js deleted file mode 100644 index 0eafe3e..0000000 --- a/lib/getEnvVar.test.js +++ /dev/null @@ -1,16 +0,0 @@ -describe('test getEnvVar', () => { - it('should return mailgun env var', () => { - process.env.MAILGUN_API_KEY = 'test key' - const getEnvVar = require('./getEnvVar') - expect(getEnvVar('mailgun').apiKey).toEqual('test key') - }) - it('should return default mailgun env var', () => { - delete process.env.MAILGUN_API_KEY - const getEnvVar = require('./getEnvVar') - expect(getEnvVar('mailgun').apiKey).toEqual('123') - }) - it('should return nothing without argument', () => { - const getEnvVar = require('./getEnvVar') - expect(getEnvVar()).toEqual(undefined) - }) -}) \ No newline at end of file diff --git a/lib/users.js b/lib/users.js index 742f344..9d1ff83 100644 --- a/lib/users.js +++ b/lib/users.js @@ -29,7 +29,7 @@ users.sendPasswordResetEmail = async (userAccount) => { const accountJSON = JSON.stringify(account); const encodedToken = Buffer.from(accountJSON).toString("base64"); - await email.sendPasswordResetEmail(userAccount.email, encodedToken); + await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.NODE_ENV, process.env.PORT); const updatedAccount = await userAccount.update({ passwordReset: randomToken, tokenExpiration: Date.now() + 1000 * 60 * 60 * 24, diff --git a/services/__snapshots__/mailer.test.js.snap b/services/__snapshots__/mailer.test.js.snap index fa77bf8..2b44a81 100644 --- a/services/__snapshots__/mailer.test.js.snap +++ b/services/__snapshots__/mailer.test.js.snap @@ -9,7 +9,7 @@ Object {

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

- Set my Password + Set my Password

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.js b/services/mailer.js index c5c18ee..ab5bfd5 100644 --- a/services/mailer.js +++ b/services/mailer.js @@ -1,13 +1,16 @@ const mailgun = require("mailgun-js"); const logger = require("../lib/log")(__filename); -const getEnvVar = require("../lib/getEnvVar"); +require("dotenv").config(); -const mg = mailgun(getEnvVar("mailgun")); +const mg = mailgun({ + apiKey: process.env.MAILGUN_API_KEY, + domain: process.env.MAILGUN_DOMAIN, +}); const mgModule = {}; -mgModule.sendPasswordResetEmail = (receiver, token) => { - const link = `https://learndatabases.dev/setPassword/${token}`; +mgModule.sendPasswordResetEmail = (receiver, token, hostname = "https://learndatabases.dev") => { + const link = `${hostname}/setPassword/${token}` const data = { from: "admin@learndatabases.dev", to: receiver, @@ -19,7 +22,8 @@ mgModule.sendPasswordResetEmail = (receiver, token) => {

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

- Set my Password + Set my Password ${hostname != "https://learndatabases.dev" ? + "

DEVELOPMENT MODE IS ON. This link will redirect you to your development server

" : ""}

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.test.js b/services/mailer.test.js index 6d33473..896d2e9 100644 --- a/services/mailer.test.js +++ b/services/mailer.test.js @@ -1,4 +1,5 @@ jest.mock('../lib/log') +require("dotenv").config(); const logGen = require('../lib/log') const logger = { info: jest.fn(), @@ -19,7 +20,7 @@ mailgun.mockImplementation(() => { const email = require('./mailer'); -describe('Test mailgun', ()=>{ +describe('Test mailgun', () => { beforeEach(() => { jest.clearAllMocks() }) @@ -39,5 +40,9 @@ describe('Test mailgun', ()=>{ expect(logger.error).toHaveBeenCalledTimes(1) expect(logger.error.mock.calls[0][0]).toEqual('Confirmation Email Error:') }) - + it('should notify that development mode is on in confirmation email', async () => { + messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) + await email.sendPasswordResetEmail('paul@github.com', 'token123', "http://localhost:4000") + expect(messages.send.mock.calls[0][0].html.includes('DEVELOPMENT MODE IS ON')).toEqual(true) + }) }) From 0a833ed635c721f3e3345bd888d5d9632db16e6e Mon Sep 17 00:00:00 2001 From: Ulisseus Date: Sun, 27 Sep 2020 18:20:57 +0300 Subject: [PATCH 08/12] closes #221 - Update Readme, remove getEnv.js, fixes to envExample --- README.md | 39 +++++------------- env.example | 47 +++++++++++++++++----- lib/getEnvVar.js | 8 ---- lib/getEnvVar.test.js | 16 -------- lib/users.js | 2 +- services/__snapshots__/mailer.test.js.snap | 5 +++ services/mailer.js | 25 ++++++++++-- services/mailer.test.js | 22 ++++++++-- 8 files changed, 95 insertions(+), 69 deletions(-) delete mode 100644 lib/getEnvVar.js delete mode 100644 lib/getEnvVar.test.js diff --git a/README.md b/README.md index 9e0b738..010f7b2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,14 @@ -# databases +# Learn Databases -Fork the repository -Go to https://c0d3.com -Create a username and account +A free site to learn and try different databases. -Ssh into c0d3 server -`ssh [c0d3.com username]@c0d.com -p 221` +**Installation** -Clone your fork on the server -`git clone https://github.com/[github username]/databases.git` - -To run server -1. install nodemon -2. run npm start:dev - -To install nodemon -`npm i -g nodemon` - -To start the server -`npm run start:dev` - -If you need to make changes to the Database run: -`ALTER_DB=true npm run start:dev` +1. Fork the repository. If you're new to github refer to this [guide](https://github.com/garageScript/curriculum/wiki/Engineering-Workflow). +2. Download the fork into your local machine. +3. Install required node modules with `npm i`. +4. Create new .env file, it should look like env.example but with valid credentials instead of `***`. Ask for passwords and api keys on our [chat](https://chat.c0d3.com/c0d3/channels/). +5. Start server with `npm run start:dev` and register a new user. ### Production Phases @@ -34,13 +21,9 @@ Phase 3: API to power user interactions (backend) Phase 4: UI (aka frontend) **We are here!** -What needs to worked on -https://github.com/garageScript/databases/issues +* [Issues](https://github.com/garageScript/databases/issues) -Wiki -https://github.com/garageScript/databases/wiki +* [Wiki](https://github.com/garageScript/databases/wiki) -If you have any questions message us on our chat: -https://chat.c0d3.com/c0d3/channels/ +* If you have any questions message us on our [chat](https://chat.c0d3.com/c0d3/channels/) -__Sequelize credentials are in the chat for privacy__ diff --git a/env.example b/env.example index edf8144..fd363ef 100644 --- a/env.example +++ b/env.example @@ -1,9 +1,38 @@ -HOST=databases.com -PORT=3000 -PG_USER=username -PASSWORD=password -DATABASE=database -DIALECT=postgres -ALTER_DB=true -MAILGUN_API_KEY=01234567890123456789 -MAILGUN_DOMAIN=databases.com \ No newline at end of file +#api key for our mailing service +MAILGUN_API_KEY=*** +#mailgun domain +MAILGUN_DOMAIN=code3scape.com +#testing port for jest +TEST_PORT=30300 +#sequelize host adress +HOST=104.168.169.204 +#sequelize postgres username +PG_USER=*** +#sequelize postgres database name +DATABASE=*** +#sequelize database password +PASSWORD=*** +#sequelize allow to alter database: true/false +ALTER_DB=true +#password for session-express +SECRET=*** +#Neo4j graph database url +NEO4J_URL=neo4j://104.168.169.204 +#Neo4j username +NEO4J_USER=*** +#Neo4j password +NEO4J_PASSWORD=*** +<<<<<<< HEAD +#production/development flag +NODE_ENV = development +#local port, default value 3052 +PORT = 3052 +#hostname adress, default is https://learndatabases.dev, +#if you want to use localhost you need to specify port, for example http://localhost:4000 +HOSTNAME = http://localhost:3052 +======= +#production/development flag, with development mode on confirmations emails will redirect to localhost +NODE_ENV = development +#local port, default value 3052 +PORT = 4000 +>>>>>>> 392f76f... closes #221 - Update Readme.md diff --git a/lib/getEnvVar.js b/lib/getEnvVar.js deleted file mode 100644 index 08af531..0000000 --- a/lib/getEnvVar.js +++ /dev/null @@ -1,8 +0,0 @@ -require('dotenv').config() - -module.exports = (type) => { - if (type === 'mailgun') return { - apiKey: process.env.MAILGUN_API_KEY || '123', - domain: process.env.MAILGUN_DOMAIN - } -} \ No newline at end of file diff --git a/lib/getEnvVar.test.js b/lib/getEnvVar.test.js deleted file mode 100644 index 0eafe3e..0000000 --- a/lib/getEnvVar.test.js +++ /dev/null @@ -1,16 +0,0 @@ -describe('test getEnvVar', () => { - it('should return mailgun env var', () => { - process.env.MAILGUN_API_KEY = 'test key' - const getEnvVar = require('./getEnvVar') - expect(getEnvVar('mailgun').apiKey).toEqual('test key') - }) - it('should return default mailgun env var', () => { - delete process.env.MAILGUN_API_KEY - const getEnvVar = require('./getEnvVar') - expect(getEnvVar('mailgun').apiKey).toEqual('123') - }) - it('should return nothing without argument', () => { - const getEnvVar = require('./getEnvVar') - expect(getEnvVar()).toEqual(undefined) - }) -}) \ No newline at end of file diff --git a/lib/users.js b/lib/users.js index 742f344..9d1ff83 100644 --- a/lib/users.js +++ b/lib/users.js @@ -29,7 +29,7 @@ users.sendPasswordResetEmail = async (userAccount) => { const accountJSON = JSON.stringify(account); const encodedToken = Buffer.from(accountJSON).toString("base64"); - await email.sendPasswordResetEmail(userAccount.email, encodedToken); + await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.NODE_ENV, process.env.PORT); const updatedAccount = await userAccount.update({ passwordReset: randomToken, tokenExpiration: Date.now() + 1000 * 60 * 60 * 24, diff --git a/services/__snapshots__/mailer.test.js.snap b/services/__snapshots__/mailer.test.js.snap index fa77bf8..81d4458 100644 --- a/services/__snapshots__/mailer.test.js.snap +++ b/services/__snapshots__/mailer.test.js.snap @@ -9,7 +9,12 @@ Object {

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

+<<<<<<< HEAD + Set my Password +======= Set my Password + +>>>>>>> 392f76f... closes #221 - Update Readme.md

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.js b/services/mailer.js index c5c18ee..516ceff 100644 --- a/services/mailer.js +++ b/services/mailer.js @@ -1,13 +1,23 @@ const mailgun = require("mailgun-js"); const logger = require("../lib/log")(__filename); -const getEnvVar = require("../lib/getEnvVar"); +require("dotenv").config(); -const mg = mailgun(getEnvVar("mailgun")); +const mg = mailgun({ + apiKey: process.env.MAILGUN_API_KEY, + domain: process.env.MAILGUN_DOMAIN, +}); const mgModule = {}; -mgModule.sendPasswordResetEmail = (receiver, token) => { - const link = `https://learndatabases.dev/setPassword/${token}`; +<<<<<<< HEAD +mgModule.sendPasswordResetEmail = (receiver, token, hostname = "https://learndatabases.dev") => { + const link = `${hostname}/setPassword/${token}` +======= +mgModule.sendPasswordResetEmail = (receiver, token, mode = "production", port = 3052) => { + const link = mode === "development" ? + `http://localhost:${port}/setPassword/${token}` : + `https://learndatabases.dev/setPassword/${token}`; +>>>>>>> 392f76f... closes #221 - Update Readme.md const data = { from: "admin@learndatabases.dev", to: receiver, @@ -19,7 +29,14 @@ mgModule.sendPasswordResetEmail = (receiver, token) => {

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

+<<<<<<< HEAD + Set my Password ${hostname != "https://learndatabases.dev" ? + "

DEVELOPMENT MODE IS ON. This link will redirect you to your development server

" : ""} +======= Set my Password + ${mode === "development" ? + "

DEVELOPMENT MODE IS ON. This link will redirect you to your local server

" : ""} +>>>>>>> 392f76f... closes #221 - Update Readme.md

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.test.js b/services/mailer.test.js index 6d33473..f50d8bb 100644 --- a/services/mailer.test.js +++ b/services/mailer.test.js @@ -1,4 +1,5 @@ jest.mock('../lib/log') +require("dotenv").config(); const logGen = require('../lib/log') const logger = { info: jest.fn(), @@ -19,14 +20,14 @@ mailgun.mockImplementation(() => { const email = require('./mailer'); -describe('Test mailgun', ()=>{ +describe('Test mailgun', () => { beforeEach(() => { jest.clearAllMocks() }) it('should test if mocksend and mailgun is called', async () => { messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) - await email.sendPasswordResetEmail('paul@github.com', 'token123') + await email.sendPasswordResetEmail('paul@github.com', 'token123', true) expect(messages.send).toHaveBeenCalledTimes(1) expect(messages.send.mock.calls[0][0]).toMatchSnapshot() expect(logger.info).toHaveBeenCalledTimes(1) @@ -39,5 +40,20 @@ describe('Test mailgun', ()=>{ expect(logger.error).toHaveBeenCalledTimes(1) expect(logger.error.mock.calls[0][0]).toEqual('Confirmation Email Error:') }) - +<<<<<<< HEAD + it('should notify that development mode is on in confirmation email', async () => { + messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) + await email.sendPasswordResetEmail('paul@github.com', 'token123', "http://localhost:4000") +======= + it('should send email to learndatabases with ENV_MODE set to production', async () => { + messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) + await email.sendPasswordResetEmail('paul@github.com', 'token123', "production", 4000) + expect(messages.send.mock.calls[0][0].html.includes('https://learndatabases.dev/setPassword/token123')).toEqual(true) + }) + it('should notify that development mode is on in confirmation email', async () => { + messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) + await email.sendPasswordResetEmail('paul@github.com', 'token123', "development", 4000) +>>>>>>> 392f76f... closes #221 - Update Readme.md + expect(messages.send.mock.calls[0][0].html.includes('DEVELOPMENT MODE IS ON')).toEqual(true) + }) }) From 1ba2e06827e89c9def6ab6a302a193b9141cda9e Mon Sep 17 00:00:00 2001 From: Ulisseus Date: Tue, 29 Sep 2020 00:06:30 +0300 Subject: [PATCH 09/12] fix a typo --- env.example | 7 ------- lib/users.js | 2 +- services/__snapshots__/mailer.test.js.snap | 5 ----- services/mailer.js | 13 ------------- services/mailer.test.js | 13 +------------ 5 files changed, 2 insertions(+), 38 deletions(-) diff --git a/env.example b/env.example index fd363ef..91941cb 100644 --- a/env.example +++ b/env.example @@ -22,7 +22,6 @@ NEO4J_URL=neo4j://104.168.169.204 NEO4J_USER=*** #Neo4j password NEO4J_PASSWORD=*** -<<<<<<< HEAD #production/development flag NODE_ENV = development #local port, default value 3052 @@ -30,9 +29,3 @@ PORT = 3052 #hostname adress, default is https://learndatabases.dev, #if you want to use localhost you need to specify port, for example http://localhost:4000 HOSTNAME = http://localhost:3052 -======= -#production/development flag, with development mode on confirmations emails will redirect to localhost -NODE_ENV = development -#local port, default value 3052 -PORT = 4000 ->>>>>>> 392f76f... closes #221 - Update Readme.md diff --git a/lib/users.js b/lib/users.js index 9d1ff83..4348d55 100644 --- a/lib/users.js +++ b/lib/users.js @@ -29,7 +29,7 @@ users.sendPasswordResetEmail = async (userAccount) => { const accountJSON = JSON.stringify(account); const encodedToken = Buffer.from(accountJSON).toString("base64"); - await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.NODE_ENV, process.env.PORT); + await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.HOSTNAME); const updatedAccount = await userAccount.update({ passwordReset: randomToken, tokenExpiration: Date.now() + 1000 * 60 * 60 * 24, diff --git a/services/__snapshots__/mailer.test.js.snap b/services/__snapshots__/mailer.test.js.snap index 81d4458..2b44a81 100644 --- a/services/__snapshots__/mailer.test.js.snap +++ b/services/__snapshots__/mailer.test.js.snap @@ -9,12 +9,7 @@ Object {

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

-<<<<<<< HEAD Set my Password -======= - Set my Password - ->>>>>>> 392f76f... closes #221 - Update Readme.md

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.js b/services/mailer.js index 516ceff..ab5bfd5 100644 --- a/services/mailer.js +++ b/services/mailer.js @@ -9,15 +9,8 @@ const mg = mailgun({ const mgModule = {}; -<<<<<<< HEAD mgModule.sendPasswordResetEmail = (receiver, token, hostname = "https://learndatabases.dev") => { const link = `${hostname}/setPassword/${token}` -======= -mgModule.sendPasswordResetEmail = (receiver, token, mode = "production", port = 3052) => { - const link = mode === "development" ? - `http://localhost:${port}/setPassword/${token}` : - `https://learndatabases.dev/setPassword/${token}`; ->>>>>>> 392f76f... closes #221 - Update Readme.md const data = { from: "admin@learndatabases.dev", to: receiver, @@ -29,14 +22,8 @@ mgModule.sendPasswordResetEmail = (receiver, token, mode = "production", port =

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

-<<<<<<< HEAD Set my Password ${hostname != "https://learndatabases.dev" ? "

DEVELOPMENT MODE IS ON. This link will redirect you to your development server

" : ""} -======= - Set my Password - ${mode === "development" ? - "

DEVELOPMENT MODE IS ON. This link will redirect you to your local server

" : ""} ->>>>>>> 392f76f... closes #221 - Update Readme.md

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.test.js b/services/mailer.test.js index f50d8bb..896d2e9 100644 --- a/services/mailer.test.js +++ b/services/mailer.test.js @@ -27,7 +27,7 @@ describe('Test mailgun', () => { it('should test if mocksend and mailgun is called', async () => { messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) - await email.sendPasswordResetEmail('paul@github.com', 'token123', true) + await email.sendPasswordResetEmail('paul@github.com', 'token123') expect(messages.send).toHaveBeenCalledTimes(1) expect(messages.send.mock.calls[0][0]).toMatchSnapshot() expect(logger.info).toHaveBeenCalledTimes(1) @@ -40,20 +40,9 @@ describe('Test mailgun', () => { expect(logger.error).toHaveBeenCalledTimes(1) expect(logger.error.mock.calls[0][0]).toEqual('Confirmation Email Error:') }) -<<<<<<< HEAD it('should notify that development mode is on in confirmation email', async () => { messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) await email.sendPasswordResetEmail('paul@github.com', 'token123', "http://localhost:4000") -======= - it('should send email to learndatabases with ENV_MODE set to production', async () => { - messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) - await email.sendPasswordResetEmail('paul@github.com', 'token123', "production", 4000) - expect(messages.send.mock.calls[0][0].html.includes('https://learndatabases.dev/setPassword/token123')).toEqual(true) - }) - it('should notify that development mode is on in confirmation email', async () => { - messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) - await email.sendPasswordResetEmail('paul@github.com', 'token123', "development", 4000) ->>>>>>> 392f76f... closes #221 - Update Readme.md expect(messages.send.mock.calls[0][0].html.includes('DEVELOPMENT MODE IS ON')).toEqual(true) }) }) From 423ddaada5b4021aaa92e4a61640bbdb85e91208 Mon Sep 17 00:00:00 2001 From: Ulisseus Date: Tue, 29 Sep 2020 00:09:57 +0300 Subject: [PATCH 10/12] remove merge comment --- lib/users.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/users.js b/lib/users.js index d13d879..4348d55 100644 --- a/lib/users.js +++ b/lib/users.js @@ -29,11 +29,7 @@ users.sendPasswordResetEmail = async (userAccount) => { const accountJSON = JSON.stringify(account); const encodedToken = Buffer.from(accountJSON).toString("base64"); -<<<<<<< HEAD await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.HOSTNAME); -======= - await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.NODE_ENV, process.env.PORT); ->>>>>>> 4575bc7cf27c2bdad2a376dc3c228d5bad875f10 const updatedAccount = await userAccount.update({ passwordReset: randomToken, tokenExpiration: Date.now() + 1000 * 60 * 60 * 24, From d900ca7aa72f158df85a1b44158ee55263529c5f Mon Sep 17 00:00:00 2001 From: Hoie Kim Date: Tue, 29 Sep 2020 02:01:11 +0000 Subject: [PATCH 11/12] fixes database page anon userdata rendering, add integration test --- .../__snapshots__/welcome.test.js.snap | 370 +++++++++++++++++- tests/integration/welcome.test.js | 11 +- views/tutorial.ejs | 4 +- 3 files changed, 374 insertions(+), 11 deletions(-) diff --git a/tests/integration/__snapshots__/welcome.test.js.snap b/tests/integration/__snapshots__/welcome.test.js.snap index 4c94a03..905b957 100644 --- a/tests/integration/__snapshots__/welcome.test.js.snap +++ b/tests/integration/__snapshots__/welcome.test.js.snap @@ -1,16 +1,370 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`test welcome page should render elasticsearch page correctly 1`] = ` +" + + + + + + + + + learndatabases.dev + +
+

Learn Databases

+ + +

Login / Signup

+ +
+ + + +

Learn Elasticsearch

+
+
+
+ + + + + + +" +`; + exports[`test welcome page should render postgres page correctly 1`] = ` -" +" + + + + + + - - -Error - - -
Cannot GET /postgres
- + + learndatabases.dev + +
+

Learn Databases

+ + +

Login / Signup

+ +
+ + +

Learn Postgres

+
+
+
+ + + + + + " `; diff --git a/tests/integration/welcome.test.js b/tests/integration/welcome.test.js index 35fffe6..a7f0496 100644 --- a/tests/integration/welcome.test.js +++ b/tests/integration/welcome.test.js @@ -49,7 +49,16 @@ describe("test welcome page", () => { }); test("should render postgres page correctly", async () => { - const result = await fetch(baseUrl + "postgres").then((r) => r.text()); + const result = await fetch(baseUrl + "tutorial/Postgres").then((r) => + r.text() + ); + expect(result).toMatchSnapshot(); + }); + + test("should render elasticsearch page correctly", async () => { + const result = await fetch(baseUrl + "tutorial/Elasticsearch").then((r) => + r.text() + ); expect(result).toMatchSnapshot(); }); }); diff --git a/views/tutorial.ejs b/views/tutorial.ejs index b348f15..e66a0f5 100644 --- a/views/tutorial.ejs +++ b/views/tutorial.ejs @@ -93,13 +93,13 @@ index: ${username}-*`

Temporary database credentials will expire in 5 days. If you want, you can sign up with us and get a non-expiring Postgres database!

` introduction.append(createDb_button) - renderContent() + renderContent("username", "dbPassword") } else if (!dbExists) { introduction.innerHTML = `

In this module, you can learn how to use ${database}. Don't have ${database} installed on your local machine yet? Fear not! We can make an ${database} database for you. Simply click on the button below to create your ${database} database. Credentials for your database will appear after it is done being created.

` introduction.append(createDb_button) - renderContent() + renderContent("username", "dbPassword") } else { introduction.innerHTML = `

In this module, you can learn how to use ${database}. You have already created an ${database} database on our server. Here are the credentials for your database.

From 5b60ba8027e30d6d1d2e76de8a6f272636739754 Mon Sep 17 00:00:00 2001 From: Ulisseus Date: Wed, 30 Sep 2020 12:52:46 +0300 Subject: [PATCH 12/12] remove global variable in mailer.js args --- env.example | 6 +++--- lib/users.js | 2 +- services/mailer.js | 7 +++---- services/mailer.test.js | 11 ++++++++++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/env.example b/env.example index 91941cb..83bca08 100644 --- a/env.example +++ b/env.example @@ -1,4 +1,4 @@ -#api key for our mailing service +#api key for mailing service MAILGUN_API_KEY=*** #mailgun domain MAILGUN_DOMAIN=code3scape.com @@ -22,10 +22,10 @@ NEO4J_URL=neo4j://104.168.169.204 NEO4J_USER=*** #Neo4j password NEO4J_PASSWORD=*** -#production/development flag +#production/development flag, in jest tests this varible is "test" by default NODE_ENV = development #local port, default value 3052 PORT = 3052 #hostname adress, default is https://learndatabases.dev, #if you want to use localhost you need to specify port, for example http://localhost:4000 -HOSTNAME = http://localhost:3052 +HOSTNAME = https://learndatabases.dev diff --git a/lib/users.js b/lib/users.js index 4348d55..742f344 100644 --- a/lib/users.js +++ b/lib/users.js @@ -29,7 +29,7 @@ users.sendPasswordResetEmail = async (userAccount) => { const accountJSON = JSON.stringify(account); const encodedToken = Buffer.from(accountJSON).toString("base64"); - await email.sendPasswordResetEmail(userAccount.email, encodedToken, process.env.HOSTNAME); + await email.sendPasswordResetEmail(userAccount.email, encodedToken); const updatedAccount = await userAccount.update({ passwordReset: randomToken, tokenExpiration: Date.now() + 1000 * 60 * 60 * 24, diff --git a/services/mailer.js b/services/mailer.js index ab5bfd5..6e89e73 100644 --- a/services/mailer.js +++ b/services/mailer.js @@ -9,8 +9,8 @@ const mg = mailgun({ const mgModule = {}; -mgModule.sendPasswordResetEmail = (receiver, token, hostname = "https://learndatabases.dev") => { - const link = `${hostname}/setPassword/${token}` +mgModule.sendPasswordResetEmail = (receiver, token) => { + const link = `${process.env.HOSTNAME||"https://learndatabases.dev"}/setPassword/${token}` const data = { from: "admin@learndatabases.dev", to: receiver, @@ -22,8 +22,7 @@ mgModule.sendPasswordResetEmail = (receiver, token, hostname = "https://learndat

You have requested a (re)set password token. The button below will redirect you to our website with an autheticated token. Please click the button and set your password.

- Set my Password ${hostname != "https://learndatabases.dev" ? - "

DEVELOPMENT MODE IS ON. This link will redirect you to your development server

" : ""} + Set my Password ${link!=`https://learndatabases.dev/setPassword/${token}`?"

DEVELOPMENT MODE IS ON. This link will redirect you to your development server

" : ""}

Warning: Anyone with access to this email has access to your account. Don't share this email with other people.

diff --git a/services/mailer.test.js b/services/mailer.test.js index 896d2e9..c63e3f4 100644 --- a/services/mailer.test.js +++ b/services/mailer.test.js @@ -26,6 +26,7 @@ describe('Test mailgun', () => { }) it('should test if mocksend and mailgun is called', async () => { + process.env.HOSTNAME = "" messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) await email.sendPasswordResetEmail('paul@github.com', 'token123') expect(messages.send).toHaveBeenCalledTimes(1) @@ -35,14 +36,22 @@ describe('Test mailgun', () => { }) it('should call logger.error when function is called with invalid argument', async () => { + process.env.HOSTNAME = "" messages.send = jest.fn().mockReturnValue(Promise.reject('rejected')) await email.sendPasswordResetEmail(null, null) expect(logger.error).toHaveBeenCalledTimes(1) expect(logger.error.mock.calls[0][0]).toEqual('Confirmation Email Error:') }) it('should notify that development mode is on in confirmation email', async () => { + process.env.HOSTNAME= "http://localhost:4000" messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) - await email.sendPasswordResetEmail('paul@github.com', 'token123', "http://localhost:4000") + await email.sendPasswordResetEmail('paul@github.com', 'token123') expect(messages.send.mock.calls[0][0].html.includes('DEVELOPMENT MODE IS ON')).toEqual(true) }) + it('should redirect to learndatabases.dev in confirmation email if no HOSTNAME was provided in .env file', async () => { + process.env.HOSTNAME = "" + messages.send = jest.fn().mockReturnValue(Promise.resolve('hello')) + await email.sendPasswordResetEmail('paul@github.com', 'token123') + expect(messages.send.mock.calls[0][0].html.includes('https://learndatabases.dev/setPassword')).toEqual(true) + }) })