diff --git a/app.js b/app.js index e96e45a..54804bd 100644 --- a/app.js +++ b/app.js @@ -3,8 +3,10 @@ const MongoStore = require("connect-mongo"); const session = require("express-session"); const express = require('express'); const path = require('path'); +const joi = require('joi'); require('dotenv').config(); + const app = express(); const port = process.env.PORT || 3000; @@ -18,7 +20,7 @@ app.use(session({ store: MongoStore.create({ mongoUrl: `${mongoURI}${database}`, crypto: { secret: secret } }), resave: true, saveUninitialized: false, - cookie: { maxAge: 60000 }, + cookie: { maxAge: 3600000 }, })); app.set('view engine', 'ejs'); @@ -75,7 +77,7 @@ initDatabase().then(() => { // Import middleware & apply to user routes const middleware = require("./src/auth/middleware")(users); - app.use(require('./src/router/user')(middleware)); + app.use(require('./src/router/user')(middleware, users)); // 404 handler app.get('/*splat', (req, res) => { diff --git a/package-lock.json b/package-lock.json old mode 100755 new mode 100644 index 54d9320..95750e3 --- a/package-lock.json +++ b/package-lock.json @@ -354,27 +354,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -549,6 +528,7 @@ "version": "16.5.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -580,6 +560,7 @@ "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -969,13 +950,12 @@ } }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-symbols": { @@ -1479,6 +1459,29 @@ "url": "https://opencollective.com/nodemon" } }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -1976,16 +1979,15 @@ } }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/tar": { diff --git a/src/auth/authentication.js b/src/auth/authentication.js index 068f789..fe7c97d 100644 --- a/src/auth/authentication.js +++ b/src/auth/authentication.js @@ -8,17 +8,11 @@ module.exports = (users) => { router.get("/logout", (req, res) => { req.session.destroy(); - // res.status(status.Unauthorized); return res.redirect('/login'); }); router.post("/login", async (req, res) => { - - if (req.session.authenticated) { - res.redirect("/home"); - return res.status(status.Ok); - } - + const credentialSchema = joi.object({ email: joi.string().email().required(), password: joi.string().max(20).required(), @@ -45,9 +39,10 @@ module.exports = (users) => { return res.redirect("/login"); } + console.log("User logged in successfully"); + console.log("User email: " + req.body.email); req.session.authenticated = true; req.session.email = req.body.email; - req.session.errMessage = ""; res.redirect("/home"); return res.status(status.Ok); diff --git a/src/auth/middleware.js b/src/auth/middleware.js index 7ca7afe..8eeaff2 100644 --- a/src/auth/middleware.js +++ b/src/auth/middleware.js @@ -1,4 +1,5 @@ const status = require("../util/statuses"); +const session = require("express-session"); /** * createMiddleware returns a middleware function for express. diff --git a/src/database/connection.js b/src/database/connection.js index 0f52263..18ac54f 100644 --- a/src/database/connection.js +++ b/src/database/connection.js @@ -16,6 +16,7 @@ const connectMongo = async (mongoURI, databaseName) => { * getCollection object to interact with MongoDB * @param {MongoClient} dbo * @param {string} collection + * @return {MongoClient.collection} */ const getCollection = async (dbo, collection) => { return await dbo.collection(collection); diff --git a/src/router/user.js b/src/router/user.js index 3187328..dd8eaf6 100644 --- a/src/router/user.js +++ b/src/router/user.js @@ -1,6 +1,7 @@ const status = require("../util/statuses"); +const joi = require("joi"); -module.exports = (middleware) => { +module.exports = (middleware, users) => { const router = require("express").Router(); router.use(middleware); @@ -35,9 +36,76 @@ module.exports = (middleware) => { return res.status(status.Ok); }); - router.get('/logout', (req, res) => { - req.session.destroy(); - return res.redirect('/login'); + router.get('/questionnaire', (req, res) => { + const errMessage = req.session.errMessage; + req.session.errMessage = ""; + res.render('questionnaire', { user: req.user, errMessage: errMessage }); + }); + + router.post('/questionnaire', (req, res) => { + console.log("Questionnaire POST body:", req.body); + + const questionnaireSchema = joi.object({ + dob: joi.date().required(), + education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(), + maritalStatus: joi.string().valid('single', 'married', 'divorced', 'widowed').required(), + income: joi.number().min(0).required(), + expenses: joi.number().min(0).required(), + assets: joi.number().min(0).required(), + liabilities: joi.number().min(0).required(), + retirementAge: joi.number().min(18).max(120).required(), + retirementExpenses: joi.number().min(0).required(), + retirementAssets: joi.number().min(0).required(), + retirementLiabilities: joi.number().min(0).required(), + }); + + const validationOptions = { convert: true, abortEarly: false }; + const { error, value } = questionnaireSchema.validate(req.body, validationOptions); + + 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; + } + + users.updateOne( + { email: req.session.email }, + { + $set: { + financialData: true, + dob: value.dob, + education: value.education, + maritalStatus: value.maritalStatus, + income: value.income, + expenses: value.expenses, + assets: value.assets, + liabilities: value.liabilities, + retirementAge: value.retirementAge, + retirementExpenses: value.retirementExpenses, + retirementAssets: value.retirementAssets, + retirementLiabilities: value.retirementLiabilities, + } + } + ).then((result) => { + if (result.matchedCount === 0) { + console.log(`User not found during questionnaire update: ${req.session.email}`); + 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.email}`); + } + + 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"); + }); }); return router; diff --git a/src/views/aboutUs.ejs b/src/views/aboutUs.ejs index e5979bd..32ded54 100644 --- a/src/views/aboutUs.ejs +++ b/src/views/aboutUs.ejs @@ -1,56 +1,39 @@ - - - RCalculator - - - - - <%- include("./partials/headerStart") %> -
-
-
-

About RCalculator

-

Welcome to RCalculator, your partner in building a secure and fulfilling financial future.

-
+<%- include("./partials/fileHeader") %> +<%- include("./partials/headerStart") %> -
-

Our Mission

-

- Our mission is to empower you with the tools and insights needed to take control of your long-term financial goals. -

-
-

- We believe that planning for your desired future, especially retirement, shouldn't be daunting. It doesn't matter what your current age or career status is – starting now is what counts. -

-

- At RCalculator, we help you trace a personalized plan that suits your unique aspirations. We provide clear, actionable advice and projections to guide you on how to get there, whether you're just starting your career, mid-way through, or nearing retirement. -

-

- Let us help you navigate the path to financial independence and achieve the retirement lifestyle you envision. -

-
-
- -
-
- - - - - + +
+ + Get started + + +
+ + - - \ No newline at end of file +<%- include("./partials/footer") %> \ No newline at end of file diff --git a/src/views/assets.ejs b/src/views/assets.ejs index acaa431..a926f36 100644 --- a/src/views/assets.ejs +++ b/src/views/assets.ejs @@ -1,23 +1,9 @@ - - - - - - - Document - - - <%- include("./partials/header") %> -
- The Assets Page -
- - - +<%- include("./partials/fileHeader") %> +<%- include("./partials/header") %> + +
+ The Assets Page +
+ +<%- include("./partials/navBar") %> +<%- include("./partials/footer") %> \ No newline at end of file diff --git a/src/views/home.ejs b/src/views/home.ejs index bd514d7..3637163 100644 --- a/src/views/home.ejs +++ b/src/views/home.ejs @@ -1,26 +1,15 @@ - - +<%- include("./partials/fileHeader") %> +<%- include("./partials/header") %> - - - - - Document - +
+ <%- include("./partials/header") %> + Welcome: <%= user.email %> + The Dashboard Page + <% if(user.authenticated) { %> + <%= user.email %> + <%= user.errMessage %> + <% } %> +
- -
- <%- include("./partials/header") %> - Welcome: <%= user.email %> - The Dashboard Page -
- - - - +<%- include("./partials/navBar") %> +<%- include("./partials/footer") %> diff --git a/src/views/index.ejs b/src/views/index.ejs index 512ff5b..3c612b2 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -1,17 +1,6 @@ - - - - - - - - Document - - - <%- include("./partials/fileHeader") %> <%- include("./partials/header") %> - +
- <%- include("./partials/navBar") %> - <%- include("./partials/footer") %> - - - +<%- include("./partials/navBar") %> +<%- include("./partials/footer") %> \ No newline at end of file diff --git a/src/views/landing.ejs b/src/views/landing.ejs index 325ad13..d7a24d1 100644 --- a/src/views/landing.ejs +++ b/src/views/landing.ejs @@ -1,38 +1,24 @@ - - - RCalculator - - - - - <%- include("./partials/headerStart") %> -
-
-
-

Plan now, live better.

-

Planning your retirement is a crucial step towards financial security and a happy future.

- -
-
-
- - - - - + + + + +<%- include("./partials/footer") %> diff --git a/src/views/login.ejs b/src/views/login.ejs index cf46b63..c8dbcbd 100644 --- a/src/views/login.ejs +++ b/src/views/login.ejs @@ -1,22 +1,10 @@ - - +<%- include("./partials/fileHeader") %> - - - - - Login Page - - - - - - - -
+
+
-

Login

+

Login


@@ -51,7 +39,6 @@
- - +
- +<%- include("./partials/footer") %> \ No newline at end of file diff --git a/src/views/more.ejs b/src/views/more.ejs index 7884eaf..59e3acf 100644 --- a/src/views/more.ejs +++ b/src/views/more.ejs @@ -1,25 +1,10 @@ - - - - - - - Document - +<%- include("./partials/fileHeader") %> +<%- include("./partials/header") %> - - <%- include("./partials/header") %> -
- The More Page -
- - +
+ The More Page +
- +<%- include("./partials/navBar") %> +<%- include("./partials/footer") %> + \ No newline at end of file diff --git a/src/views/partials/fileHeader.ejs b/src/views/partials/fileHeader.ejs new file mode 100644 index 0000000..35b60ed --- /dev/null +++ b/src/views/partials/fileHeader.ejs @@ -0,0 +1,10 @@ + + + + + + + + RCalculator + + \ No newline at end of file diff --git a/src/views/partials/footer.ejs b/src/views/partials/footer.ejs new file mode 100644 index 0000000..8f457f3 --- /dev/null +++ b/src/views/partials/footer.ejs @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/src/views/partials/headerStart.ejs b/src/views/partials/headerStart.ejs index 5e07c61..b2b4f4b 100644 --- a/src/views/partials/headerStart.ejs +++ b/src/views/partials/headerStart.ejs @@ -20,7 +20,7 @@ Home
  • - About Us + Login
  • Contact us diff --git a/src/views/partials/navBar.ejs b/src/views/partials/navBar.ejs index edc5772..6b12cf0 100644 --- a/src/views/partials/navBar.ejs +++ b/src/views/partials/navBar.ejs @@ -26,5 +26,4 @@ More
  • - - + \ No newline at end of file diff --git a/src/views/plans.ejs b/src/views/plans.ejs index 9645deb..7f6e699 100644 --- a/src/views/plans.ejs +++ b/src/views/plans.ejs @@ -1,26 +1,9 @@ - - +<%- include("./partials/fileHeader") %> +<%- include("./partials/header") %> - - - - - Document - +
    + The Plans Page +
    - - <%- include("./partials/header") %> -
    - The Plans Page -
    - - - - +<%- include("./partials/navBar") %> +<%- include("./partials/footer") %> diff --git a/src/views/questionnaire.ejs b/src/views/questionnaire.ejs new file mode 100644 index 0000000..10a1b29 --- /dev/null +++ b/src/views/questionnaire.ejs @@ -0,0 +1,91 @@ +<%- include("./partials/fileHeader") %> +<%- include("./partials/header") %> + +
    +

    Welcome: <%= user.email %>

    +
    +

    Financial Questionnaire

    +
    +
    + + +
    +
    + + +
    +
    + +
    + + + + +
    +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    +
    +
    +
    + +<%- include("./partials/footer") %> \ No newline at end of file diff --git a/src/views/signup.ejs b/src/views/signup.ejs index 2345bbf..7180cb5 100644 --- a/src/views/signup.ejs +++ b/src/views/signup.ejs @@ -1,22 +1,11 @@ - - +<%- include("./partials/fileHeader") %> +<%- include("./partials/headerStart") %> - - - - - Signup - - - - - -
    +
    +
    -

    Signup

    +

    Signup


    @@ -52,7 +41,6 @@
    - - +
    - +<%- include("./partials/footer") %> \ No newline at end of file diff --git a/src/views/template.ejs b/src/views/template.ejs new file mode 100644 index 0000000..868ca17 --- /dev/null +++ b/src/views/template.ejs @@ -0,0 +1,9 @@ +<%- include("./partials/fileHeader") %> +<%- include("./partials/header") %> + +
    + +
    + +<%- include("./partials/navBar") %> +<%- include("./partials/footer") %> \ No newline at end of file