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") %>
-
-
-
-
-
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.
-
-
-
-
-
-
-
-
-
-
© 2025 RCalculator. All rights reserved.
-
+
+
+
+
+
Welcome to RCalculator, your partner in building a secure and fulfilling financial future.
+
+
+
+
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.
+
-
-
-
-
-
+
+
+
+
-
-
\ 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
-
-
-
-
-
© 2025 RCalculator. All rights reserved.
-
-
- <%- include("./partials/navBar") %>
-
-
-
+<%- 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
-
-
-
-
-
© 2025 RCalculator. All rights reserved.
-
-
-
-
-
-
+<%- 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") %>
-
+
@@ -285,8 +274,5 @@
- <%- 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") %>
-
-
-
-
-
Planning your retirement is a crucial step towards financial security and a happy future.
-
-
-
-
-