diff --git a/IoT-sensor/Database/mySQL.js b/IoT-sensor/Database/mySQL.js index 255dc3a..0f86d40 100644 --- a/IoT-sensor/Database/mySQL.js +++ b/IoT-sensor/Database/mySQL.js @@ -9,13 +9,13 @@ const sequelize = new Sequelize( process.env.DB_USER, process.env.DB_PASS, { - host: "mpsqldatabase.mysql.database.azure.com", - dialect: 'mysql', + host: "mpsqldatabasean.mysql.database.azure.com", + dialect: 'mysql', // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy'; attributeBehavior: 'escape', dialectOptions: { ssl: { - ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt.pem')), + ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt_3.pem')), }, }, diff --git a/README.md b/README.md index 3eaadc6..8e78093 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,15 @@ i repeat DO NOT USE CREDS IN CODE! Please use .env files (https://www.npmjs.com/ ## Workload 1) Ti Seng -* Webserver Microservices * IoT sensor * Most Database / Backend Functions of this repo * consumer website api and user function + 2) Sean * Admin Website Microservice Micro Service -1) api.blah handle api -2) admin.blah admin website -3) consumer.blah comsumer -4) proxy.blah reverproxy -5) mqtt.blah mqtt service +1) admin.blah admin website +2) consumer.blah comsumer +3) proxy.blah reverproxy +4) mqtt.blah mqtt service diff --git a/Sean/modules/validationMiddleware.js b/Sean/modules/validationMiddleware.js index 3551e12..81b8606 100644 --- a/Sean/modules/validationMiddleware.js +++ b/Sean/modules/validationMiddleware.js @@ -1,4 +1,4 @@ -const { body } = require('express-validator'); +const { validationResult, body } = require('express-validator'); const locationValidation = [ body('name').trim().isLength({ min: 1 }).withMessage('Name must not be empty').escape(), @@ -69,7 +69,34 @@ const createValidation = [ body('jobTitle').trim().isLength({ min: 1 }).withMessage('Job title must not be empty').escape(), ]; - + function isStrongPassword(password) { + // Password must be at least 10 characters long + if (password.length < 10) { + return false; + } + + // Password must contain at least one uppercase letter + if (!/[A-Z]/.test(password)) { + return false; + } + + // Password must contain at least one lowercase letter + if (!/[a-z]/.test(password)) { + return false; + } + + // Password must contain at least one digit + if (!/\d/.test(password)) { + return false; + } + + // Password must contain at least one symbol + if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) { + return false; + } + + return true; +} module.exports = { locationValidation,locationValidationUpdate,locationdeleteValidation ,sensorValidation,sensorupdateValidation,sensordeleteValidation,loginValidation,otpValidation diff --git a/Sean/server.js b/Sean/server.js index 9dbd795..f8a6047 100644 --- a/Sean/server.js +++ b/Sean/server.js @@ -7,10 +7,10 @@ const crypto = require("crypto"); const validator = require('validator'); const axios = require('axios'); -const {validationResult } = require('express-validator'); -const {locationValidation, locationValidationUpdate, locationdeleteValidation +const { validationResult } = require('express-validator'); +const { locationValidation, locationValidationUpdate, locationdeleteValidation ,sensorValidation, sensorupdateValidation, sensordeleteValidation, loginValidation -,otpValidation, createValidation} = require('./modules/validationMiddleware'); +,otpValidation, createValidation } = require('./modules/validationMiddleware'); const rateLimit = require('./modules/rateLimitMiddleware'); const { generateOTP, sendOTPByEmail } = require('./modules/otpUtils'); const { format } = require('date-fns'); @@ -255,15 +255,15 @@ function isStrongPassword(password) { return true; } -app.post( - '/createUser', createValidation, async (req, res) => { +app.post + ('/createUser', createValidation, + async (req, res) => { try { - const errors = validationResult(req); + const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } - const sessionTokencookie = req.cookies['sessionToken']; // Verify sessionToken with the one stored in the database @@ -286,10 +286,6 @@ app.post( // Extract the username of the user creating a new user const creatorUsername = req.session.username; // Adjust this based on how you store the creator's username in your session - // Additional password complexity check - if (!isStrongPassword(password)) { - return res.status(400).json({ error: "Password does not meet complexity requirements" }); - } // Check if the username is already taken const existingUser = await User.findOne({ where: { username } }); diff --git a/Sean/views/index.ejs b/Sean/views/index.ejs index 431fd0f..e3d14b4 100644 --- a/Sean/views/index.ejs +++ b/Sean/views/index.ejs @@ -57,7 +57,7 @@ Contacts - + diff --git a/api.MD b/api.MD index c435b69..f6f8514 100644 --- a/api.MD +++ b/api.MD @@ -172,4 +172,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:3000/api/v0/token/new -H "Content-Type: application/json" -X POST -d '{"userid": "5", "permission": "canRead"}' +curl localhost/api/v0/token/new -H "Content-Type: application/json" -X POST -d '{"userid": "5", "permission": "canRead"}' diff --git a/consumerWebsite/app.js b/consumerWebsite/app.js index f8dc2a8..b214bc3 100644 --- a/consumerWebsite/app.js +++ b/consumerWebsite/app.js @@ -50,49 +50,48 @@ 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. app.use(function (req, res, next) { - //application/json; charset=utf-8 - if (req.is("application/json" || "application/json; charset=utf-8")) { - var err = new Error("Not Found"); - err.message = "Page not found"; - err.status = 404; - next(err); - } else { - res.status(404).render("404"); - } + //application/json; charset=utf-8 + var err = new Error("Not Found"); + err.message = "Page not found"; + err.status = 404; + next(err); }); // Error handler. This is where `next()` will go on error app.use(function (err, req, res, next) { - console.error(err.status || res.status, err.name, req.method, req.url); - if (![404].includes(err.status || res.status)) { - console.error(err.message); - console.error(err.stack); - console.error("========================================="); - } + console.error(err.status || res.status, err.name, req.method, req.url); - console.log(err.name + " validation error"); - // Parse key error for Sequilzw - let keyErrors = {}; - if (["SequelizeValidationError"].includes(err.name) && err.errors) { - for (let item of err.errors) { - if (item.path) { - keyErrors[item.path] = item.message; - } - } - res.status = 422; - } + // Parse key error for Sequilzw + let keyErrors = {}; + if (["SequelizeValidationError"].includes(err.name) && err.errors) { + for (let item of err.errors) { + if (item.path) { + keyErrors[item.path] = item.message; + } + } + res.status = 422; + } - if (![404, 422].includes(err.status || res.status)) { - console.error(err.message); - console.error(err.stack); - console.error("========================================="); - } + if (![404, 401, 422].includes(err.status || res.status)) { + console.error(err.message); + console.error(err.stack); + console.error("========================================="); + } + res.status(err.status || 500); + // res.status(err.status || 500); - - res.status(err.status || 500); - res.json({ - name: err.name || "Unknown error", - message: err.message, - keyErrors, - }); -}); + if (req.get('Content-Type') && req.get('Content-Type').includes("json")) { + res.json({ + name: err.name || "Unknown error", + message: err.message, + keyErrors, + }); + } + else { + res.json({ + name: err.name || "Unknown error", + message: err.message, + keyErrors, + }); + } +}); \ No newline at end of file diff --git a/consumerWebsite/database/model/sensorModel.js b/consumerWebsite/database/model/sensorModel.js index 769c0dc..d1eb2c3 100644 --- a/consumerWebsite/database/model/sensorModel.js +++ b/consumerWebsite/database/model/sensorModel.js @@ -107,4 +107,6 @@ const sensorModel = sequelize.define( } ); +sensorModel.belongsTo(locationModel); + module.exports = { sensorModel }; diff --git a/consumerWebsite/database/model/tokenModel.js b/consumerWebsite/database/model/tokenModel.js index 26ec8af..17e6e4e 100644 --- a/consumerWebsite/database/model/tokenModel.js +++ b/consumerWebsite/database/model/tokenModel.js @@ -3,7 +3,7 @@ const { Sequelize, DataTypes } = require("sequelize"); const { sequelize } = require("../mySQL"); const { userModel } = require("./userModel"); -//sequelize.sync(); +sequelize.sync(); const tokenModel = sequelize.define( "token", { diff --git a/consumerWebsite/database/mySQL.js b/consumerWebsite/database/mySQL.js index 255dc3a..6bba43b 100644 --- a/consumerWebsite/database/mySQL.js +++ b/consumerWebsite/database/mySQL.js @@ -9,13 +9,13 @@ const sequelize = new Sequelize( process.env.DB_USER, process.env.DB_PASS, { - host: "mpsqldatabase.mysql.database.azure.com", + host: "mpsqldatabasean.mysql.database.azure.com", dialect: 'mysql', // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy'; attributeBehavior: 'escape', dialectOptions: { ssl: { - ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt.pem')), + ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt_3.pem')), }, }, diff --git a/consumerWebsite/functions/api.js b/consumerWebsite/functions/api.js index fcba1f3..5422d8b 100644 --- a/consumerWebsite/functions/api.js +++ b/consumerWebsite/functions/api.js @@ -1,19 +1,41 @@ -const { hash, compareHash } = require("./bcrypt.js"); const { tokenModel } = require("../database/model/tokenModel.js"); +const { userModel } = require("../database/model/userModel"); +const { hash, compareHash } = require("./bcrypt.js"); const { generateUUID } = require("./generateUUID.js"); +const { isValid } = require("./isValid"); -/* -1) take userid -2) generate random api key -3) hash the api key -4) append userid with - and api key -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 addToken(userId, permission , expiry) { +async function getTokenByToken(token) { + const splitAuthToken = token.split("-"); + const rowid = splitAuthToken[0]; + const suppliedToken = splitAuthToken.slice(1).join("-"); + + token = await tokenModel.findByPk(rowid, { include: userModel }); + + token.isValid = await compareHash(suppliedToken, token.token); //true + console.log("function api getTokenByToken token", token.isValid); + token.isValid = token.isValid && isValid(token.expiration); + console.log("function api getTokenByToken token", token.isValid); + if (!token.isValid) { + //add boolean to token table + token.destroy(); + } + /* + console.log( + "function api getTokenByToken token", + await compareHash(suppliedToken, token.token), + isValid("token" , token.expiration) + ); + */ + console.log(token.isValid); + return token; +} + +async function addToken(userId, permission, expiry) { let uuid = await generateUUID(); let hashtoken = await hash(uuid); + //console.log("user id", userId); + // return { token: token, userid: userRes.id, username: userRes.username }; + // let token = await addToken(userRes.id , "canRead" , tokenToLive); let token = await tokenModel.create({ userid: userId, @@ -26,23 +48,4 @@ async function addToken(userId, permission , expiry) { return token.id + "-" + uuid; } -async function checkToken(Supplied, rowid) { - try { - const retrivedToken = await tokenModel.findOne({ - raw: true, - attributes: ["token", "permission"], - where: { - id: rowid, - }, - }); - //console.log(retrivedKey.apikey); - if (compareHash(Supplied, retrivedToken.token)) { - //return true; - return retrivedToken.permission; - } - } catch (error) { - console.error(error); - } -} - -module.exports = { addToken , checkToken }; \ No newline at end of file +module.exports = { addToken, getTokenByToken }; diff --git a/consumerWebsite/functions/isValid.js b/consumerWebsite/functions/isValid.js index 2d79e13..ae39ce1 100644 --- a/consumerWebsite/functions/isValid.js +++ b/consumerWebsite/functions/isValid.js @@ -1,20 +1,14 @@ -const moment = require("moment"); -const currentTime = moment().format("YYYY-MM-DD HH:mm:ss"); - //time is taken from the token -function isValid(time){ - const timeDiff = moment(currentTime).diff(time, "minutes"); - - if (timeDiff > 1) { - console.log(timeDiff); - return false; - } - - return true; +function isValid(time) { + if ( + Math.floor(new Date(time).getTime() / 1000) < + Math.floor(new Date().getTime() / 1000) + ) { + return false; + } + return true; + } - - - -module.exports = { isValid }; \ No newline at end of file +module.exports = { isValid }; diff --git a/consumerWebsite/functions/nodeMail.js b/consumerWebsite/functions/nodeMail.js new file mode 100644 index 0000000..f81a8c4 --- /dev/null +++ b/consumerWebsite/functions/nodeMail.js @@ -0,0 +1,82 @@ +const { transporter } = require("../modules/nodemailer"); +const path = require("path"); +require("dotenv").config({ path: path.resolve(__dirname, "../.env") }); + +/* +var message = { + from: "sender@server.com", + to: "receiver@sender.com", + subject: "Message title", + text: "Plaintext version of the message", + html: "

HTML version of the message

", +}; +//send mail with defined transport object +transporter.sendMail(data[, callback]) + +*/ + +async function sendContactEmail(email, name, message) { + console.log(email, name, message); + + try { + let contactMessage = await transporter.sendMail({ + to: process.env.euser, + subject: "Contact us Message", + html: ` +

Contact us Message

+

From: ${name}

+

User Email: ${email}

+

Message: ${message}

+

Thank you for contacting us. We will get back to you as soon as possible.

+

Regards,

+

EcoSaver Team

+

EcoSaver Website

+

Please do not reply to this email.

+ `, + }); + transporter.sendMail({ contactMessage }, function (error, info) { + if (error) { + console.log(error); + } else { + console.log("Email sent: " + info.response); + } + }); + } catch (error) { + console.error(error); + } +} + +async function sendTokenEmail(email, token) { + + try { + let tokenMessage = await transporter.sendMail({ + to: email, + from: process.env.euser, + subject: "API Token", + html: ` +

API Token

+

Token: ${token}

+

Please do not lose this token and do not share your token with anyone!

+

Thank you for using EcoSaver.

+

Regards,

+

EcoSaver Team

+

EcoSaver Website

+

Please do not reply to this email.

+ + `, + }); + transporter.sendMail({ tokenMessage }, function (error, info) { + if (error) { + console.log(error); + } else { + console.log("Email sent: " + info.response); + } + }); + } catch (error) { + console.error(error); + } +} + + + +module.exports = { sendContactEmail , sendTokenEmail }; diff --git a/consumerWebsite/functions/sensorData.js b/consumerWebsite/functions/sensorData.js index 28cf3a0..20a4176 100644 --- a/consumerWebsite/functions/sensorData.js +++ b/consumerWebsite/functions/sensorData.js @@ -18,15 +18,17 @@ async function getSensorData() { const sensorData = await sensorDataModel.findAll(); return sensorData; } - async function addSensorData(id_sensor, id_location, sensordata) { const sensorData = await sensorDataModel.create({ sensorid: id_sensor, - locationid: id_location, - measurement: sensordata, + locationid: id_location , + measurement: sensordata.measurement, }); - io().emit('sensorData:new', sensorData) + //console.log("sensorData", sensorData); + //console.log("sensorData", sensordata.measurement); + + io().emit('sensorData:new', sensordata) return sensorData; } @@ -61,6 +63,14 @@ async function getSensorDataById(id) { }); return sensorData; } + +async function getLatestData() { + const sensorData = await sensorDataModel.findAll({ + limit: 6, + order: [["createdAt", "DESC"]], + }); + return sensorData; +} var ormQuery = {}; var whereClause = {}; var whereDate = {}; @@ -724,5 +734,6 @@ module.exports = { getSensorDataById, getData, getDatabyRange, + getLatestData, }; \ No newline at end of file diff --git a/consumerWebsite/functions/user.js b/consumerWebsite/functions/user.js index a7d16d1..d79d86d 100644 --- a/consumerWebsite/functions/user.js +++ b/consumerWebsite/functions/user.js @@ -2,7 +2,6 @@ const { Op } = require('sequelize') const { hash, compareHash } = require("./bcrypt.js"); const { addToken } = require("./api"); const { userModel } = require("../database/model/userModel"); -moment = require('moment') @@ -21,6 +20,16 @@ async function getUserByID(userid) { return userRes; } +async function getUserByEmail(email) { + let userRes = await userModel.findOne({ + where: { + email: email, + }, + }); + if (!userRes) return false; + return userRes; +} + //api/v0/auth/register /* Registering new user 1) req.body is taken from html form or wtv @@ -71,9 +80,11 @@ async function loginUser(user) { if (!match) return false; //console.log('loginUser', userRes.id, userRes.username); - //generate token and permission and experiation time - const currentTime = moment().format('YYYY-MM-DD HH:mm:ss'); - let token = await addToken(userRes.id , "canRead" , currentTime); + //generate token and permission and experiation time + 30 mins + //let tokenToLive = moment().add(30, 'minutes').format(); + let currentDate = new Date(); + let tokenToLive = new Date(currentDate.getTime() + 30 * 60000); + let token = await addToken(userRes.id , "canRead" , tokenToLive); return { token: token, userid: userRes.id, username: userRes.username }; } @@ -130,9 +141,23 @@ async function updateProfile(user, body) { } } +async function checkEmail(email) { + let emailRes = await userModel.findOne({ + where: { + email: email, + }, + }); + if (!emailRes) return false; + return true; + +} + + module.exports = { getUserByID, + getUserByEmail, addUser, loginUser, updateProfile, + checkEmail }; \ No newline at end of file diff --git a/consumerWebsite/middleware/authChecker.js b/consumerWebsite/middleware/authChecker.js index 2eea06e..300cf4d 100644 --- a/consumerWebsite/middleware/authChecker.js +++ b/consumerWebsite/middleware/authChecker.js @@ -1,68 +1,48 @@ -const { tokenModel } = require("../database/model/tokenModel"); -const { userModel } = require("../database/model/userModel"); -const { compareHash } = require("../functions/bcrypt"); -const { checkToken } = require("../functions/api"); -const { isValid } = require("../functions/isValid"); +const { getTokenByToken } = require("../functions/api"); +const permissionError = new Error('PermissionError') +permissionError.name = "Inadequate Permission Error" +permissionError.status = 401 +permissionError.message = "Inadequate permission to complete this response" async function auth(req, res, next) { try { - const authToken = req.header("auth-token"); - if (!authToken) { - const error = new Error("No Token key was supplied. Invalid request"); - throw error; + const token = await getTokenByToken(req.header("auth-token")); + + if (!token || !token.isValid){ + throw permissionError; } - const splitAuthToken = authToken.split("-"); - const rowid = splitAuthToken[0]; - const suppliedToken = splitAuthToken.slice(1).join("-"); - - const token = await tokenModel.findByPk(rowid, { include: userModel }); - - if (!token) { - const error = new Error("Token key not found. Invalid request"); - throw error; - } - - const isMatch = await compareHash(suppliedToken, token.token); - - console.log(isMatch); - if (!isMatch) { - const error = new Error("Token key not found. Invalid request"); - throw error; - } //if token is a match req.token = token; req.user = await token.getUser(); - const permission = await checkToken(suppliedToken, rowid); const route = req.originalUrl.split("?")[0]; // Removing query parameters //if route is from user/ and permission is canRead allow it to do CRUD - if (route.includes("/user/") && permission === "canRead") { - next(); + if (route.includes("/user/") || route.includes("/token/") && token.permission === "canRead") { + console.log("user route"); + return next(); } - if ((req.method === "GET" && permission === "canRead") || (["GET", "POST", "PUT", "DELETE"].includes(req.method) && permission === "canWrite")) { - next(); - } - - if (!isValid(token.expiration)){ - req.token.destroy(); - throw new Error("Token expired"); + if ((req.method === "GET" && token.permission === "canRead")){ + console.log("wtf you shldnt be here"); + return next(); } + if (["GET", "POST", "PUT", "DELETE"].includes(req.method) && token.permission === "canWrite") { + console.log("wtf you shldnt be here"); + return next(); + } + /* + if ((req.method === "GET" && token.permission === "canRead") || + (["GET", "POST", "PUT", "DELETE"].includes(req.method) && token.permission === "canWrite")) { + return next(); + } + */ + throw permissionError } catch (error) { next(error); } } - module.exports = { auth }; -/* - else { - const error = new Error("Insufficient permission"); - error.status = 401; - throw error; - } - -*/ \ No newline at end of file diff --git a/consumerWebsite/modules/nodemailer.js b/consumerWebsite/modules/nodemailer.js index 64e745f..a6d3cc2 100644 --- a/consumerWebsite/modules/nodemailer.js +++ b/consumerWebsite/modules/nodemailer.js @@ -2,6 +2,7 @@ const nodemailer = require("nodemailer"); const dotenv = require("dotenv"); const path = require('path') require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) +//.env let transporter = nodemailer.createTransport({ service: 'gmail', diff --git a/consumerWebsite/public/css/all.css b/consumerWebsite/public/css/all.css index a213d9c..e9f791c 100644 --- a/consumerWebsite/public/css/all.css +++ b/consumerWebsite/public/css/all.css @@ -3812,6 +3812,7 @@ .card-text { color: #000000; + font-size: 16px; } /* edit profile */ diff --git a/consumerWebsite/public/css/api.css b/consumerWebsite/public/css/api.css index ba1fa73..e1d3812 100644 --- a/consumerWebsite/public/css/api.css +++ b/consumerWebsite/public/css/api.css @@ -528,23 +528,22 @@ body.one-content-column-version .content thead { .generate-key-button { - float: right; /* Align the button to the right */ - margin-right: 85%; - margin-top: -40px; /* Adjust the margin-top value based on your layout */ - /* Add any additional styling you want for the button */ -} - -#content-get-api .generate-key-button { + margin-top: -40px; + margin-left: 25px; background-color: #4caf50; /* Green background color */ - color: white; /* White text color */ + color: #ffffff; padding: 5px 11px; /* Padding for the button */ border: none; /* Remove button border */ border-radius: 5px; /* Add border-radius for rounded corners */ cursor: pointer; /* Add pointer cursor on hover */ font-size: 14px; /* Font size */ } +.api-form { + margin-top: 20px; + margin-left: 25px; +} -#content-get-api .generate-key-button:hover { +.generate-key-button:hover { background-color: #45a049; /* Darker green on hover */ } diff --git a/consumerWebsite/public/js/app.js b/consumerWebsite/public/js/app.js index 96ee7a4..90da0c3 100644 --- a/consumerWebsite/public/js/app.js +++ b/consumerWebsite/public/js/app.js @@ -186,9 +186,7 @@ app.auth = (function (app) { function isLoggedIn(callback) { if (getToken()) { - console.log("you shldnt appear at all"); return app.api.get("user/me", function (error, data) { - console.log(error, data); if (!error) app.auth.user = data; return callback(error, data); }); @@ -287,7 +285,7 @@ function formAJAX(btn, del) { //console.log('Data being sent to', $form.attr('action'), formData) app.api[method]($form.attr("action"), formData, function (error, data) { - //console.log('Data back from the server', error, data) + console.log('Data back from the server', error, data) app.util.actionMessage(data.message, $form, error ? "danger" : "success"); //re-populate table if (!error) { $form.trigger("reset"); diff --git a/consumerWebsite/routes/api_routes.js b/consumerWebsite/routes/api_routes.js index 7715165..d91b34c 100644 --- a/consumerWebsite/routes/api_routes.js +++ b/consumerWebsite/routes/api_routes.js @@ -18,5 +18,8 @@ router.use('/sensor', [auth, APIlogger], require('./sensor.js')); //sensor data route router.use('/sensor-data', [auth, APIlogger], require('./sensorData.js')); +//latest sensor data to display on dashboard +router.use('/latest-sensor-data', [APIlogger], require('./latestsensorData.js')); + module.exports = router; diff --git a/consumerWebsite/routes/auth.js b/consumerWebsite/routes/auth.js index dd16dc1..276a5a2 100644 --- a/consumerWebsite/routes/auth.js +++ b/consumerWebsite/routes/auth.js @@ -1,4 +1,5 @@ -const { addUser, loginUser } = require("../functions/user"); +const { addUser, loginUser, checkEmail } = require("../functions/user"); +const { sendContactEmail } = require("../functions/nodeMail"); const express = require("express"); const router = express.Router(); @@ -12,12 +13,11 @@ router.post("/register", async (req, res, next) => { error.message = "The user failed to be craated"; error.status = 400; return next(error); + } else { + return res.json({ + message: "User created successfully", + }); } - else{ - return res.json({ - message: "User created successfully", - }); - } } catch (error) { console.error(error); next(error); @@ -29,20 +29,18 @@ router.post("/login", async (req, res, next) => { try { let Res = await loginUser(req.body); if (Res == false) { - let error = new Error("User Login Failed"); + 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 + return res.json({ + message: "User login successfully", + token: Res.token, + userid: Res.userid, + username: Res.username, + }); } - else{ - //pass res back to form to be set in local storage - return res.json({ - message: "User login successfully", - token: Res.token, - userid: Res.userid, - username: Res.username, - }); - - } } catch (error) { console.error(error); next(error); @@ -52,8 +50,26 @@ router.post("/login", async (req, res, next) => { //contact //auth/contact router.post("/contact", async (req, res, next) => { - + try { + //console.log(req.body); + let Res = await checkEmail(req.body.email); + if (!Res) { + let error = new Error("Email not found"); + error.status = 400; + return next(error); + } + else{ + //console.log(Res); + sendContactEmail(req.body.email, req.body.name, req.body.message); + return res.json({ + message: "Email sent successfully", + }); + + } + } catch (error) { + console.error(error); + next(error); + } }); module.exports = router; - diff --git a/consumerWebsite/routes/latestsensorData.js b/consumerWebsite/routes/latestsensorData.js new file mode 100644 index 0000000..b8d007c --- /dev/null +++ b/consumerWebsite/routes/latestsensorData.js @@ -0,0 +1,21 @@ +const { + getLatestData, + +} = require("../functions/sensorData"); + +const express = require("express"); +const router = express.Router(); + +router.get("/data", async (req, res, next) => { + try { + console.log(req.query); + const data = await getLatestData(); + res.status(200).json(data); + + } catch (error) { + console.error(error); + next(error); + } +}); + +module.exports = router; diff --git a/consumerWebsite/routes/token.js b/consumerWebsite/routes/token.js index f6d4b46..18b8df0 100644 --- a/consumerWebsite/routes/token.js +++ b/consumerWebsite/routes/token.js @@ -1,4 +1,7 @@ const { addToken } = require("../functions/api"); +const { checkEmail , getUserByEmail } = require("../functions/user"); +const { sendTokenEmail } = require("../functions/nodeMail"); + const express = require("express"); @@ -15,8 +18,29 @@ const router = express.Router(); //'{"userid": "5", "permission": "canRead" ,}' router.post("/new", async (req, res, next) => { try { - const token = await addToken(req.body.userid, req.body.permission , "2204-01-24 07:34:36" ); - res.json({token: token}); + //console.log(req.body); + const Res = await checkEmail(req.body.email); + if (!Res) { + let error = new Error("Email not found"); + error.status = 400; + return next(error); + } + else + { + //console.log("email found"); + let userid = await getUserByEmail(req.body.email); + if (!userid) return false; + + const token = await addToken(userid.id, "canRead" , "2204-01-24 07:34:36" ); + if (!token) return false; + sendTokenEmail(req.body.email, token); + res.json({ + message: "Token generated successfully and sent to email", + }) + + } + //const token = await addToken(req.body.userid, "canRead" , "2204-01-24 07:34:36" ); + //res.json({token: token}); } catch (error) { console.error(error); next(error); diff --git a/consumerWebsite/routes/user.js b/consumerWebsite/routes/user.js index ca0b940..a7cf3b6 100644 --- a/consumerWebsite/routes/user.js +++ b/consumerWebsite/routes/user.js @@ -8,7 +8,7 @@ const router = express.Router(); router.get("/me", async function (req, res, next) { try { let user = await getUserByID(req.user); - console.log(user); + //console.log(user); res.json({ user: user, }); diff --git a/consumerWebsite/views/api.ejs b/consumerWebsite/views/api.ejs index be8f73c..de88786 100644 --- a/consumerWebsite/views/api.ejs +++ b/consumerWebsite/views/api.ejs @@ -5,8 +5,12 @@ https://github.com/ticlekiwi/API-Documentation-HTML-Template !--> - <%- include('top') %> - +<%- include('top') %> + +
@@ -50,353 +54,633 @@
-
-
-
-

Get started

-

- The following API is provided by the Eco saver developer team. It allows you to get Location and - Sensor and Sensor Data from the Eco saver database. -

-

- To use this API, you need an API key. -

-
-
-

Get all location

-

- To get Location of sensors you need to make a GET call to the following url :
- https://api.teeseng.uk/api/v0/location -
-
- Return Response :
- {"status":"200"} -

-
-

QUERY PARAMETERS

- - - - - - - - - - - - - - - - - -
FieldTypeDescription
AuthorizationJSONYour API key.(Required) Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: - {provide your - API key here}"
-
-
-

Get location by ID

-

- To get Location you need to make a GET call to the following url :
- https://api.teeseng.uk/api/v0/location/{id} -
-
- Return Response :
- {"status":"200"} -

-
-

QUERY PARAMETERS

- - - - - - - - - - - - - - - - - -
FieldTypeDescription
AuthorizationJSON(Required) Your API key.Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: {provide - your - API key here}"
-
-
-

Add Location (Only for system or admin API key)

-

- To add an Location you need to make a POST call to the following url :
- https://api.teeseng.uk/api/v0/location/new -
-
- Example :
- curl https://api.teeseng.uk/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "SAMPLE", "added_by": "system" , "description": "test"}' -
-
- Return Response :
- {"status":"200"} -

-
-

QUERY PARAMETERS

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeDescription
AuthorizationJSONYour API key.(Required) Example: curl https://api.teeseng.uk/api/v0/location/new -H - "Authorization: {provide your - API key here}"
Location nameJSONLocation name.(Required) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new - -H "Authorization: provide - your API key here" -d '{"name":"Location name"}'
Added by JSONSystem or Admin(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new - -H "Authorization: provide - your API key here" -d '{"added_by":"system"}'
DescriptionJSONDescription of Location(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new - -H "Authorization: provide - your API key here" -d '{"description":"test"}'
-
- -
-

Update Location (Only for system or admin API key)

-

- To update an Location you need to make a PUT call to the following url :
- https://api.teeseng.uk/api/v0/location/update -
-
- Example :
- curl https://api.teeseng.uk/api/v0/location/update -H "Content-Type: application/json" -X POST -d '{"id": "7" , "name": "SAMPLE", "added_by": "system" , "description": "test"}' -
-
- Return Response :
- {"status":"200","message":"Location 7 updated"} -

-
-

QUERY PARAMETERS

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeDescription
AuthorizationJSONYour API key.(Required) example: curl https://api.teeseng.uk/api/v0/location/update -H - "Authorization: {provide your - API key here}"
IDJSONLocation ID(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/update - -H "Authorization: provide - your API key here" -d '{"id": "7"}'
Location nameJSONLocation name.(Optional) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new - -H "Authorization: provide - your API key here" -d '{"name":"Location name"}'
Added by JSONSystem or Admin(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new - -H "Authorization: provide - your API key here" -d '{"added_by":"system"}'
DescriptionJSONDescription of Location(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new - -H "Authorization: provide - your API key here" -d '{"description":"test"}'
-
- -
-

Delete Location (Only for system or admin API key)

-

- To delete an Location you need to make a DELETE call to the following url :
- https://api.teeseng.uk/api/v0/location/delete -
-
- Example :
- curl https://api.teeseng.uk/api/v0/location/delete -H "Content-Type: application/json" -X POST -d '{"id": "7"}' -

-
-

QUERY PARAMETERS

- - - - - - - - - - - - - - - - - - - - - - - - -
FieldTypeDescription
AuthorizationJSONYour API key.(Required) example: curl https://api.teeseng.uk/api/v0/location/delete -H - "Authorization: {provide your - API key here}"
IDJSONLocation ID(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/delete - -H "Authorization: provide - your API key here" -d '{"id": "7"}'
-
- -
-

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. -
403 - Unknown or unvalid secret_key. This error appears if - you use an unknow API key or if your API key expired. -
500 - Unvalid secret_key No API key was supplied. Invalid - request. -
X003 - Unknown or unvalid user token. This error appears if - you use an unknow user token or if the user token expired. -
-
-
-
-

API Keys

-
- - -
+
+
+
+

Get started

- You can generate API Keys here: + The following API is provided by the Eco saver developer team. It allows you to get Location and + Sensor and Sensor Data from the Eco saver database.

+

+ To use this API, you need an API key. +

+
+
+

Get all location

+

+ To get Location of sensors you need to make a GET call to the following url :
+ https://api.teeseng.uk/api/v0/location +
+
+ Return Response :
+ {"status":"200"} +

+
+

QUERY PARAMETERS

- - - - + + + - - - - + + + + + + + +
NamePublic KeyPrivate KeyCreatedFieldTypeDescription
API KeyGR234-We34greR-234-fEG2024-01-22AuthorizationJSONYour API key.(Required) Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: + {provide your + API key here}"
+
+
+

Get location by ID

+

+ To get Location you need to make a GET call to the following url :
+ https://api.teeseng.uk/api/v0/location/{id} +
+
+ Return Response :
+ {"status":"200"} +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSON(Required) Your API key.Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: {provide + your + API key here}"
+
+
+

Add Location (Only for system or admin API key)

+

+ To add an Location you need to make a POST call to the following url :
+ https://api.teeseng.uk/api/v0/location/new +
+
+ Example :
+ curl https://api.teeseng.uk/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "SAMPLE", "added_by": "system" , "description": "test"}' +
+
+ Return Response :
+ {"status":"200"} +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSONYour API key.(Required) Example: curl https://api.teeseng.uk/api/v0/location/new -H + "Authorization: {provide your + API key here}"
Location nameJSONLocation name.(Required) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new + -H "Authorization: provide + your API key here" -d '{"name":"Location name"}'
Added by JSONSystem or Admin(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new + -H "Authorization: provide + your API key here" -d '{"added_by":"system"}'
DescriptionJSONDescription of Location(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new + -H "Authorization: provide + your API key here" -d '{"description":"test"}'
+
+ +
+

Update Location (Only for system or admin API key)

+

+ To update an Location you need to make a PUT call to the following url :
+ https://api.teeseng.uk/api/v0/location/update +
+
+ Example :
+ curl https://api.teeseng.uk/api/v0/location/update -H "Content-Type: application/json" -X POST -d '{"id": "7" , "name": "SAMPLE", "added_by": "system" , "description": "test"}' +
+
+ Return Response :
+ {"status":"200","message":"Location 7 updated"} +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSONYour API key.(Required) example: curl https://api.teeseng.uk/api/v0/location/update -H + "Authorization: {provide your + API key here}"
IDJSONLocation ID(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/update + -H "Authorization: provide + your API key here" -d '{"id": "7"}'
Location nameJSONLocation name.(Optional) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new + -H "Authorization: provide + your API key here" -d '{"name":"Location name"}'
Added by JSONSystem or Admin(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new + -H "Authorization: provide + your API key here" -d '{"added_by":"system"}'
DescriptionJSONDescription of Location(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new + -H "Authorization: provide + your API key here" -d '{"description":"test"}'
+
+ +
+

Delete Location (Only for system or admin API key)

+

+ To delete an Location you need to make a DELETE call to the following url :
+ https://api.teeseng.uk/api/v0/location/delete +
+
+ Example :
+ curl https://api.teeseng.uk/api/v0/location/delete -H "Content-Type: application/json" -X POST -d '{"id": "7"}' +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSONYour API key.(Required) example: curl https://api.teeseng.uk/api/v0/location/delete -H + "Authorization: {provide your + API key here}"
IDJSONLocation ID(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/delete + -H "Authorization: provide + your API key here" -d '{"id": "7"}'
+
+
+

Get all sensor

+

+ To get sensors you need to make a GET call to the following url :
+ https://api.teeseng.uk/api/v0/sensor +
+
+ Return Response :
+ {"status":"200"} +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSONYour API key.(Required) Example: curl https://api.teeseng.uk/api/v0/sensor -H "Authorization: + {provide your + API key here}"
+
+
+

Get sensor by ID

+

+ To get Sensor you need to make a GET call to the following url :
+ https://api.teeseng.uk/api/v0/sensor/{id} +
+
+ Return Response :
+ {"status":"200"} +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSON(Required) Your API key.Example: curl https://api.teeseng.uk/api/v0/sensor/{id} -H "Authorization: {provide + your + API key here}"
+
+
+

Add Sensor (Only for system or admin API key)

+

+ To add a Sensor you need to make a POST call to the following url :
+ https://api.teeseng.uk/api/v0/sensor/new +
+
+ Example :
+ curl https://api.teeseng.uk/api/v0/sensor/new -H "Content-Type: application/json" -X POST -d '{"sensorname": "test", "added_by": "system" , "mac_address": "99-6A-F8-7D-B4-94", "description": "test" , "location": "11"}' +
+
+ Return Response :
+ {"status":"200"} +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSONYour API key.(Required) Example: curl https://api.teeseng.uk/api/v0/sensor/new + -H "Authorization: {provide your + API key here}"
Sensor nameJSONSesnsor name.(Required) Location name. Example: curl https://api.teeseng.uk/api/v0/sensor/new + -H "Authorization: provide + your API key here" -d '{"sensorname": "test"}'
Added by JSONSystem or Admin(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/sensor/new + -H "Authorization: provide + your API key here" -d '{"added_by":"system"}'
Mac AddressJSONMac Address(Required) Mac Address Example: curl https://api.teeseng.uk/api/v0/sensor/new + -H "Authorization: provide + your API key here" -d '{"mac_address": "99-6A-F8-7D-B4-94"}'
DescriptionJSONDescription of Sensor(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/sensor/new + -H "Authorization: provide + your API key here" -d '{"description":"test"}'
Mac AddressJSONMac Address(Required) Mac Address Example: curl https://api.teeseng.uk/api/v0/sensor/new + -H "Authorization: provide + your API key here" -d '{"mac_address": "99-6A-F8-7D-B4-94"}'
LocationJSONLocation(Required) Location Example: curl https://api.teeseng.uk/api/v0/sensor/new + -H "Authorization: provide + your API key here" -d '{"location": "11"}'
+
+
+

Update Sensor (Only for system or admin API key)

+

+ To update a Sensor you need to make a PUT call to the following url :
+ https://api.teeseng.uk/api/v0/sensor/update +
+
+ Example :
+ curl https://api.teeseng.uk/api/v0/sensor/update -H "Content-Type: application/json" -X POST -d '{"id": "2" ,"sensorname": "test", "added_by": "system" , "mac_address": "99-6A-F8-7D-B4-94" , "description": "test123" , "location": "11" }' +
+
+ Return Response :
+ {"status":"200","message":"Sensor 2 updated"} +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSONYour API key.(Required) example: curl https://api.teeseng.uk/api/v0/sensor/update -H + "Authorization: {provide your + API key here}"
IDJSONSensor ID(Required) Sensor ID Example: curl https://api.teeseng.uk/api/v0/sensor/update + -H "Authorization: provide + your API key here" -d '{"id": "7"}'
Sensor nameJSONSensor name.(Optional) Sensor name. Example: curl https://api.teeseng.uk/api/v0/sensor/update + -H "Authorization: provide + your API key here" -d '{"sensorname": "test"}'
Added by JSONSystem or Admin(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/sensor/update + -H "Authorization: provide + your API key here" -d '{"added_by":"system"}'
Mac Address JSONMac Address(Optional) Mac Address Example: curl https://api.teeseng.uk/api/v0/sensor/update + -H "Authorization: provide + your API key here" -d '{"mac_address": "99-6A-F8-7D-B4-94"}'
DescriptionJSONDescription of Sensor(Optional) Description of Sensor Example: curl + https://api.teeseng.uk/api/v0/sensor/update + -H "Authorization: provide + your API key here" -d '{"description":"test"}'
LocationJSONLocation of Sensor(Optional) Location of Sensor Example: curl + https://api.teeseng.uk/api/v0/sensor/update + -H "Authorization: provide + your API key here" -d '{"location": "11"}'
+
+
+

Delete Sensor (Only for system or admin API key)

+

+ To delete a sensor you need to make a DELETE call to the following url :
+ https://api.teeseng.uk/api/v0/sensor/delete +
+
+ Example :
+ curl https://api.teeseng.uk/api/v0/sensor/delete -H "Content-Type: application/json" -X POST -d '{"id": "7"}' +

+
+

QUERY PARAMETERS

+ + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescription
AuthorizationJSONYour API key.(Required) example: curl https://api.teeseng.uk/api/v0/sensor/delete -H + "Authorization: {provide your + API key here}"
IDJSONSensor ID(Required) Sensor ID Example: curl https://api.teeseng.uk/api/v0/sensor/delete + -H "Authorization: provide + your API key here" -d '{"id": "7"}'
+
+

API Keys

+

+ You can generate API Keys here: +

+
+ + +
+ + +
+ +
+
+ +
+

Errors

+

+ The EcoSaver API uses the following error codes: +

+ + + + + + + + + + + + + + + + + + + + + + +
Error CodeMeaning
X000 + Some parameters are missing. This error appears when you don't pass every + mandatory + parameters. +
403 + Unknown or unvalid secret_key. This error + appears if + you use an unknow API key or if your API key expired. +
500 + Unvalid secret_key No API key was supplied. + Invalid + request. +
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/contact.ejs b/consumerWebsite/views/contact.ejs index edc98f5..670c760 100644 --- a/consumerWebsite/views/contact.ejs +++ b/consumerWebsite/views/contact.ejs @@ -42,7 +42,8 @@

E: - leongdingxuan@gmail.com + ecosaverx@gmail.com +

@@ -55,17 +56,18 @@

+

Send us a Message

-
- + +
- +
- +
@@ -75,6 +77,8 @@
+
+
diff --git a/consumerWebsite/views/index.ejs b/consumerWebsite/views/index.ejs index 921e105..c4419a3 100644 --- a/consumerWebsite/views/index.ejs +++ b/consumerWebsite/views/index.ejs @@ -1,11 +1,60 @@ <%- include('top') %> - @@ -53,56 +102,60 @@
-
-

Services

- -
-
-
-

Air Quality Index

-
-

15 - 18 PSI

-
- +
+

Services

+ +
+
+
+

Air Quality Index

+
+

Average: {{average.psi}} PSI

+

Latest: {{latest.psi}} PSI

-
-
-
-

Humidity

-
-

70% - 75%

-
- + -
-
-
-

Temperature

-
-

30° - 37°

-
- +
+
+
+
+

Humidity

+
+

Average: {{average.humidity}} %

+

Latest: {{latest.humidity}} %

-
-
-
-

Another Category

-
-

values

-
- + -
-
- +
+
+
+
+

Temperature

+
+

Average: {{average.temperature}}°

+

Latest: {{latest.temperature}}°

+
+ +
+
+
+
+

Wind Speed

+
+

Average: {{average.windspeed}} Km/h

+

Latest: {{latest.windspeed}} Km/h

+
+ +
+
+
+
diff --git a/consumerWebsite/views/logintop.ejs b/consumerWebsite/views/logintop.ejs index 80f9355..081ef8b 100644 --- a/consumerWebsite/views/logintop.ejs +++ b/consumerWebsite/views/logintop.ejs @@ -27,7 +27,7 @@ - + @@ -44,19 +44,22 @@ $(document).ready(function () { //check if user is logged in app.auth.isLoggedIn(function (error, data) { - if (data) { - $('#cl-logout-button').show('fast'); - $('#cl-profile-button').show('fast'); - $('#cl-login-button').hide('fast'); + if (!error) { + $("#cl-logout-button").show("fast"); + $("#cl-api-button").show("fast"); + $("#cl-profile-button").show("fast"); + $("#cl-login-button").hide("fast"); + } else { $('#cl-login-button').show('fast'); + } $('body').show('fast') }); }); - +