diff --git a/.gitignore b/.gitignore index 4f9be4d..7734288 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Environment variables .env +#vscode files +.vscode + # Node modules node_modules/ diff --git a/app.js b/app.js index b976e9a..ff449a4 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,7 @@ const MongoStore = require("connect-mongo"); const session = require("express-session"); const express = require('express'); const path = require('path'); +const bcrypt = require('bcrypt'); const joi = require('joi'); require('dotenv').config(); @@ -11,7 +12,7 @@ const port = process.env.PORT || 3000; const mongoURI = process.env.MONGO_URI; const database = process.env.DATABASE; // Database name -const secret = process.env.SECRET || "123-secret-xyz"; +const secret = process.env.SECRET || "123-secret-xyz"; /*** Sessions ***/ app.use(session({ @@ -37,11 +38,11 @@ let assets; let plans; async function initDatabase() { const db = await connectMongo(mongoURI, database); - + // For any collection, init here - users = await getCollection(db, "users"); + users = await getCollection(db, "users"); assets = await getCollection(db, "assets"); - plans = await getCollection(db, "plans"); + plans = await getCollection(db, "plans"); } /*** ROUTINGS ***/ @@ -73,6 +74,96 @@ app.get('/aboutUs', (req, res) => { return res.status(status.Ok); }); +app.get('/forgotPassword', (req, res) => { + const error = req.session.error; + const reset = req.session.reset; + req.session.reset = ''; + req.session.error = ''; + res.render('forgotPass', { error: error, reset: reset }); + return res.status(status.Ok); +}); + + +// Reset with token given to user via email +app.get('/reset/:token', async (req, res) => { + const token = req.params.token; + + const user = await users.findOne({ + resetToken: token, + resetTokenExpires: { $gt: Date.now() }, + }); + + if (!user) { + req.session.error = 'reset link not valid or has expired'; + return res.redirect('/forgotPassword'); + } + const error = req.session.error; + delete req.session.error; + + res.render('resetPass', { + token: token, + errMessage: error, + }); +}); + +app.post('/resetLink', async (req, res) => { + const { token, password, confirmPassword, } = req.body; + const passwordSchema = joi.object({ + password: joi.string().max(20).required(), + confirmPassword: joi.string().max(20).required(), + }); + const valid = passwordSchema.validate({ password, confirmPassword }); + if (valid.error) { + console.log("houston we have a problem"); + req.session.error = 'Invalid input'; + res.status(status.BadRequest); + return res.redirect(`/reset/${token}`); + } + if (!token || !password || !confirmPassword) { + req.session.error = 'field may be missing'; + return res.redirect(`/reset/${token}`); + } + if (password !== confirmPassword) { + req.session.error = 'passwords do not match'; + return res.redirect(`/reset/${token}`); + } + const user = await users.findOne({ + resetToken: token, + resetTokenExpires: { $gt: Date.now() }, + }); + + if (!user) { + req.session.error = 'Reset link is invalid.'; + return res.redirect(`/reset`); + } + const hashPassword = await bcrypt.hash(password, 12); + + await users.updateOne( + { + email: user.email + }, + { + $set: { + password: hashPassword, + resetToken: '', + resetTokenExpires: 0, + }, + } + ); + req.session.success = 'Password has been reset'; + res.redirect('/login'); +}); + +app.post('/api/location', async (req, res) => { + const { latitude, longitude } = req.body; + + const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&result_type=country&key=${process.env.geolocation_api}`); + + const data = await response.json(); + + res.json(data); +}); + // 404 handler - keep the actual notFound route please // REALLY DONT DELETE THIS app.get('/notFound', (req, res) => { @@ -86,7 +177,9 @@ initDatabase().then(() => { // Import authentication handler app.use(require("./src/auth/authentication")(users)); - + app.use(require('./src/auth/forgotPass')(users)); + + // Import middleware & apply to user routes const middleware = require("./src/auth/middleware")(users); app.use(require('./src/router/user')(middleware, users, plans, assets)); @@ -96,7 +189,7 @@ initDatabase().then(() => { res.render('notFound'); return res.status(status.NotFound); }); - + // Start app app.listen(port, () => { console.log(`Server listening on port ${port}`); diff --git a/package-lock.json b/package-lock.json index 95750e3..521a7be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,14 @@ "dependencies": { "bcrypt": "^5.1.1", "connect-mongo": "^5.1.0", + "crypto": "^1.0.1", "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^5.1.0", "express-session": "^1.18.1", "joi": "^17.13.3", - "mongodb": "^6.16.0" + "mongodb": "^6.16.0", + "nodemailer": "^7.0.3" }, "devDependencies": { "nodemon": "^3.1.10" @@ -483,6 +485,12 @@ "node": ">=6.6.0" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -1430,6 +1438,14 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/nodemailer": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.3.tgz", + "integrity": "sha512-Ajq6Sz1x7cIK3pN6KesGTah+1gnwMnx5gKl3piQlQQE/PwyJ4Mbc8is2psWYxK3RJTVeqsDaCv8ZzXLCDHMTZw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", diff --git a/package.json b/package.json index 4f1048f..1071779 100755 --- a/package.json +++ b/package.json @@ -20,12 +20,14 @@ "dependencies": { "bcrypt": "^5.1.1", "connect-mongo": "^5.1.0", + "crypto": "^1.0.1", "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^5.1.0", "express-session": "^1.18.1", "joi": "^17.13.3", - "mongodb": "^6.16.0" + "mongodb": "^6.16.0", + "nodemailer": "^7.0.3" }, "devDependencies": { "nodemon": "^3.1.10" diff --git a/src/auth/forgotPass.js b/src/auth/forgotPass.js new file mode 100644 index 0000000..62260bf --- /dev/null +++ b/src/auth/forgotPass.js @@ -0,0 +1,68 @@ +const express = require('express'); +const crypto = require('crypto'); +const joi = require('joi'); +const nodeMail = require('nodemailer'); +require('dotenv').config(); + +const transporter = nodeMail.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL_USER, + pass: process.env.PASS, + } +}); +// users info +module.exports = (users) => { + const router = express.Router(); + + router.post('/auth/resetPass', async (req, res) => { + console.log("we are inside of the post"); + const resetSchema = joi.object({ + email: joi.string().email().required(), + }); + req.session.error = ''; + req.session.reset = ''; + + const valid = resetSchema.validate(req.body); + if (valid.error) { + req.session.error = 'invalid email'; + return res.redirect('/forgotPassword') + } + const { email } = req.body; + const user = await users.findOne({ email }); + + if (!user) { + req.session.error = 'No user found' + return res.redirect('/forgotPassword'); + } + + const token = crypto.randomBytes(32).toString('hex'); + console.log(`The reset token is ${token}`) + const expiration = Date.now() + 360000; + + await users.updateOne({ email }, { + $set: { resetToken: token, resetTokenExpires: expiration } + }); + + const resetUrl = `http://localhost:3000/reset/${token}`; + + const mailSend = { + from: process.env.EMAIL_USER, + to: email, + subject: 'Password reset', + text: `reset your password here ${resetUrl} this link will expire within 1 hour`, + + }; + + try { + await transporter.sendMail(mailSend); + req.session.reset = 'Reset link sent Check your email'; + res.redirect('/forgotPassword'); + } catch (err) { + console.log('there was an error', err); + res.status(500).send('email failed to send try again'); + } + }); + return router; +} + diff --git a/src/router/user.js b/src/router/user.js index 8816990..c8f67f6 100644 --- a/src/router/user.js +++ b/src/router/user.js @@ -86,7 +86,7 @@ module.exports = (middleware, users, plans, assets) => { router.get('/assets', async (req, res) => { let userAssets = await assets.find({ userId: new ObjectId(req.session.user._id) }).toArray(); - res.render('assets', { + res.render('assets', { user: req.session.user, errMessage: req.session.errMessage, assets: userAssets, @@ -98,14 +98,14 @@ module.exports = (middleware, users, plans, assets) => { router.get('/plans', async (req, res) => { try { - const userPlansFromDB = await plans.find({userId: new ObjectId(req.session.user._id) }).toArray(); - + const userPlansFromDB = await plans.find({ userId: new ObjectId(req.session.user._id) }).toArray(); + // Use a for...of loop for proper async/await behavior in series for updates for (const plan of userPlansFromDB) { const percentage = await calculatePlanProgress(plan, assets, req.session.user._id); await updatePlanProgressInDB(plan._id, percentage, plans); // Pass the 'plans' collection } - + // Re-fetch plans to get updated progress for rendering const updatedUserPlans = await plans.find({ userId: new ObjectId(req.session.user._id) }).toArray(); @@ -122,12 +122,12 @@ module.exports = (middleware, users, plans, assets) => { }); router.get('/plans/:id', async (req, res) => { - + try { const planId = req.params.id; let userAssets = await assets.find({ userId: new ObjectId(req.session.user._id) }).toArray(); - - + + if (!ObjectId.isValid(planId)) { req.session.errMessage = "Invalid plan ID format."; return res.status(status.BadRequest).redirect('/plans'); @@ -140,7 +140,7 @@ module.exports = (middleware, users, plans, assets) => { req.session.errMessage = "Plan not found or you do not have permission to view it."; return res.status(status.NotFound).redirect('/plans'); } - + // The plan.progress should be up-to-date from the database as it was updated in the /plans route // or when assets/plans are modified. If an immediate recalculation for this specific view is absolutely needed, // (e.g., if assets were modified without an immediate plan progress update elsewhere), @@ -148,13 +148,13 @@ module.exports = (middleware, users, plans, assets) => { // const currentProgress = await calculatePlanProgress(plan, assets, req.session.user._id); // plan.progress = currentProgress; // This would only update the 'plan' object for this render, not in DB - res.render('planDetail', { + res.render('planDetail', { user: req.session.user, plan: plan, // This plan object will have the progress from the database geoData: req.session.geoData, assets: userAssets, }); - + } catch (err) { console.error("Error fetching plan:", err); req.session.errMessage = "Could not load your plan. Please try again."; @@ -163,13 +163,13 @@ module.exports = (middleware, users, plans, assets) => { }); router.get('/newPlan', (req, res) => { - if(!req.session.user.financialData){ + if (!req.session.user.financialData) { req.session.errMessage = "Please complete your financial data before creating a plan."; return res.status(status.Unauthorized).redirect('/questionnaire'); } const errMessage = req.session.errMessage; - req.session.errMessage = ""; - res.render('newPlan', { + req.session.errMessage = ""; + res.render('newPlan', { user: req.session.user, errMessage: errMessage, geoData: req.session.geoData @@ -185,14 +185,14 @@ module.exports = (middleware, users, plans, assets) => { retirementLiabilities: joi.number().min(0).required(), }); - const validationOptions = { convert: true, abortEarly: false }; + const validationOptions = { convert: true, abortEarly: false }; const { error, value } = planSchema.validate(req.body, validationOptions); - if (error) { + if (error) { console.error("Plan validation error:", error.details); - req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); - res.status(status.BadRequest).redirect("/newPlan"); - return; + req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); + res.status(status.BadRequest).redirect("/newPlan"); + return; } const newPlan = { userId: new ObjectId(req.session.user._id), @@ -204,12 +204,12 @@ module.exports = (middleware, users, plans, assets) => { progress: "0" }; - try{ - await plans.insertOne({userId: new ObjectId(req.session.user._id), ...newPlan}); - req.session.errMessage = ""; - res.redirect('/plans'); + try { + await plans.insertOne({ userId: new ObjectId(req.session.user._id), ...newPlan }); + req.session.errMessage = ""; + res.redirect('/plans'); } - catch(err){ + catch (err) { console.error("Error saving plan:", err); req.session.errMessage = "An error occurred while saving your plan. Please try again."; res.status(status.InternalServerError).redirect("/newPlan"); @@ -217,7 +217,7 @@ module.exports = (middleware, users, plans, assets) => { }); router.get('/more', (req, res) => { - res.render('more', { + res.render('more', { user: req.session.user, geoData: req.session.geoData }); @@ -225,7 +225,7 @@ module.exports = (middleware, users, plans, assets) => { }); router.get('/profile', (req, res) => { - res.render('profile', { + res.render('profile', { user: req.session.user, errMessage: req.session.errMessage, geoData: req.session.geoData @@ -234,7 +234,7 @@ module.exports = (middleware, users, plans, assets) => { }); router.get('/settings', (req, res) => { - res.render('settings', { + res.render('settings', { user: req.session.user, geoData: req.session.geoData }); @@ -243,15 +243,15 @@ module.exports = (middleware, users, plans, assets) => { router.get('/questionnaire', (req, res) => { const errMessage = req.session.errMessage; - req.session.errMessage = ""; - res.render('questionnaire', { + req.session.errMessage = ""; + res.render('questionnaire', { user: req.session.user, errMessage: errMessage, geoData: req.session.geoData }); }); - router.post('/questionnaire', (req, res) => { + router.post('/questionnaire', (req, res) => { const questionnaireSchema = joi.object({ dob: joi.date().required(), education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(), @@ -261,22 +261,22 @@ module.exports = (middleware, users, plans, assets) => { assets: joi.number().min(0).required(), liabilities: joi.number().min(0).required(), }); - - const validationOptions = { convert: true, abortEarly: false }; + + const validationOptions = { convert: true, abortEarly: false }; const { error, value } = questionnaireSchema.validate(req.body, validationOptions); - - if (error) { + + if (error) { console.error("Questionnaire validation error:", error.details); - req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); - res.status(status.BadRequest).redirect("/questionnaire"); - return; + req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); + res.status(status.BadRequest).redirect("/questionnaire"); + return; } - - users.updateOne( - { _id: new ObjectId(req.session.user._id) }, - { - $set: { - financialData: true, + + users.updateOne( + { _id: new ObjectId(req.session.user._id) }, + { + $set: { + financialData: true, dob: value.dob, education: value.education, maritalStatus: value.maritalStatus, @@ -284,27 +284,27 @@ module.exports = (middleware, users, plans, assets) => { expenses: value.expenses, assets: value.assets, liabilities: value.liabilities, - } + } } - ).then((result) => { + ).then((result) => { if (result.matchedCount === 0) { console.log(`User not found during questionnaire update: ${req.session.user.email}`); - req.session.errMessage = "User session invalid. Please log in again."; - res.status(status.NotFound).redirect("/login"); + req.session.errMessage = "User session invalid. Please log in again."; + res.status(status.NotFound).redirect("/login"); return; } if (result.modifiedCount === 0 && result.matchedCount === 1) { console.log(`User questionnaire data unchanged (already up-to-date): ${req.session.user.email}`); } - + req.session.user.financialData = true; - req.session.errMessage = ""; - res.status(status.Ok).redirect("/home"); - - }).catch(err => { + req.session.errMessage = ""; + res.status(status.Ok).redirect("/home"); + + }).catch(err => { console.error("Error updating questionnaire in database:", err); req.session.errMessage = "An error occurred while saving your information. Please try again."; - res.status(status.InternalServerError).redirect("/questionnaire"); + res.status(status.InternalServerError).redirect("/questionnaire"); }); }); @@ -320,7 +320,7 @@ module.exports = (middleware, users, plans, assets) => { if (valid.err) { req.session.errMessage = "Invalid input", - res.status(status.BadRequest); + res.status(status.BadRequest); return res.redirect("/profile"); } @@ -343,20 +343,20 @@ module.exports = (middleware, users, plans, assets) => { ).then((result) => { if (result.matchedCount === 0) { console.log(`User not found during account update: ${req.session.email}`); - req.session.errMessage = "User session invalid. Please log in again."; - res.status(status.NotFound).redirect("/login"); + req.session.errMessage = "User session invalid. Please log in again."; + res.status(status.NotFound).redirect("/login"); return; } if (result.modifiedCount === 0 && result.matchedCount === 1) { console.log(`User account data unchanged (already up-to-date): ${req.session.email}`); } - - req.session.errMessage = ""; - return res.status(status.Ok).redirect("/profile"); - }).catch(err => { + + req.session.errMessage = ""; + return res.status(status.Ok).redirect("/profile"); + }).catch(err => { console.error("Error updating account in database:", err); req.session.errMessage = "An error occurred while saving your information. Please try again."; - return res.status(status.InternalServerError).redirect("/profile"); + return res.status(status.InternalServerError).redirect("/profile"); }); }); @@ -375,7 +375,7 @@ module.exports = (middleware, users, plans, assets) => { if (valid.err) { req.session.errMessage = "Invalid input", - res.status(status.BadRequest); + res.status(status.BadRequest); return res.redirect("/assets"); } @@ -402,7 +402,7 @@ module.exports = (middleware, users, plans, assets) => { } }); - req.session.errMessage = ""; + req.session.errMessage = ""; return res.status(status.Ok).redirect("/assets"); }); @@ -415,18 +415,18 @@ module.exports = (middleware, users, plans, assets) => { req.session.errMessage = "Invalid input"; return res.status(status.BadRequest).redirect("/assets"); } - + const valid = assetSchema.validate(req.body); if (valid.err) { req.session.errMessage = "Invalid input", - res.status(status.BadRequest); + res.status(status.BadRequest); return res.redirect("/assets"); } if (req.body.userId != req.session.user._id) { req.session.errMessage = "Cannot change asset owner", - res.status(status.BadRequest); + res.status(status.BadRequest); return res.redirect("/assets"); } @@ -440,11 +440,11 @@ module.exports = (middleware, users, plans, assets) => { update.value = parseFloat(update.value); delete update.id; delete update.userId; - + assets.updateOne( { "_id": new ObjectId(req.body.id) }, { $set: update }, - ).then((result) => { + ).then((result) => { if (result.matchedCount === 0) { console.log(`Asset not found: ${req.body.id}`); req.session.errMessage = "Unable to update asset"; @@ -453,14 +453,14 @@ module.exports = (middleware, users, plans, assets) => { if (result.modifiedCount === 0 && result.matchedCount === 1) { console.log(`Asset data unchanged (already up-to-date): ${req.body.id}`); } - - req.session.errMessage = ""; - return res.status(status.Ok).redirect("/assets"); - - }).catch((err) => { + + req.session.errMessage = ""; + return res.status(status.Ok).redirect("/assets"); + + }).catch((err) => { console.error("Error updating asset: ", err); req.session.errMessage = "An error occurred while saving your information. Please try again."; - return res.status(status.InternalServerError).redirect("/assets"); + return res.status(status.InternalServerError).redirect("/assets"); }); }); @@ -469,7 +469,7 @@ module.exports = (middleware, users, plans, assets) => { assets.deleteOne( { "_id": id } - ).then((result) => { + ).then((result) => { if (result.deletedCount === 0) { console.error(`Asset not found: ${req.body.id}`); req.session.errMessage = "Unable to delete asset. Please try again."; @@ -481,11 +481,11 @@ module.exports = (middleware, users, plans, assets) => { req.session.errMessage = "An error occurred while deleting an asset. Please try again."; return res.status(status.InternalServerError).redirect("/assets"); } - - req.session.errMessage = ""; - return res.status(status.Ok).redirect("/assets"); - - }).catch((err) => { + + req.session.errMessage = ""; + return res.status(status.Ok).redirect("/assets"); + + }).catch((err) => { console.error("Error deleting asset: ", err); req.session.errMessage = "An error occurred while deleting an asset. Please try again."; return res.status(status.InternalServerError).redirect("/assets"); @@ -535,7 +535,7 @@ module.exports = (middleware, users, plans, assets) => { if (!req.session.geoData.country) { const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${req.params.lat},${req.params.lon}&result_type=country&key=${process.env.GEOLOCATION_API}`); const data = await response.json(); - + country = data.results[0].formatted_address; let results = await getRates(country); req.session.geoData = { diff --git a/src/views/dashboard.ejs b/src/views/dashboard.ejs index 519832d..d36b979 100644 --- a/src/views/dashboard.ejs +++ b/src/views/dashboard.ejs @@ -1,5 +1,5 @@ <%- include("./partials/fileHeader") %> -<%- include("./partials/header") %> + <%- include("./partials/header") %>
@@ -22,74 +22,94 @@ class="block text-right antialiased tracking-normal font-sans text-2xl font-semibold leading-snug text-blue-gray-900"> $53k -
-

- Make this percent bar or graph or something  than lastweek -

-
- -
-
-

- Retirement goal

-

- $53k

-
-
-

- Make this percent bar or graph or something  than last - week -

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
-
-

- Retirement goal

-

- $53k

-
-
-

- Make this percent bar or graph or something  than last - week -

-
-
-
-
- -
-
-
+ +
+
+ +
+ + + +
+

+ Retirement goal

+

+ $53k

+
+
+

+ Make this percent bar or graph or something +  than lastweek +

+
+
+
+
+

+ Retirement goal

+

+ $53k

+
+
+

+ Make this percent bar or graph or something +  than last + week +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

+ Retirement goal

+

+ $53k

+
+
+

+ Make this percent bar or graph or something +  than last + week +

+
+
+
+
+
+
+
+ <%- include("./partials/navBar") %> <%- include("./partials/scriptLoader") %> -<%- include("./partials/footer") %> \ No newline at end of file +<%- include("./partials/footer") %> diff --git a/src/views/forgotPass.ejs b/src/views/forgotPass.ejs new file mode 100644 index 0000000..811c32d --- /dev/null +++ b/src/views/forgotPass.ejs @@ -0,0 +1,38 @@ +<%- include("./partials/fileHeader") %> + +
+
+
+
+

Reset Password

+
+ <% if (reset==='' ) { %> +
+ + +
+ <% } else { %> +
+

Reset link sent.

+
+ <% } %> + + +
+ <%= error%> +
+ <% if (reset==='' ) { %> +
+ +
+ <% } %> +
+
+
+
+ + <%- include("./partials/footer") %> <%- include("./partials/footer") %> diff --git a/src/views/login.ejs b/src/views/login.ejs index c8dbcbd..d35800f 100644 --- a/src/views/login.ejs +++ b/src/views/login.ejs @@ -25,7 +25,7 @@
- Forgot Password? + Forgot Password?
@@ -41,4 +41,4 @@
-<%- include("./partials/footer") %> \ No newline at end of file +<%- include("./partials/footer") %> diff --git a/src/views/resetPass.ejs b/src/views/resetPass.ejs new file mode 100644 index 0000000..2673347 --- /dev/null +++ b/src/views/resetPass.ejs @@ -0,0 +1,36 @@ +<%- include("./partials/fileHeader") %> + +
+
+
+
+

Reset Password

+
+ +
+ + +
+
+ + +
+ <% if (typeof errMessage !=='undefined' ) { %> +
+ <%= errMessage %> +
+ <% } %> +
+ +
+
+
+
+
+ <%- include("./partials/footer") %> diff --git a/src/views/signup.ejs b/src/views/signup.ejs index 7180cb5..840e839 100644 --- a/src/views/signup.ejs +++ b/src/views/signup.ejs @@ -1,46 +1,46 @@ <%- include("./partials/fileHeader") %> -<%- include("./partials/headerStart") %> + <%- include("./partials/headerStart") %> -
-
-
-
-

Signup

-
-
- - -
-
- - -
-
- - - - -
-
<%= errMessage %>
-
- -
-
- - Login -
-
-
-
-
+
+
+
+
+

Signup

+
+
+ + +
+
+ + +
+
+ + + + +
+
+ <%= errMessage %> +
+
+ +
+
+ + Login +
+
+
-<%- include("./partials/footer") %> \ No newline at end of file + <%- include("./partials/footer") %>