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 -

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

Profile Settings

-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
- - - - - - - - \ 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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
secret_keyStringYour API key.
searchString(optional) A search word to find character by name.
houseString + (optional) a string array of houses: +
aliveBoolean + (optional) a boolean to filter alived characters +
genderString + (optional) a string to filter character by gender:
m: male
f: female +
offsetInteger(optional - default: 0) A cursor for use in pagination. Pagination starts offset the specified offset.
limitInteger(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 CodeMeaning
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. +
+
+
+
+ + + + + diff --git a/consumerWebsite/views/bot.ejs b/consumerWebsite/views/bot.ejs index 22424d4..9d211b0 100644 --- a/consumerWebsite/views/bot.ejs +++ b/consumerWebsite/views/bot.ejs @@ -88,4 +88,5 @@ + diff --git a/consumerWebsite/views/index.ejs b/consumerWebsite/views/index.ejs index 978a726..84a2e60 100644 --- a/consumerWebsite/views/index.ejs +++ b/consumerWebsite/views/index.ejs @@ -1,6 +1,7 @@ <%- include('top') %> +