diff --git a/api.MD b/api.MD
index 1100817..b655bed 100644
--- a/api.MD
+++ b/api.MD
@@ -174,4 +174,4 @@ http://localhost/api/v0/sensor-data/data?week=1&sensorid=1&locationid=1&page=2&p
curl localhost/api/v0/user/register -H "Content-Type: application/json" -X POST -d '{"username": "testuser123", "password": "thisisthesystemuserpasswordnoob", "email": "testuser123@ecosaver.com", "address": "Nanyang Polytechnic 180 Ang Mo Kio Avenue 8 Singapore 569830", "phone": "12345678"}'
-curl localhost/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d '{"userid": "2", "permission": "canRead"}'
+curl localhost/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d '{"userid": "5", "permission": "canRead"}'
diff --git a/consumerWebsite/database/model/apiKeyModel.js b/consumerWebsite/database/model/apiKeyModel.js
index f644a14..2604d2f 100644
--- a/consumerWebsite/database/model/apiKeyModel.js
+++ b/consumerWebsite/database/model/apiKeyModel.js
@@ -61,6 +61,7 @@ const apikeyModel = sequelize.define(
timestamps: true,
}
);
+apikeyModel.belongsTo(userModel);
module.exports = { apikeyModel };
diff --git a/consumerWebsite/database/model/userModel.js b/consumerWebsite/database/model/userModel.js
index 561890c..fb4cbc2 100644
--- a/consumerWebsite/database/model/userModel.js
+++ b/consumerWebsite/database/model/userModel.js
@@ -19,6 +19,26 @@ const userModel = sequelize.define(
isNumeric: true,
},
},
+ firstname: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ length: 60,
+ validate: {
+ notEmpty: true,
+ len: [1, 60],
+ isAlpha: true, // will only allow letters
+ },
+ },
+ lastname: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ length: 60,
+ validate: {
+ notEmpty: true,
+ len: [1, 60],
+ isAlpha: true, // will only allow letters
+ },
+ },
username: {
type: DataTypes.STRING,
allowNull: false,
diff --git a/consumerWebsite/functions/apiDatabase.js b/consumerWebsite/functions/apiDatabase.js
index fcee670..f011846 100644
--- a/consumerWebsite/functions/apiDatabase.js
+++ b/consumerWebsite/functions/apiDatabase.js
@@ -3,45 +3,58 @@ const { apikeyModel } = require("../database/model/apikeyModel.js");
const { userModel } = require("../database/model/userModel.js");
const { Op, Sequelize } = require("sequelize");
const { generateUUID } = require("../functions/generateUUID.js");
-const { hashPassword , comparePassword , hashAPIKey } = require("../functions/bcrypt.js");
+const {
+ hashPassword,
+ comparePassword,
+ hashAPIKey,
+} = require("../functions/bcrypt.js");
-//helper function
+//getuser
+//api/v0/user/me
+async function getUserID(userid) {
+ //console.log(userid);
+ //console.log(userid.id);
+ let userRes = await userModel.findByPk(userid.id, {
+ attributes: {
+ exclude: ["password"],
+ },
+ });
+
+ if (!userRes) return false;
+ return userRes;
+
+}
-//api/v0/user/register
+
+//api/v0/auth/register
/* Registering new user
1) req.body is taken from html form or wtv
2) bcrpyt and hash the password on the server side
3) pass to db
*/
async function addUser(user) {
- console.log(user);
//hash password
let hash = await hashPassword(user.password);
- const addRes = await userModel.create({
+ const addRes = await userModel.create({
+ firstname: user.firstname,
+ lastname: user.lastname,
username: user.username,
password: hash,
email: user.email,
address: user.address,
phone: user.phone,
});
- if (addRes){
- return true;
- }
- else{
- return false;
- }
-}
-
-//add token to db
-async function addToken(userid , token) {
- console.log(userid);
- console.log(token);
-
+ if (addRes) {
+ return true;
+ } else {
+ return false;
+ }
}
+//api/v0/auth/login
async function loginUser(user) {
//look up username or email in db
const userRes = await userModel.findOne({
@@ -55,44 +68,19 @@ async function loginUser(user) {
},
],
},
- })
- //if user exists
- if (userRes){
- //compare password
- let match = await comparePassword(user.password, userRes.password);
- if (match){
- console.log(userRes.id);
- console.log(userRes.username);
+ });
+ // Make sure user exists
+ if (!userRes) return false;
+
+ // Compare passwords
+ let match = await comparePassword(user.password, userRes.password);
+ if (!match) return false;
+ //console.log('loginUser', userRes.id, userRes.username);
- //generate token
- let token = await generateUUID();
+ //generate token
+ let token = await addAPIKey(userRes.id, "auto-generated");
- //add to db
- addToken(userRes.id, token);
-
-
- //sucessful login
- /*
- 1) generate token
- 2) store in db and localstorage (maybe hash it?)
- 3) return userid and username and token and store in localstorage
- */
- return { token: token, userid: userRes.id, username: userRes.username };
- }
- else {
- return false;
- }
- }
-
- else{
- return false;
- }
-}
-
-
-async function getAPIKey() {
- const apikey = await apikeyModel.findAll();
- return apikey;
+ return { token: token, userid: userRes.id, username: userRes.username };
}
/*
@@ -103,27 +91,47 @@ async function getAPIKey() {
5) you give the user rowid-uuidv4
6) store in database
*/
+
+//can be used for api key or token. Both are the same logic
async function addAPIKey(userId, permission) {
- let token = await generateUUID();
- let usertoken = userId + "-" + token;
- let apikey = await hashAPIKey(token);
+ let hashtoken = await generateUUID();
+ let apikey = await hashAPIKey(hashtoken);
- console.log(token);
- console.log(apikey);
+ let token = await apikeyModel.create({
+ userid: userId,
+ apikey: apikey,
+ permission: permission,
+ });
- await apikeyModel.create({
- userid: userId,
- apikey: apikey,
- permission: permission
- });
-
-
- //user token with -
- return usertoken;
+ //user token with - tokenid is table id
+ return token.id + "-" + hashtoken;
}
+//api/v0/user/logout
+async function deleteUserToken(token) {
+ //get row id
+ let splitAuthToken = token.split("-");
+ let rowid = splitAuthToken[0];
+
+ //console.log(rowid);
+
+ //delete from db
+ let delRes = await apikeyModel.destroy({
+ where: {
+ id: rowid,
+ },
+ });
+
+ if (!delRes) return false;
+ return true;
+
+
+
+}
module.exports = {
+ getUserID,
addUser,
loginUser,
addAPIKey,
+ deleteUserToken,
};
diff --git a/consumerWebsite/functions/bcrypt.js b/consumerWebsite/functions/bcrypt.js
index bf644e0..f0b72a3 100644
--- a/consumerWebsite/functions/bcrypt.js
+++ b/consumerWebsite/functions/bcrypt.js
@@ -21,6 +21,8 @@ bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
});
*/
+
+//hash for pass or token lol doesnt matter
async function hashPassword(password) {
return await bcrypt.hash(password, saltRounds);
}
@@ -29,6 +31,7 @@ async function hashAPIKey(apikey) {
return await bcrypt.hash(apikey, saltRounds);
}
+//can be used to compare password or token
async function comparePassword(password, hash) {
return await bcrypt.compare(password, hash);
}
diff --git a/consumerWebsite/middleware/authChecker.js b/consumerWebsite/middleware/authChecker.js
index 343cd14..b12ffe2 100644
--- a/consumerWebsite/middleware/authChecker.js
+++ b/consumerWebsite/middleware/authChecker.js
@@ -1,4 +1,30 @@
-/*v
-1) check if token proided by JSON req is valid against db
-2) if valid its passed to next()
-*/
\ No newline at end of file
+const { apikeyModel } = require("../database/model/apiKeyModel");
+const { userModel } = require("../database/model/userModel");
+const { comparePassword } = require("../functions/bcrypt");
+
+async function auth(req, res, next){
+ try{
+ // let user = await Auth.checkToken({token: req.header('auth-token')});
+ let authToken = req.header('auth-token');
+ let splitAuthToken = authToken.split('-');
+ let rowid = splitAuthToken[0];
+ let suppliedToken = splitAuthToken.slice(1).join('-');
+
+ //get from db
+ let token = await apikeyModel.findByPk(rowid, {include: userModel});
+
+ //compare
+ let isMatch = await comparePassword(suppliedToken, token.apikey);
+ if (!isMatch) return false;
+
+ //else do logic
+ //pass hashed token to req.token (IMPORTANT ITS NOT PASSED TO CLIENT)
+ req.token = token
+ req.user = await token.getUser();
+ next();
+ }catch(error){
+ next(error);
+ }
+}
+
+module.exports = { auth };
diff --git a/consumerWebsite/modules/app.js b/consumerWebsite/modules/app.js
index 94d6c02..3b3d8ba 100644
--- a/consumerWebsite/modules/app.js
+++ b/consumerWebsite/modules/app.js
@@ -13,17 +13,17 @@ app.set("views", path.join(__dirname, "../views"));
app.set("view engine", "ejs");
// Have express server static content( images, CSS, browser JS) from the public
-// local folder.
app.use(express.static(path.join(__dirname, "../public")));
//middleware logic ( called by next() )
-//add token middeware upon login to validate routes that require token
+const auth = require("../middleware/authChecker");
+
//route logic
-app.use("/api/v0", require("../routes/api_routes")); //consumerWebsite\routes\api_routes.js
+app.use("/api/v0", require("../routes/api_routes"));
//render logic
-app.use("/", require("../routes/render")); //consumerWebsite\routes\render.js
+app.use("/", require("../routes/render"));
// Catch 404 and forward to error handler. If none of the above routes are
// used, this is what will be called.
diff --git a/consumerWebsite/public/css/all.css b/consumerWebsite/public/css/all.css
index 4e624d8..d3173e3 100644
--- a/consumerWebsite/public/css/all.css
+++ b/consumerWebsite/public/css/all.css
@@ -3801,3 +3801,15 @@
.fas {
font-family: 'Font Awesome 5 Free';
font-weight: 900; }
+
+ .contact-right p {
+ /* Your CSS styles here */
+ color: #4e3914;
+ font-size: 16px;
+ /* Add any other styles you need */
+}
+
+
+.card-text {
+ color: #000000;
+}
diff --git a/consumerWebsite/public/css/api.css b/consumerWebsite/public/css/api.css
new file mode 100644
index 0000000..d3d44b1
--- /dev/null
+++ b/consumerWebsite/public/css/api.css
@@ -0,0 +1,526 @@
+@charset "utf-8";
+
+/* RESET
+----------------------------------------------------------------------------------------*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size:100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+img, embed, object, video { max-width: 100%; }
+.ie6 img.full, .ie6 object.full, .ie6 embed, .ie6 video { width: 100%; }
+
+/* BASE
+----------------------------------------------------------------------------------------*/
+
+*{
+ -webkit-transition: all 0.3s ease;
+ -moz-transition: all 0.3s ease;
+ -o-transition: all 0.3s ease;
+ -ms-transition: all 0.3s ease;
+ transition: all 0.3s ease;
+}
+html,
+body{
+ position:relative;
+ min-height: 100%;
+ height: 100%;
+ -webkit-backface-visibility: hidden;
+ font-family: 'Roboto', sans-serif;
+}
+strong{
+ font-weight: 500;
+}
+i{
+ font-style: italic;
+}
+.overflow-hidden{
+ position: relative;
+ overflow: hidden;
+}
+.content a{
+ color: #00a8e3;
+ text-decoration: none;
+}
+.content a:hover{
+ text-decoration: underline;
+}
+.scroll-to-link{
+ cursor: pointer;
+}
+p, .content ul, .content ol{
+ font-size: 14px;
+ color: #ffffff;
+ margin-bottom: 16px;
+ line-height: 1.6;
+ font-weight: 300;
+}
+.content h1:first-child{
+ font-size: 1.333em;
+ color: #034c8f;
+ padding-top: 2.5em;
+ text-transform: uppercase;
+ border-top: 1px solid rgba(255,255,255,0.3);
+ border-top-width: 0;
+ margin-top: 0;
+ margin-bottom: 1.3em;
+ clear: both;
+}
+
+code,
+pre{
+ font-family: 'Source Code Pro', monospace;
+}
+.higlighted{
+ background-color: rgba(0,0,0,0.05);
+ padding: 3px;
+ border-radius: 3px;
+}
+
+/* LEFT-MENU
+----------------------------------------------------------------------------------------*/
+
+.left-menu{
+ position: fixed;
+ z-index: 3;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 300px;
+ box-sizing: border-box;
+ background-color: #f4f5f8;
+ overflow-x: hidden;
+ font-size: 18px;
+}
+.left-menu .content-infos {
+ position: relative;
+ padding: 12px 13.25%;
+ margin-bottom: 20px;
+}
+.left-menu .info {
+ position: relative;
+ font-size: 14px;
+ margin-top: 5px;
+ color: #777A7A;
+}
+.left-menu .info b {
+ font-weight: 500;
+ color: #034c8f;
+}
+.content-logo{
+ position: relative;
+ display: block;
+ width: 100%;
+ box-sizing: border-box;
+ padding: 1.425em 11.5%;
+ padding-right: 0;
+}
+.content-logo img{
+ display: inline-block;
+ max-width: 70%;
+ vertical-align: middle;
+}
+.content-logo span{
+ display: inline-block;
+ margin-left: 10px;
+ vertical-align: middle;
+ color: #323F4C;
+ font-size: 1.1em;
+}
+.content-menu{
+ margin: 2em auto 2em;
+ padding: 0 0 100px;
+}
+.content-menu ul{
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ line-height: 28px;
+}
+.content-menu ul li{
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ line-height: 0;
+}
+.content-menu ul li:hover,
+.content-menu ul li.active{
+ background-color:#DCDEE9;
+}
+.content-menu ul li:hover a,
+.content-menu ul li.active a{
+ color: #00a8e3;
+}
+@media (hover: none) {
+ .content-menu ul li:not(.active):hover {
+ background-color: inherit;
+ }
+ .content-menu ul li:not(.active):hover a {
+ color: #777A7A;
+ }
+}
+.content-menu ul li a{
+ padding: 12px 13.25%;
+ color: #777A7A;
+ letter-spacing: 0.025em;
+ line-height: 1.1;
+ display: block;
+ text-transform: capitalize;
+}
+
+/* CONTENT-PAGE
+----------------------------------------------------------------------------------------*/
+
+.content-page {
+ position: relative;
+ box-sizing: border-box;
+ margin-left: 300px;
+ z-index: 2;
+ background-color: #fff;
+ min-height: 100%;
+ padding-bottom: 1px;
+}
+.content-code{
+ width: 50%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: #323f4c;
+ border-color: #323f4c;
+}
+.content {
+ position: relative;
+ z-index: 30;
+}
+.content h1,
+.content h2,
+.content h3,
+.content h4,
+.content h5,
+.content h6,
+.content p,
+.content table,
+.content aside,
+.content dl,
+.content ul,
+.content ol,
+.content .central-overflow-x {
+ margin-right: 50%;
+ padding: 0 28px;
+ box-sizing: border-box;
+ display: block;
+ max-width: 680px;
+}
+.content .central-overflow-x {
+ margin-right: calc(50% + 28px);
+ margin-left: 28px;
+ padding: 0;
+ overflow-y: hidden;
+ max-width: 100%;
+ display: block;
+}
+.content p .central-overflow-x {
+ margin-right: 0;
+ margin-left: 0;
+}
+.break-word {
+ word-break: break-word;
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+}
+.content ul,
+.content ol {
+ padding: 0 44px;
+}
+.content h2,
+.content h3,
+.content h4,
+.content h5,
+.content h6 {
+ font-size: 15px;
+ margin-top: 2.5em;
+ margin-bottom: 0.8em;
+ color: #034c8f;
+ text-transform: uppercase;
+}
+.content h2{
+ font-size: 1.333em;
+}
+.content h4{
+ color: #00a8e3;
+ margin-top: 0;
+ text-transform: none;
+ font-size: 14px;
+ margin-bottom: 0.2em;
+}
+.content-page .content p,
+.content-page .content pre {
+ max-width: 680px;
+}
+.content pre,
+.content blockquote {
+ background-color: #323f4c;
+ border-color: #323f4c;
+ color: #fff;
+ padding: 0 28px 2em;
+ margin: 0;
+ width: 50%;
+ float: right;
+ clear: right;
+ box-sizing: border-box;
+}
+.content pre code, .content pre {
+ font-size: 12px;
+ line-height: 1.5;
+}
+.content blockquote,
+.content pre,
+.content pre code{
+ padding-top: 0;
+ margin-top: 0;
+}
+.content pre code{
+ margin-top: -2em;
+}
+.content table {
+ font-size: 0.825em;
+ margin-bottom: 1.5em;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+.content table tr:last-child {
+ border-bottom: 1px solid #ccc;
+}
+.content table th {
+ font-size: 0.925em;
+ padding: 5px 18px 5px 0;
+ border-bottom: 1px solid #ccc;
+ vertical-align: bottom;
+ text-align: left;
+ line-height: 1.6;
+}
+.content table td {
+ padding: 5px 18px 5px 0;
+ text-align: left;
+ vertical-align: top;
+ line-height: 1.6;
+ font-family: 'Roboto', sans-serif;
+ font-weight: 300;
+ color: #777A7A;
+}
+
+
+/* burger-menu-icon
+----------------------------------------------------------------------------------------*/
+.burger-menu-icon {
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ display: inline-block;
+ vertical-align: middle;
+ padding: 0;
+ position: absolute;
+ right: 26px;
+ top: 26px;
+ display: none;
+}
+.burger-menu-icon .line {
+ fill: none;
+ stroke: #000;
+ stroke-width: 6;
+ transition: stroke-dasharray 600ms cubic-bezier(0.4, 0, 0.2, 1),
+ stroke-dashoffset 600ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+.burger-menu-icon .line1 {
+ stroke-dasharray: 60 207;
+ stroke-width: 6;
+}
+.burger-menu-icon .line2 {
+ stroke-dasharray: 60 60;
+ stroke-width: 6;
+}
+.burger-menu-icon .line3 {
+ stroke-dasharray: 60 207;
+ stroke-width: 6;
+}
+html.menu-opened .burger-menu-icon .line1 {
+ stroke-dasharray: 90 207;
+ stroke-dashoffset: -134;
+ stroke-width: 6;
+}
+html.menu-opened .burger-menu-icon .line2 {
+ stroke-dasharray: 1 60;
+ stroke-dashoffset: -30;
+ stroke-width: 6;
+}
+html.menu-opened .burger-menu-icon .line3 {
+ stroke-dasharray: 90 207;
+ stroke-dashoffset: -134;
+ stroke-width: 6;
+}
+
+
+/* ONE CONTENT COLUMN VERSION
+----------------------------------------------------------------------------------------*/
+
+body.one-content-column-version .content h1,
+body.one-content-column-version .content h2,
+body.one-content-column-version .content h3,
+body.one-content-column-version .content h4,
+body.one-content-column-version .content h5,
+body.one-content-column-version .content h6,
+body.one-content-column-version .content p,
+body.one-content-column-version .content table,
+body.one-content-column-version .content ul,
+body.one-content-column-version .content ol,
+body.one-content-column-version .content aside,
+body.one-content-column-version .content dl,
+body.one-content-column-version .content ul,
+body.one-content-column-version .content ol {
+ margin-right: 0;
+ max-width: 100%;
+}
+body.one-content-column-version .content-page .content p,
+body.one-content-column-version .content-page .content pre {
+ max-width: 100%;
+}
+body.one-content-column-version .content-page {
+ background-color: #323f4c;
+}
+body.one-content-column-version .content h1:first-child,
+body.one-content-column-version .content h2,
+body.one-content-column-version .content h3,
+body.one-content-column-version .content h4,
+body.one-content-column-version .content h5,
+body.one-content-column-version .content h6 {
+ color: #59C3C3;
+}
+body.one-content-column-version p {
+ color: #D6F0F0;
+}
+body.one-content-column-version .content table td {
+ color: #D6F0F0;
+}
+body.one-content-column-version .content thead {
+ color: #417179;
+}
+
+/* RESPONSIVE
+----------------------------------------------------------------------------------------*/
+
+@media only screen and (max-width:980px){
+ .content h1, .content h2, .content h3, .content h4, .content h5, .content h6, .content p, .content table, .content ul, .content ol, .content aside, .content dl, .content ul, .content ol {
+ margin-right: 0;
+ }
+ .content .central-overflow-x {
+ margin: 0;
+ padding: 0 28px;
+ }
+ .content-code{
+ display: none;
+ }
+ .content pre, .content blockquote {
+ margin: 20px 0;
+ padding: 28px;
+ display: block;
+ width: auto;
+ float: none;
+ }
+ .content pre code {
+ margin-top: 0;
+ }
+}
+
+@media only screen and (max-width:680px){
+ html {
+ scroll-padding-top: 83px;
+ }
+ html.menu-opened {
+ overflow: hidden;
+ }
+ .left-menu {
+ position: relative;
+ width: auto;
+ }
+ .left-menu .content-menu {
+ position: fixed;
+ width: 400px;
+ max-width: 90vw;
+ z-index: 3;
+ top: 0;
+ bottom: 0;
+ right: -405px;
+ left: auto;
+ background-color: #fff;
+ margin: 0;
+ overflow-x: hidden;
+ padding-top: 83px;
+ padding-bottom: 20px;
+ }
+ .left-menu .content-menu ul {
+ position: relative;
+ }
+ .left-menu .mobile-menu-closer {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 2;
+ background-color: rgba(50, 63, 76, .5);
+ opacity: 0;
+ visibility: hidden;
+ }
+ html.menu-opened .left-menu .mobile-menu-closer {
+ opacity: 1;
+ visibility: visible;
+ }
+ html.menu-opened .left-menu .content-menu {
+ right: 0;
+ }
+ .left-menu .content-logo {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 4;
+ background-color: #f4f5f8;
+ }
+ .content-logo .logo {
+ margin-right: 65px;
+ }
+ .content-page{
+ margin-left: 0;
+ padding-top: 83px;
+ }
+ .burger-menu-icon {
+ display: block;
+ }
+}
+
+/* BROWSER AND NON-SEMANTIC STYLING
+----------------------------------------------------------------------------------------*/
+
+.cf:before, .cf:after { content: ""; display: block; }
+.cf:after { clear: both; }
+.ie6 .cf { zoom: 1 }
\ No newline at end of file
diff --git a/consumerWebsite/public/css/style.css b/consumerWebsite/public/css/style.css
index d707ecb..ecab1bf 100644
--- a/consumerWebsite/public/css/style.css
+++ b/consumerWebsite/public/css/style.css
@@ -73,6 +73,21 @@ button.btn-secondary:hover{
color: #ffffff;
border-radius: 4.8px;
}
+.btn-outline-danger{
+ width: 78px;
+ height: 40px;
+ padding-top: 8px;
+ font-size: 16px;
+
+}
+.btn-outline-info{
+ width: 78px ;
+ height: 40px ;
+ padding-top: 8px;
+ font-size: 16px;
+
+}
+
.navbar-expand-lg.top-nav .navbar-nav .dropdown-menu{
margin: 0px;
box-shadow: 3px 5px 15px rgba(0,0,0, .15);
diff --git a/consumerWebsite/public/images/apilogo.png b/consumerWebsite/public/images/apilogo.png
new file mode 100644
index 0000000..37634fb
Binary files /dev/null and b/consumerWebsite/public/images/apilogo.png differ
diff --git a/consumerWebsite/public/js/api.js b/consumerWebsite/public/js/api.js
new file mode 100644
index 0000000..594b411
--- /dev/null
+++ b/consumerWebsite/public/js/api.js
@@ -0,0 +1,88 @@
+
+var elements = [];
+
+[].forEach.call(document.querySelectorAll('.scroll-to-link'), function (div) {
+ div.onclick = function (e) {
+ e.preventDefault();
+ var target = this.dataset.target;
+ document.getElementById(target).scrollIntoView({ behavior: 'smooth' });
+ var elems = document.querySelectorAll(".content-menu ul li");
+ [].forEach.call(elems, function (el) {
+ el.classList.remove("active");
+ });
+ this.classList.add("active");
+ return false;
+ };
+});
+
+document.getElementById('button-menu-mobile').onclick = function (e) {
+ e.preventDefault();
+ document.querySelector('html').classList.toggle('menu-opened');
+}
+document.querySelector('.left-menu .mobile-menu-closer').onclick = function (e) {
+ e.preventDefault();
+ document.querySelector('html').classList.remove('menu-opened');
+}
+
+function debounce (func) {
+ var timer;
+ return function (event) {
+ if (timer) clearTimeout(timer);
+ timer = setTimeout(func, 100, event);
+ };
+}
+
+function calculElements () {
+ var totalHeight = 0;
+ elements = [];
+ [].forEach.call(document.querySelectorAll('.content-section'), function (div) {
+ var section = {};
+ section.id = div.id;
+ totalHeight += div.offsetHeight;
+ section.maxHeight = totalHeight - 25;
+ elements.push(section);
+ });
+ onScroll();
+}
+
+function onScroll () {
+ var scroll = window.pageYOffset;
+ console.log('scroll', scroll, elements)
+ for (var i = 0; i < elements.length; i++) {
+ var section = elements[i];
+ if (scroll <= section.maxHeight) {
+ var elems = document.querySelectorAll(".content-menu ul li");
+ [].forEach.call(elems, function (el) {
+ el.classList.remove("active");
+ });
+ var activeElems = document.querySelectorAll(".content-menu ul li[data-target='" + section.id + "']");
+ [].forEach.call(activeElems, function (el) {
+ el.classList.add("active");
+ });
+ break;
+ }
+ }
+ if (window.innerHeight + scroll + 5 >= document.body.scrollHeight) { // end of scroll, last element
+ var elems = document.querySelectorAll(".content-menu ul li");
+ [].forEach.call(elems, function (el) {
+ el.classList.remove("active");
+ });
+ var activeElems = document.querySelectorAll(".content-menu ul li:last-child");
+ [].forEach.call(activeElems, function (el) {
+ el.classList.add("active");
+ });
+ }
+}
+
+calculElements();
+window.onload = () => {
+ calculElements();
+};
+window.addEventListener("resize", debounce(function (e) {
+ e.preventDefault();
+ calculElements();
+}));
+window.addEventListener('scroll', function (e) {
+ e.preventDefault();
+ onScroll();
+});
\ No newline at end of file
diff --git a/consumerWebsite/public/js/app.js b/consumerWebsite/public/js/app.js
index d2687d6..1e3d235 100644
--- a/consumerWebsite/public/js/app.js
+++ b/consumerWebsite/public/js/app.js
@@ -134,7 +134,6 @@ app.api = (function (app) {
complete: function (res, text) {
callback(
text !== "success" ? res.statusText : null,
- //console.log(res.responseText),
JSON.parse(res.responseText),
res.status
);
@@ -151,8 +150,9 @@ app.auth = (function (app) {
localStorage.setItem("APIToken", token);
}
- function setUserId(userId) {
- localStorage.setItem("userId", userId);
+ function setUserId(userid) {
+ console.log("userid", userid);
+ localStorage.setItem("userid", userid);
}
function setUsername(username) {
@@ -185,25 +185,39 @@ app.auth = (function (app) {
*/
function logOut(callback) {
- localStorage.removeItem("APIToken");
- localStorage.removeItem("userId");
- localStorage.removeItem("username");
+ //call logout route
+ $.ajax({
+ type: "DELETE",
+ url: "/api/v0/user/logout",
+ headers: {
+ "auth-token": app.auth.getToken(),
+ },
+ contentType: "application/json; charset=utf-8",
+ dataType: "json",
+ complete: function (res, text) {
+ callback(
+ text !== "success" ? res.statusText : null,
+ JSON.parse(res.responseText),
+ res.status
+ );
+ },
+ });
- //remove token from db NOT the api key.
+ localStorage.removeItem("APIToken");
+ localStorage.removeItem("userid");
+ localStorage.removeItem("username");
callback();
}
function forceLogin() {
- $.holdReady(true);
- app.auth.isLoggedIn(function (error, isLoggedIn) {
- if (error || !isLoggedIn) {
- app.auth.logOut(function () {});
- location.replace(`/login`);
- } else {
- $.holdReady(false);
- }
- });
- }
+ app.auth.isLoggedIn(function (error, isLoggedIn) {
+ if (error || !isLoggedIn) {
+ app.auth.logOut(function () {
+ location.replace(`/login`);
+ });
+ }
+ });
+ }
function logInRedirect() {
window.location.href =
@@ -215,6 +229,18 @@ app.auth = (function (app) {
window.location.href = location.href.replace(location.replace(`/`)) || "/";
}
+ //if isLoggedin is true, redirect user away from login / register page
+ function redirectIfLoggedIn() {
+ $.holdReady(true);
+ app.auth.isLoggedIn(function (error, isLoggedIn) {
+ if (error || isLoggedIn) {
+ location.replace(`/`);
+ } else {
+ $.holdReady(false);
+ }
+ });
+ }
+
return {
getToken: getToken,
setToken: setToken,
@@ -226,6 +252,7 @@ app.auth = (function (app) {
forceLogin,
logInRedirect,
homeRedirect,
+ redirectIfLoggedIn,
};
})(app);
diff --git a/consumerWebsite/public/profile.html b/consumerWebsite/public/profile.html
deleted file mode 100644
index 2d4e66f..0000000
--- a/consumerWebsite/public/profile.html
+++ /dev/null
@@ -1,193 +0,0 @@
-
-
-
-
-
-
-
-
- N & LW Lawn Care - Landscaping Bootstrap4 HTML5 Responsive Template
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Profile Settings
-
-
-
-
- Change
- Password
-
-
Save
- Profile
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/consumerWebsite/routes/api_routes.js b/consumerWebsite/routes/api_routes.js
index 4d110fe..4edf988 100644
--- a/consumerWebsite/routes/api_routes.js
+++ b/consumerWebsite/routes/api_routes.js
@@ -1,10 +1,14 @@
'use strict';
const router = require('express').Router();
+const { auth } = require("../middleware/authChecker")
-router.use('/user', require('./user'));
+
+router.use('/auth', require('./auth'));
router.use('/apikey', require('./apikey'));
+router.use('/user', auth ,require('./user'));
+
module.exports = router;
diff --git a/consumerWebsite/routes/auth.js b/consumerWebsite/routes/auth.js
new file mode 100644
index 0000000..e5e41e2
--- /dev/null
+++ b/consumerWebsite/routes/auth.js
@@ -0,0 +1,56 @@
+const { addUser, loginUser } = require("../functions/apiDatabase.js");
+
+const express = require("express");
+const router = express.Router();
+
+// /user/register
+router.post("/register", async (req, res, next) => {
+ try {
+ console.log(req.body);
+ let Res = await addUser(req.body);
+ if (Res == false) {
+ let error = new Error("UserRegFailed");
+ error.message = "The user failed to be craated";
+ error.status = 400;
+ return next(error);
+ }
+ else{
+ return res.json({
+ message: "User created successfully",
+ });
+ }
+ } catch (error) {
+ console.error(error);
+ next(error);
+ }
+});
+
+//login
+router.post("/login", async (req, res, next) => {
+ try {
+ let Res = await loginUser(req.body);
+ if (Res == false) {
+ let error = new Error("User Login Failed");
+ error.status = 400;
+ return next(error);
+ }
+ else{
+ //pass res back to form to be set in local storage
+ console.log("my res" , Res);
+ return res.json({
+ message: "User login successfully",
+ token: Res.token,
+ userid: Res.userid,
+ username: Res.username,
+ });
+
+ }
+ } catch (error) {
+ console.error(error);
+ next(error);
+ }
+});
+
+
+module.exports = router;
+
diff --git a/consumerWebsite/routes/render.js b/consumerWebsite/routes/render.js
index 4a48f10..9bd1783 100644
--- a/consumerWebsite/routes/render.js
+++ b/consumerWebsite/routes/render.js
@@ -77,4 +77,14 @@ router.get('/contact', function(req, res, next) {
res.render('contact');
});
+//api doc
+router.get('/api', function(req, res, next) {
+ res.render('api');
+});
+
+//profile page
+router.get('/profile', function(req, res, next) {
+ res.render('profile');
+});
+
module.exports = router;
diff --git a/consumerWebsite/routes/user.js b/consumerWebsite/routes/user.js
index f9025ff..5df5304 100644
--- a/consumerWebsite/routes/user.js
+++ b/consumerWebsite/routes/user.js
@@ -1,100 +1,58 @@
-const { addUser, loginUser } = require("../functions/apiDatabase.js");
+const { getUserID , deleteUserToken } = require("../functions/apiDatabase.js");
const express = require("express");
const router = express.Router();
-// /user/register
-router.post("/register", async (req, res, next) => {
- try {
- let Res = await addUser(req.body);
- if (Res == false) {
- let error = new Error("UserRegFailed");
- error.message = "The user failed to be craated";
- error.status = 400;
- return next(error);
- }
- else{
- return res.json({
- message: "User created successfully",
- });
+//api/v0/user/me
+//getbyid
+router.get("/me", async function (req, res, next) {
+ try {
+
+ //console.log(req.user);
+ let user = await getUserID(req.user);
+ if (!user) {
+ let error = new Error("User not found");
+ error.status = 400;
+ return next(error);
+ }
+ res.json({
+ user: user,
+ });
+
+ } catch (error) {
+ next(error);
}
- } catch (error) {
- console.error(error);
- next(error);
- }
});
-//login
-router.post("/login", async (req, res, next) => {
- try {
- let Res = await loginUser(req.body);
- if (Res == false) {
- let error = new Error("User Login Failed");
- error.status = 400;
- return next(error);
- }
- else{
- //pass res back to form to be set in local storage
- console.log(Res);
- return res.json({
- message: "User login successfully",
- token: Res.token,
- userId: Res.userid,
- username: Res.username,
- });
+//logout
+router.delete('/logout', async function(req, res, next){
+ try{
+ /*
+ let authToken = req.header('auth-token');
+ let userDel = await deleteUserToken(authToken);
+ if (!userDel) {
+ let error = new Error("User not found");
+ error.status = 400;
+ return next(error);
+ }
+ */
+ //destroy method call on seq object
+ req.token.destroy();
+ // DO NOT CALL THIS!!! IT WILL DESTROY USERMODEL SEQ OBJ
+ //req.user.destroy();
+ res.json({
+ message: "User logged out successfully",
+ });
+ }catch(error){
+ next(error);
}
- } catch (error) {
- console.error(error);
- next(error);
- }
-});
+ });
+
//update
//delete
-//getbyid
-
-module.exports = router;
-
-/*
-
-curl localhost/api/v0/user/register -H "Content-Type: application/json" -X POST -d '{"username":
-"testuser123", "password": "thisisthesystemuserpasswordnoob", "email": "testuser123@ecosaver.com", "address":
-"Nanyang Polytechnic 180 Ang Mo Kio Avenue 8 Singapore 569830", "phone": "12345678"}'
-'use strict';
-
-const router = require('express').Router();
-const {User} = require('../models/user');
-
-router.get('/', async function(req, res, next){
- try{
- return res.json({
- results: await User[req.query.detail ? "listDetail" : "list"]()
- });
- }catch(error){
- next(error);
- }
-});
-
-router.get('/me', async function(req, res, next){
- try{
-
- return res.json(await User.get({uid: req.user.uid}));
- }catch(error){
- next(error);
- }
-});
-
-router.get('/:uid', async function(req, res, next){
- try{
- return res.json({
- results: await User.get(req.params.uid),
- });
- }catch(error){
- next(error);
- }
-});
module.exports = router;
-*/
+
diff --git a/consumerWebsite/views/api.ejs b/consumerWebsite/views/api.ejs
new file mode 100644
index 0000000..db3d1b7
--- /dev/null
+++ b/consumerWebsite/views/api.ejs
@@ -0,0 +1,160 @@
+
+
+ <%- include('top') %>
+
+
+
+
+
+
+
+
+
Get started
+
+ The Westeros API provides programmatic access to read Game of Thrones data. Retrieve a character, provide an oauth connexion, retrieve a familly, filter them, etc.
+
+
+ To use this API, you need an API key . Please contact us at jon.snow@nightswatch.wes to get your own API key.
+
+
+
+
get characters
+
+ To get characters you need to make a POST call to the following url :
+ http://api.westeros.com/character/get
+
+
+
QUERY PARAMETERS
+
+
+
+ Field
+ Type
+ Description
+
+
+
+
+ secret_key
+ String
+ Your API key.
+
+
+ search
+ String
+ (optional) A search word to find character by name.
+
+
+ house
+ String
+
+ (optional) a string array of houses:
+
+
+
+ alive
+ Boolean
+
+ (optional) a boolean to filter alived characters
+
+
+
+ gender
+ String
+
+ (optional) a string to filter character by gender: m: male f: female
+
+
+
+ offset
+ Integer
+ (optional - default: 0) A cursor for use in pagination. Pagination starts offset the specified offset.
+
+
+ limit
+ Integer
+ (optional - default: 10) A limit on the number of objects to be returned, between 1 and 100.
+
+
+
+
+
+
Errors
+
+ The Westeros API uses the following error codes:
+
+
+
+
+ Error Code
+ Meaning
+
+
+
+
+ X000
+
+ Some parameters are missing. This error appears when you don't pass every mandatory parameters.
+
+
+
+ X001
+
+ Unknown or unvalid secret_key
. This error appears if you use an unknow API key or if your API key expired.
+
+
+
+ X002
+
+ Unvalid secret_key
for this domain. This error appears if you use an API key non specified for your domain. Developper or Universal API keys doesn't have domain checker.
+
+
+
+ X003
+
+ Unknown or unvalid user token
. This error appears if you use an unknow user token
or if the user token
expired.
+
+
+
+
+
+
+
+
+
+
+