aboutsummaryrefslogtreecommitdiff
path: root/interface
diff options
context:
space:
mode:
Diffstat (limited to 'interface')
-rw-r--r--interface/config/db.config.js9
-rw-r--r--interface/controllers/citizens.controller.js27
-rw-r--r--interface/controllers/users.controller.js18
-rw-r--r--interface/functions/index.js5
-rw-r--r--interface/models/index.js2
-rw-r--r--interface/models/users.model.js20
-rw-r--r--interface/package-lock.json224
-rw-r--r--interface/package.json4
-rw-r--r--interface/public/login.js39
-rw-r--r--interface/public/logout.js5
-rw-r--r--interface/public/router.js7
-rw-r--r--interface/public/verify.js21
-rw-r--r--interface/routes/api/index.js80
-rw-r--r--interface/routes/index.js22
-rw-r--r--interface/views/citizens.ejs51
-rw-r--r--interface/views/index.ejs103
-rw-r--r--interface/views/partials/head.ejs7
-rw-r--r--interface/views/partials/navbar.ejs17
-rw-r--r--interface/views/partials/scripts.ejs9
19 files changed, 610 insertions, 60 deletions
diff --git a/interface/config/db.config.js b/interface/config/db.config.js
index 9118d6c..ea90d0c 100644
--- a/interface/config/db.config.js
+++ b/interface/config/db.config.js
@@ -1,14 +1,15 @@
+require('dotenv').config();
module.exports = {
// The name of the database
- database: 'WelfareSchemes',
+ database: process.env.DATABASE,
// The username used to connect to the database
- username: 'postgres',
+ username: process.env.USERNAME,
// The password used to connect to the database
- password: 'lucifer',
+ password: process.env.PASSWORD,
// The dialect of the database you are connecting to
dialect: 'postgres',
// The host of the database
- host: 'localhost',
+ host: process.env.HOST,
// The port of the database
port: 5432,
// Setup pool of connections to the database
diff --git a/interface/controllers/citizens.controller.js b/interface/controllers/citizens.controller.js
index 64522b1..b50ee74 100644
--- a/interface/controllers/citizens.controller.js
+++ b/interface/controllers/citizens.controller.js
@@ -1,11 +1,26 @@
const db = require("../models");
const citizens = db.citizens;
-const op = db.Sequelize.Op;
// Retrieve all citizens from the database. Limit the number of citizens returned to 10.
exports.findXCitizens = () => {
- const limit = 10;
- return citizens.findAll({
- limit: limit
- });
-}
+ const limit = 10;
+ return citizens.findAll({
+ limit: limit,
+ });
+};
+
+// Get total number of male and female citizens
+exports.findGenderDistribution = () => {
+ // group by the 'gender' column
+
+ /**
+ * This code is equivalent to the following SQL query:
+ * select count(gender), gender from citizens group by gender;
+ */
+
+ return citizens.findAll({
+ group: ["gender"],
+ attributes: ["gender", [db.sequelize.fn("COUNT", "gender"), "genderCount"]],
+ raw: true
+ });
+};
diff --git a/interface/controllers/users.controller.js b/interface/controllers/users.controller.js
new file mode 100644
index 0000000..fff4469
--- /dev/null
+++ b/interface/controllers/users.controller.js
@@ -0,0 +1,18 @@
+const { users } = require("../models");
+const db = require("../models");
+const citizens = db.citizens;
+const op = db.Sequelize.Op;
+
+// Create a new user
+exports.create = (username, password) => {
+ return users.create({ username, password });
+}
+
+// Get a user by their username
+exports.findByUsername = (username) => {
+ return users.findOne({
+ where: {
+ username: username
+ }
+ });
+}
diff --git a/interface/functions/index.js b/interface/functions/index.js
new file mode 100644
index 0000000..034dc80
--- /dev/null
+++ b/interface/functions/index.js
@@ -0,0 +1,5 @@
+const jwt = require('jsonwebtoken');
+
+exports.verifyJWT = (jwtToken) => {
+ return jwt.verify(jwtToken, process.env.JWT_SECRET);
+}
diff --git a/interface/models/index.js b/interface/models/index.js
index 852fb65..e799e1e 100644
--- a/interface/models/index.js
+++ b/interface/models/index.js
@@ -18,7 +18,9 @@ const sequelize = new Sequelize(databaseConfig.database, databaseConfig.username
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
+db.users = require('./users.model')(Sequelize, sequelize);
db.citizens = require('./citizens.model.js')(Sequelize, sequelize);
db.village_master = require('./village_master.model.js')(Sequelize, sequelize);
+db.bank_master = require('./bank_master.model.js')(Sequelize, sequelize);
module.exports = db;
diff --git a/interface/models/users.model.js b/interface/models/users.model.js
new file mode 100644
index 0000000..df8b276
--- /dev/null
+++ b/interface/models/users.model.js
@@ -0,0 +1,20 @@
+module.exports = (Sequelize, sequelize) => {
+ const Users = sequelize.define("users", {
+ user_id: {
+ type: Sequelize.INTEGER,
+ allowNull: false,
+ primaryKey: true,
+ autoIncrement: true,
+ },
+ username: {
+ type: Sequelize.STRING(155),
+ allowNull: false,
+ unique: true,
+ },
+ password: {
+ type: Sequelize.STRING(155),
+ allowNull: false,
+ }
+ });
+ return Users;
+} \ No newline at end of file
diff --git a/interface/package-lock.json b/interface/package-lock.json
index e4be03a..dc4079f 100644
--- a/interface/package-lock.json
+++ b/interface/package-lock.json
@@ -9,10 +9,14 @@
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"dependencies": {
+ "async": "^3.2.3",
+ "bcryptjs": "^2.4.3",
"body-parser": "^1.20.0",
"cors": "^2.8.5",
+ "dotenv": "^16.0.0",
"ejs": "^3.1.7",
"express": "^4.17.3",
+ "jsonwebtoken": "^8.5.1",
"pg": "^8.7.3",
"pg-hstore": "^2.3.4",
"sequelize": "^6.19.0"
@@ -143,6 +147,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -283,6 +292,11 @@
"node": ">=8"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
"node_modules/buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
@@ -585,6 +599,14 @@
"node": ">=8"
}
},
+ "node_modules/dotenv": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
+ "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/dottie": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
@@ -596,6 +618,14 @@
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -1174,6 +1204,59 @@
"integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
"dev": true
},
+ "node_modules/jsonwebtoken": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+ "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^5.6.0"
+ },
+ "engines": {
+ "node": ">=4",
+ "npm": ">=1.4.28"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "node_modules/jsonwebtoken/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/keyv": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
@@ -1200,6 +1283,41 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+ },
"node_modules/lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
@@ -2499,6 +2617,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
+ },
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -2603,6 +2726,11 @@
"fill-range": "^7.0.1"
}
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
"buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
@@ -2824,6 +2952,11 @@
"is-obj": "^2.0.0"
}
},
+ "dotenv": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
+ "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q=="
+ },
"dottie": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
@@ -2835,6 +2968,14 @@
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -3275,6 +3416,54 @@
"integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
"dev": true
},
+ "jsonwebtoken": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
+ "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
+ "requires": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ }
+ }
+ },
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"keyv": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
@@ -3298,6 +3487,41 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
+ },
+ "lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
+ },
+ "lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
+ },
+ "lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
+ },
+ "lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+ },
+ "lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
+ },
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
+ },
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
diff --git a/interface/package.json b/interface/package.json
index 87ca23a..9cca33f 100644
--- a/interface/package.json
+++ b/interface/package.json
@@ -12,10 +12,14 @@
"author": "Kumar Priyansh <[email protected]>",
"license": "GPL-3.0-or-later",
"dependencies": {
+ "async": "^3.2.3",
+ "bcryptjs": "^2.4.3",
"body-parser": "^1.20.0",
"cors": "^2.8.5",
+ "dotenv": "^16.0.0",
"ejs": "^3.1.7",
"express": "^4.17.3",
+ "jsonwebtoken": "^8.5.1",
"pg": "^8.7.3",
"pg-hstore": "^2.3.4",
"sequelize": "^6.19.0"
diff --git a/interface/public/login.js b/interface/public/login.js
new file mode 100644
index 0000000..3396f81
--- /dev/null
+++ b/interface/public/login.js
@@ -0,0 +1,39 @@
+$(".ui.form").form({
+ fields: {
+ username: "empty",
+ password: "empty",
+ },
+});
+
+function login(event) {
+ event.preventDefault();
+ const username = $("#username").val().trim();
+ const password = $("#password").val().trim();
+
+ // make sure username is a-zA-Z0-9
+ if (!username.match(/^[a-zA-Z0-9]+$/)) {
+ alert("Username must be alphanumeric");
+ return;
+ } else {
+ // make request to /api/login
+ fetch("/api/login", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ username,
+ password,
+ }),
+ }).then((response) => {
+ if (response.status === 200) {
+ response.json().then((data) => {
+ localStorage.setItem("token", data.token);
+ window.location.href = "/";
+ });
+ } else {
+ alert("Invalid username or password");
+ }
+ });
+ }
+}
diff --git a/interface/public/logout.js b/interface/public/logout.js
new file mode 100644
index 0000000..401f06d
--- /dev/null
+++ b/interface/public/logout.js
@@ -0,0 +1,5 @@
+function logout(event) {
+ event.preventDefault();
+ localStorage.removeItem("token");
+ window.location.href = "/";
+}
diff --git a/interface/public/router.js b/interface/public/router.js
new file mode 100644
index 0000000..23f8115
--- /dev/null
+++ b/interface/public/router.js
@@ -0,0 +1,7 @@
+function route(page) {
+ if (!localStorage.getItem("token")) {
+ $("#loginButton").click();
+ } else {
+ window.location.href = page;
+ }
+} \ No newline at end of file
diff --git a/interface/public/verify.js b/interface/public/verify.js
new file mode 100644
index 0000000..94025f8
--- /dev/null
+++ b/interface/public/verify.js
@@ -0,0 +1,21 @@
+const token = localStorage.getItem("token");
+if (token) {
+ fetch("/api/verify", {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${token}`,
+ }
+ }).then(response => {
+ if (response.status === 200) {
+ $('#loginButton').hide();
+ $('#logoutButton').show();
+ } else {
+ window.location.href = "/";
+ $('#loginButton').show();
+ $('#logoutButton').hide();
+ }
+ });
+} else {
+ $('#loginButton').show();
+ $('#logoutButton').hide();
+}
diff --git a/interface/routes/api/index.js b/interface/routes/api/index.js
new file mode 100644
index 0000000..f21720b
--- /dev/null
+++ b/interface/routes/api/index.js
@@ -0,0 +1,80 @@
+// API for logging in
+const express = require("express");
+const router = express.Router();
+const jwt = require("jsonwebtoken");
+const bcyrpt = require("bcryptjs");
+const userController = require("../../controllers/users.controller");
+const { verifyJWT } = require("../../functions");
+require("dotenv").config();
+
+router.post("/verify", (req, res) => {
+ // get token from auth header
+ const token = req.headers.authorization;
+ // verify token
+ const decoded = verifyJWT(token.replace("Bearer ", ""));
+ if (decoded) {
+ res.status(200).json({
+ message: "Token is valid",
+ decoded: decoded,
+ });
+ } else {
+ res.status(401).json({
+ message: "Token is invalid",
+ });
+ }
+});
+
+router.post("/create", (req, res) => {
+ const { username, password } = req.body;
+ const saltRounds = 10;
+ const hash = bcyrpt.hashSync(password, saltRounds);
+ userController.create(username, hash).then((user) => {
+ const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, {
+ expiresIn: "1h",
+ });
+ res.json({
+ token,
+ user,
+ });
+ });
+});
+
+router.post("/login", (req, res) => {
+ const { username, password } = req.body;
+ // get user by username
+ userController.findByUsername(username).then((user) => {
+ if (!user) {
+ return res.status(400).json({
+ message: "User not found",
+ });
+ }
+ // check if password is correct
+ bcyrpt.compare(password, user.password).then((isMatch) => {
+ if (!isMatch) {
+ return res.status(400).json({
+ message: "Invalid credentials",
+ });
+ }
+ // generate jwt
+ const payload = {
+ id: user.id,
+ username: user.username,
+ };
+ jwt.sign(
+ payload,
+ process.env.JWT_SECRET,
+ {
+ expiresIn: 3600,
+ },
+ (err, token) => {
+ if (err) throw err;
+ res.json({
+ token,
+ });
+ }
+ );
+ });
+ });
+});
+
+module.exports = router;
diff --git a/interface/routes/index.js b/interface/routes/index.js
index 17c2021..12cd088 100644
--- a/interface/routes/index.js
+++ b/interface/routes/index.js
@@ -1,14 +1,28 @@
const express = require("express");
const router = express.Router();
const citizensController = require("../controllers/citizens.controller");
+const api = require('./api');
-// Setup Hello World route
-router.get("/", (req, res) => {
+// Setup api routes
+router.use('/api', api);
+
+
+router.get('/', (req, res) => {
+ Promise.all([citizensController.findGenderDistribution()]).then(results => {
+ const [genderDistribution] = results;
+ res.render('index', {
+ title: 'Home Page',
+ genderDistribution
+ });
+ });
+});
+
+router.get("/citizens", (req, res) => {
// Get the citizens from the database
citizensController.findXCitizens().then(citizens => {
- res.render("index", {
+ res.render("citizens", {
citizens: citizens,
- title: "Hello World"
+ title: "Citizens Data"
});
});
});
diff --git a/interface/views/citizens.ejs b/interface/views/citizens.ejs
new file mode 100644
index 0000000..97c98fa
--- /dev/null
+++ b/interface/views/citizens.ejs
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <%- include('partials/head') %>
+ </head>
+
+ <body>
+ <%- include('partials/navbar') %>
+ <table class="ui selectable table">
+ <thead>
+ <tr>
+ <th>Citizen ID</th>
+ <th>First Name</th>
+ <th>Middle Name</th>
+ <th>Last Name</th>
+ <th>Address</th>
+ <th>Mobile Number</th>
+ <th>Date of Birth</th>
+ <th>Gender</th>
+ <th>Marital Status</th>
+ <th>Disabled</th>
+ <th>Disabled Percentage</th>
+ <th>Caste</th>
+ <th>Village ID</th>
+ </tr>
+ </thead>
+ <tbody>
+ <% for(var i=0; i < citizens.length; i++) { %>
+ <tr>
+ <td><%= citizens[i].citizen_id %></td>
+ <td><%= citizens[i].first_name %></td>
+ <td>
+ <%= citizens[i].middle_name ? citizens[i].middle_name : '-' %>
+ </td>
+ <td><%= citizens[i].last_name %></td>
+ <td><%= citizens[i].address %></td>
+ <td><%= citizens[i].mobile_num %></td>
+ <td><%= citizens[i].dob %></td>
+ <td><%= citizens[i].gender %></td>
+ <td><%= citizens[i].marital_status %></td>
+ <td><%= citizens[i].disabled %></td>
+ <td><%= citizens[i].disbaled_percentage %></td>
+ <td><%= citizens[i].caste %></td>
+ <td><%= citizens[i].village_id %></td>
+ </tr>
+ <% } %>
+ </tbody>
+ </table>
+ </body>
+ <%- include('partials/scripts') %>
+</html>
diff --git a/interface/views/index.ejs b/interface/views/index.ejs
index 827334b..99dc89b 100644
--- a/interface/views/index.ejs
+++ b/interface/views/index.ejs
@@ -5,46 +5,69 @@
</head>
<body>
+ <div class="ui modal login">
+ <i class="close icon"></i>
+ <div class="header">Log in to continue</div>
+ <div
+ class="ui padded container segment"
+ style="border: none; box-shadow: none"
+ >
+ <form class="ui form" method="post" onsubmit="login(event)">
+ <div class="field">
+ <label>Username</label>
+ <input
+ placeholder="Username"
+ name="username"
+ type="text"
+ autocomplete="off"
+ id="username"
+ />
+ </div>
+ <div class="field">
+ <label>Password</label>
+ <input
+ type="password"
+ name="password"
+ placeholder="password"
+ id="password"
+ />
+ </div>
+ <div class="ui primary submit button" id="submit">Submit</div>
+ <div class="ui error message"></div>
+ </form>
+ </div>
+ </div>
<%- include('partials/navbar') %>
- <table class="ui selectable table">
- <thead>
- <tr>
- <th>Citizen ID</th>
- <th>First Name</th>
- <th>Middle Name</th>
- <th>Last Name</th>
- <th>Address</th>
- <th>Mobile Number</th>
- <th>Date of Birth</th>
- <th>Gender</th>
- <th>Marital Status</th>
- <th>Disabled</th>
- <th>Disabled Percentage</th>
- <th>Caste</th>
- <th>Village ID</th>
- </tr>
- </thead>
- <tbody>
- <% for(var i=0; i < citizens.length; i++) { %>
- <tr>
- <td><%= citizens[i].citizen_id %></td>
- <td><%= citizens[i].first_name %></td>
- <td>
- <%= citizens[i].middle_name ? citizens[i].middle_name : '-' %>
- </td>
- <td><%= citizens[i].last_name %></td>
- <td><%= citizens[i].address %></td>
- <td><%= citizens[i].mobile_num %></td>
- <td><%= citizens[i].dob %></td>
- <td><%= citizens[i].gender %></td>
- <td><%= citizens[i].marital_status %></td>
- <td><%= citizens[i].disabled %></td>
- <td><%= citizens[i].disbaled_percentage %></td>
- <td><%= citizens[i].caste %></td>
- <td><%= citizens[i].village_id %></td>
- </tr>
- <% } %>
- </tbody>
- </table>
+ <div class="ui main container segment">
+ <div class="ui cards">
+ <div class="card">
+ <div class="content">
+ <div class="header">Citizens</div>
+ <div class="description">
+ <pre>
+ Gender Statistics:<br>
+ =====================<br>
+ Male: <%= genderDistribution[0].genderCount %><br>
+ Female: <%= genderDistribution[1].genderCount %><br>
+ ---------------------<br>
+ Total: <%= genderDistribution[0].genderCount + genderDistribution[1].genderCount %>
+ </pre>
+ Provides the details of all the citizens in the database
+ </div>
+ </div>
+ <div class="extra content">
+ <div class="ui basic green button" onclick="route('citizens')">
+ View Data
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
</body>
+ <%- include('partials/scripts') %>
+ <script>
+ $(".ui.dropdown").dropdown();
+ $(".login.modal").modal("attach events", ".loginButton", "show");
+ </script>
+ <script src="/login.js"></script>
</html>
diff --git a/interface/views/partials/head.ejs b/interface/views/partials/head.ejs
index c43233a..1296994 100644
--- a/interface/views/partials/head.ejs
+++ b/interface/views/partials/head.ejs
@@ -3,5 +3,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welfare Schemes | <%= title %></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css">
-<script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
-<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script>
+<style>
+ pre, code{
+ white-space:normal;
+ }
+</style>
diff --git a/interface/views/partials/navbar.ejs b/interface/views/partials/navbar.ejs
index 6a46ff3..34116ea 100644
--- a/interface/views/partials/navbar.ejs
+++ b/interface/views/partials/navbar.ejs
@@ -1,5 +1,5 @@
<div class="ui menu">
- <a class="item active"> Home </a>
+ <a class="item"> Home </a>
<div class="ui pointing dropdown link item">
<span class="text">View Data</span>
<i class="dropdown icon"></i>
@@ -42,7 +42,16 @@
</div>
</div>
</div>
+ <div class="right menu">
+ <div class="item">
+ <div class="ui icon input">
+ <input type="text" placeholder="Search..." />
+ <i class="search link icon"></i>
+ </div>
+ </div>
+ <a class="ui item loginButton" id="loginButton"> Log in </a>
+ <a class="ui item loginButton" id="logoutButton" onclick="logout(event)">
+ Log out</a
+ >
+ </div>
</div>
-<script>
- $(".ui.dropdown").dropdown();
-</script>
diff --git a/interface/views/partials/scripts.ejs b/interface/views/partials/scripts.ejs
new file mode 100644
index 0000000..a90467a
--- /dev/null
+++ b/interface/views/partials/scripts.ejs
@@ -0,0 +1,9 @@
+<script
+ src="https://code.jquery.com/jquery-3.1.1.min.js"
+ integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
+ crossorigin="anonymous"
+></script>
+<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script>
+<script src="/verify.js"></script>
+<script src="/router.js"></script>
+<script src="/logout.js"></script>