Update CSS, add relationship between sensorModel and locationModel, and remove console.log statements

This commit is contained in:
newtbot 2024-01-26 04:07:11 +08:00
parent ba498a4d4b
commit d38423fa6d
19 changed files with 213 additions and 205 deletions

View File

@ -9,13 +9,13 @@ const sequelize = new Sequelize(
process.env.DB_USER, process.env.DB_USER,
process.env.DB_PASS, process.env.DB_PASS,
{ {
host: "mpsqldatabase.mysql.database.azure.com", host: "mpsqldatabasean.mysql.database.azure.com",
dialect: 'mysql', dialect: 'mysql',
// attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy'; // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy';
attributeBehavior: 'escape', attributeBehavior: 'escape',
dialectOptions: { dialectOptions: {
ssl: { ssl: {
ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt.pem')), ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt_3.pem')),
}, },
}, },

2
api.MD
View File

@ -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/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"}'

View File

@ -50,49 +50,48 @@ app.use("/", require("./routes/render"));
// Catch 404 and forward to error handler. If none of the above routes are // Catch 404 and forward to error handler. If none of the above routes are
// used, this is what will be called. // used, this is what will be called.
app.use(function (req, res, next) { app.use(function (req, res, next) {
//application/json; charset=utf-8 //application/json; charset=utf-8
if (req.is("application/json" || "application/json; charset=utf-8")) { var err = new Error("Not Found");
var err = new Error("Not Found"); err.message = "Page not found";
err.message = "Page not found"; err.status = 404;
err.status = 404; next(err);
next(err);
} else {
res.status(404).render("404");
}
}); });
// Error handler. This is where `next()` will go on error // Error handler. This is where `next()` will go on error
app.use(function (err, req, res, next) { app.use(function (err, req, res, next) {
console.error(err.status || res.status, err.name, req.method, req.url); 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.log(err.name + " validation error"); // Parse key error for Sequilzw
// Parse key error for Sequilzw let keyErrors = {};
let keyErrors = {}; if (["SequelizeValidationError"].includes(err.name) && err.errors) {
if (["SequelizeValidationError"].includes(err.name) && err.errors) { for (let item of err.errors) {
for (let item of err.errors) { if (item.path) {
if (item.path) { keyErrors[item.path] = item.message;
keyErrors[item.path] = item.message; }
} }
} res.status = 422;
res.status = 422; }
}
if (![404, 422].includes(err.status || res.status)) { if (![404, 401, 422].includes(err.status || res.status)) {
console.error(err.message); console.error(err.message);
console.error(err.stack); console.error(err.stack);
console.error("========================================="); console.error("=========================================");
} }
res.status(err.status || 500);
// res.status(err.status || 500);
if (req.get('Content-Type') && req.get('Content-Type').includes("json")) {
res.status(err.status || 500); res.json({
res.json({ name: err.name || "Unknown error",
name: err.name || "Unknown error", message: err.message,
message: err.message, keyErrors,
keyErrors, });
}); }
}); else {
res.json({
name: err.name || "Unknown error",
message: err.message,
keyErrors,
});
}
});

View File

@ -107,4 +107,6 @@ const sensorModel = sequelize.define(
} }
); );
sensorModel.belongsTo(locationModel);
module.exports = { sensorModel }; module.exports = { sensorModel };

View File

@ -3,7 +3,7 @@ const { Sequelize, DataTypes } = require("sequelize");
const { sequelize } = require("../mySQL"); const { sequelize } = require("../mySQL");
const { userModel } = require("./userModel"); const { userModel } = require("./userModel");
//sequelize.sync(); sequelize.sync();
const tokenModel = sequelize.define( const tokenModel = sequelize.define(
"token", "token",
{ {

View File

@ -9,13 +9,13 @@ const sequelize = new Sequelize(
process.env.DB_USER, process.env.DB_USER,
process.env.DB_PASS, process.env.DB_PASS,
{ {
host: "mpsqldatabase.mysql.database.azure.com", host: "mpsqldatabasean.mysql.database.azure.com",
dialect: 'mysql', dialect: 'mysql',
// attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy'; // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy';
attributeBehavior: 'escape', attributeBehavior: 'escape',
dialectOptions: { dialectOptions: {
ssl: { ssl: {
ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt.pem')), ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt_3.pem')),
}, },
}, },

View File

@ -1,19 +1,41 @@
const { hash, compareHash } = require("./bcrypt.js");
const { tokenModel } = require("../database/model/tokenModel.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 { generateUUID } = require("./generateUUID.js");
const { isValid } = require("./isValid");
/* async function getTokenByToken(token) {
1) take userid const splitAuthToken = token.split("-");
2) generate random api key const rowid = splitAuthToken[0];
3) hash the api key const suppliedToken = splitAuthToken.slice(1).join("-");
4) append userid with - and api key
5) you give the user rowid-uuidv4 token = await tokenModel.findByPk(rowid, { include: userModel });
6) store in database
*/ token.isValid = await compareHash(suppliedToken, token.token); //true
//can be used for api key or token. Both are the same logic console.log("function api getTokenByToken token", token.isValid);
async function addToken(userId, permission , expiry) { 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 uuid = await generateUUID();
let hashtoken = await hash(uuid); 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({ let token = await tokenModel.create({
userid: userId, userid: userId,
@ -26,23 +48,4 @@ async function addToken(userId, permission , expiry) {
return token.id + "-" + uuid; return token.id + "-" + uuid;
} }
async function checkToken(Supplied, rowid) { module.exports = { addToken, getTokenByToken };
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 };

View File

@ -1,20 +1,14 @@
const moment = require("moment");
const currentTime = moment().format("YYYY-MM-DD HH:mm:ss");
//time is taken from the token //time is taken from the token
function isValid(time){ function isValid(time) {
const timeDiff = moment(currentTime).diff(time, "minutes");
if (timeDiff > 1) {
console.log(timeDiff);
return false;
}
return true;
if (
Math.floor(new Date(time).getTime() / 1000) <
Math.floor(new Date().getTime() / 1000)
) {
return false;
}
return true;
} }
module.exports = { isValid };
module.exports = { isValid };

View File

@ -18,15 +18,19 @@ async function getSensorData() {
const sensorData = await sensorDataModel.findAll(); const sensorData = await sensorDataModel.findAll();
return sensorData; return sensorData;
} }
let dataArray = [];
async function addSensorData(id_sensor, id_location, sensordata) { async function addSensorData(id_sensor, id_location, sensordata) {
const sensorData = await sensorDataModel.create({ const sensorData = await sensorDataModel.create({
sensorid: id_sensor, sensorid: id_sensor,
locationid: id_location, locationid: id_location ,
measurement: sensordata, measurement: sensordata.measurement,
}); });
io().emit('sensorData:new', sensorData) //console.log("sensorData", sensorData);
//console.log("sensorData", sensordata);
dataArray.push(sensordata);
console.log("dataArray", dataArray);
io().emit('sensorData:new', dataArray)
return sensorData; return sensorData;
} }
@ -61,6 +65,14 @@ async function getSensorDataById(id) {
}); });
return sensorData; return sensorData;
} }
async function getLatestData() {
const sensorData = await sensorDataModel.findAll({
limit: 1,
order: [["createdAt", "DESC"]],
});
return sensorData;
}
var ormQuery = {}; var ormQuery = {};
var whereClause = {}; var whereClause = {};
var whereDate = {}; var whereDate = {};
@ -724,5 +736,6 @@ module.exports = {
getSensorDataById, getSensorDataById,
getData, getData,
getDatabyRange, getDatabyRange,
getLatestData,
}; };

View File

@ -2,7 +2,6 @@ const { Op } = require('sequelize')
const { hash, compareHash } = require("./bcrypt.js"); const { hash, compareHash } = require("./bcrypt.js");
const { addToken } = require("./api"); const { addToken } = require("./api");
const { userModel } = require("../database/model/userModel"); const { userModel } = require("../database/model/userModel");
moment = require('moment')
@ -71,9 +70,11 @@ async function loginUser(user) {
if (!match) return false; if (!match) return false;
//console.log('loginUser', userRes.id, userRes.username); //console.log('loginUser', userRes.id, userRes.username);
//generate token and permission and experiation time //generate token and permission and experiation time + 30 mins
const currentTime = moment().format('YYYY-MM-DD HH:mm:ss'); //let tokenToLive = moment().add(30, 'minutes').format();
let token = await addToken(userRes.id , "canRead" , currentTime); 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 }; return { token: token, userid: userRes.id, username: userRes.username };
} }

View File

@ -1,68 +1,36 @@
const { tokenModel } = require("../database/model/tokenModel"); const { getTokenByToken } = require("../functions/api");
const { userModel } = require("../database/model/userModel");
const { compareHash } = require("../functions/bcrypt");
const { checkToken } = require("../functions/api");
const { isValid } = require("../functions/isValid");
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) { async function auth(req, res, next) {
try { try {
const authToken = req.header("auth-token"); const token = await getTokenByToken(req.header("auth-token"));
if (!authToken) {
const error = new Error("No Token key was supplied. Invalid request"); if (!token || !token.isValid){
throw error; 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 //if token is a match
req.token = token; req.token = token;
req.user = await token.getUser(); req.user = await token.getUser();
const permission = await checkToken(suppliedToken, rowid);
const route = req.originalUrl.split("?")[0]; // Removing query parameters 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 is from user/ and permission is canRead allow it to do CRUD
if (route.includes("/user/") && permission === "canRead") { if (route.includes("/user/") && token.permission === "canRead") {
next(); return next();
} }
if ((req.method === "GET" && permission === "canRead") || (["GET", "POST", "PUT", "DELETE"].includes(req.method) && permission === "canWrite")) { if ((req.method === "GET" && token.permission === "canRead") || (["GET", "POST", "PUT", "DELETE"].includes(req.method) && token.permission === "canWrite")) {
next(); return next();
}
if (!isValid(token.expiration)){
req.token.destroy();
throw new Error("Token expired");
} }
throw permissionError
} catch (error) { } catch (error) {
next(error); next(error);
} }
} }
module.exports = { auth }; module.exports = { auth };
/*
else {
const error = new Error("Insufficient permission");
error.status = 401;
throw error;
}
*/

View File

@ -3812,6 +3812,7 @@
.card-text { .card-text {
color: #000000; color: #000000;
font-size: 16px;
} }
/* edit profile */ /* edit profile */

View File

@ -186,9 +186,7 @@ app.auth = (function (app) {
function isLoggedIn(callback) { function isLoggedIn(callback) {
if (getToken()) { if (getToken()) {
console.log("you shldnt appear at all");
return app.api.get("user/me", function (error, data) { return app.api.get("user/me", function (error, data) {
console.log(error, data);
if (!error) app.auth.user = data; if (!error) app.auth.user = data;
return callback(error, data); return callback(error, data);
}); });

View File

@ -18,5 +18,8 @@ router.use('/sensor', [auth, APIlogger], require('./sensor.js'));
//sensor data route //sensor data route
router.use('/sensor-data', [auth, APIlogger], require('./sensorData.js')); 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; module.exports = router;

View File

@ -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;

View File

@ -8,7 +8,7 @@ const router = express.Router();
router.get("/me", async function (req, res, next) { router.get("/me", async function (req, res, next) {
try { try {
let user = await getUserByID(req.user); let user = await getUserByID(req.user);
console.log(user); //console.log(user);
res.json({ res.json({
user: user, user: user,
}); });

View File

@ -1,9 +1,17 @@
<%- include('top') %> <%- include('top') %>
<script> <script>
//call socket.io $(document).ready(async function () {
app.api.get('latest-sensor-data/data', function (error, data) {
//console.log(data[0]);
$.scope.LatestSensorData.push(data[0]);
});
});
//call socket.io to get live data
app.socket.on("sensorData:new", function (data) { app.socket.on("sensorData:new", function (data) {
console.log("new data!!")
console.log(data); console.log(data[0]);
$.scope.LatestSensorData.update(data[0]);
}); });
</script> </script>
@ -53,56 +61,56 @@
</header> </header>
<!-- Page Content --> <!-- Page Content -->
<div class="container"> <div class="container">
<div class="services-bar"> <div class="services-bar" jq-repeat="LatestSensorData">
<h1 class="my-4">Services</h1> <h1 class="my-4">Services</h1>
<!-- Services Section --> <!-- Services Section -->
<div class="row"> <div class="row">
<div class="col-lg-3 mb-4"> <div class="col-lg-3 mb-4">
<div class="card"> <div class="card">
<h4 class="card-header">Air Quality Index</h4> <h4 class="card-header">Air Quality Index</h4>
<div class="card-body text-center"> <div class="card-body text-center">
<p class="card-text display-4">15 - 18 PSI</p> <p class="card-text display-4">{{ measurement.psi }} PSI</p>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div> </div>
</div> <div class="card-footer">
<div class="col-lg-3 mb-4"> <a href="/learnmore" class="btn btn-primary">Learn More</a>
<div class="card">
<h4 class="card-header">Humidity</h4>
<div class="card-body text-center">
<p class="card-text display-4">70% - 75%</p>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div> </div>
</div> </div>
<div class="col-lg-3 mb-4"> </div>
<div class="card"> <div class="col-lg-3 mb-4">
<h4 class="card-header">Temperature</h4> <div class="card">
<div class="card-body text-center"> <h4 class="card-header">Humidity</h4>
<p class="card-text display-4">30&deg; - 37&deg;</p> <div class="card-body text-center">
</div> <p class="card-text display-4">{{ measurement.humidity }} %</p>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div> </div>
</div> <div class="card-footer">
<div class="col-lg-3 mb-4"> <a href="/learnmore" class="btn btn-primary">Learn More</a>
<div class="card">
<h4 class="card-header">Another Category</h4>
<div class="card-body text-center">
<p class="card-text display-4">values</p>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-3 mb-4">
<div class="card">
<h4 class="card-header">Temperature</h4>
<div class="card-body text-center">
<p class="card-text display-4">{{ measurement.temperature }}&deg;</p>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div>
</div>
<div class="col-lg-3 mb-4">
<div class="card">
<h4 class="card-header">Wind Speed</h4>
<div class="card-body text-center">
<p class="card-text display-4">{{ measurement.windspeed }} Km/h </p>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div>
</div>
</div>
<!-- /.row --> <!-- /.row -->
</div> </div>
<!-- About Section --> <!-- About Section -->

View File

@ -8,10 +8,9 @@
<ul id="sensorDataList"> <ul id="sensorDataList">
<li jq-repeat='sensorData'> <li jq-repeat='sensorData'>
rowid: {{ id }}
sensorId: {{ sensorid }} sensorId: {{ sensorid }}
created: {{ createdAt }}
location: {{ locationid }} location: {{ locationid }}
created: {{ createdAt }}
<br /> <br />
co: {{ measurement.co }} co: {{ measurement.co }}
humidity: {{ measurement.humidity }} humidity: {{ measurement.humidity }}
@ -30,7 +29,7 @@
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(async function () { $(document).ready(async function () {
app.api.get('sensor-data/data?order=DESC&limit=40', function(error, data){ app.api.get('sensor-data/data?order=DESC&limit=6', function(error, data){
$.scope.sensorData.push(...data); $.scope.sensorData.push(...data);
}) })
}) })

View File

@ -57,9 +57,7 @@
//check if user is logged in //check if user is logged in
app.auth.isLoggedIn(function (error, data) { app.auth.isLoggedIn(function (error, data) {
if (!error) { if (!error) {
console.log(error);
$.scope.getUsername.update(data); $.scope.getUsername.update(data);
if (location.pathname == "/profile") { if (location.pathname == "/profile") {
$.scope.getUserDetails.update(data); $.scope.getUserDetails.update(data);
} }