This commit is contained in:
Leo 2024-01-24 15:51:56 +08:00
commit ba5b10a068
54 changed files with 812 additions and 999 deletions

View File

@ -1,7 +1,6 @@
"use strict"; "use strict";
const { Sequelize, DataTypes } = require("sequelize"); const { Sequelize, DataTypes } = require("sequelize");
const { sequelize } = require("./mySQL"); const { sequelize } = require("./mySQL");
const { isAlphaNumericwithSpaces } = require('../../Web-Server/functions/validateData')
//sequelize.sync(); //sequelize.sync();
const locationModel = sequelize.define( const locationModel = sequelize.define(

View File

@ -2,11 +2,7 @@
const { Sequelize, DataTypes } = require("sequelize"); const { Sequelize, DataTypes } = require("sequelize");
const { sequelize } = require("./mySQL"); const { sequelize } = require("./mySQL");
const { locationModel } = require("./locationModel"); const { locationModel } = require("./locationModel");
const {
isAlphaNumericwithSpaces,
isAlphaNumericWithSpacesAndDash,
isMacAddress,
} = require("../../Web-Server/functions/validateData");
//sequelize.sync(); //sequelize.sync();
const sensorModel = sequelize.define( const sensorModel = sequelize.define(

View File

@ -1,5 +1,5 @@
const { locationModel } = require("../Database/locationModel"); const { locationModel } = require("../database/locationModel");
const { sensorModel } = require("../Database/sensorModel"); const { sensorModel } = require("../database/sensorModel");
async function getLocation() { async function getLocation() {
const location = locationModel.findAll({ const location = locationModel.findAll({

View File

@ -27,9 +27,9 @@ client.on("error", (err) => {
}); });
//every 15 minutes //every 15 minutes
setInterval(publishData, 900000); //setInterval(publishData, 900000);
//every 1 minute //every 1 minute
//setInterval(publishData, 60000); setInterval(publishData, 60000);

2
api.MD
View File

@ -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/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": "5", "permission": "canRead"}' curl localhost:3000/api/v0/apikey/new -H "Content-Type: application/json" -X POST -d '{"userid": "5", "permission": "canRead"}'

View File

@ -1,29 +1,46 @@
const express = require("express"); const express = require("express");
const { rateLimit } = require("express-rate-limit");
const path = require("path"); const path = require("path");
const app = express(); const app = express();
const port = 3000; const port = 3000;
const ejs = require("ejs"); const ejs = require("ejs");
module.exports = app;
app.use(express.json()); app.use(express.json());
app.set("json spaces", 2); app.set("json spaces", 2);
//express-rate-limit stolen from docs
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 600, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
});
// Hold list of functions to run when the server is ready
app.onListen = [function(){console.log('Express is ready')}];
// Apply the rate limiting middleware to all requests.
app.use(limiter);
//disable x-powered-by header for security reasons
app.disable("x-powered-by");
// Set up the templating engine to build HTML for the front end. // Set up the templating engine to build HTML for the front end.
app.set("views", path.join(__dirname, "../views")); app.set("views", path.join(__dirname, "./views"));
app.set("view engine", "ejs"); app.set("view engine", "ejs");
// Have express server static content( images, CSS, browser JS) from the public // Have express server static content( images, CSS, browser JS) from the public
app.use(express.static(path.join(__dirname, "../public"))); app.use(express.static(path.join(__dirname, "./public")));
//middleware logic ( called by next() )
const auth = require("../middleware/authChecker");
//route logic //route logic
app.use("/api/v0", require("../routes/api_routes")); app.use("/api/seed/v0" ,require("./routes/seed_route.js"));
app.use("/api/v0", require("./routes/api_routes"));
//render logic //render logic
app.use("/", require("../routes/render")); 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.
@ -69,8 +86,4 @@ app.use(function (err, req, res, next) {
keyErrors, keyErrors,
}); });
}); });
app.listen(port, () => {
console.log(`app listening on port ${port}`);
});
module.exports = { app };

102
consumerWebsite/bin/www Normal file
View File

@ -0,0 +1,102 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
const app = require('../app');
const mqttApp = require('../mqttApp');
const debug = require('debug')('proxy-api:server');
const http = require('http');
const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname, '../../.env')})
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.NODE_PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
//create server with app
var server = http.createServer(app);
var io = require('socket.io')(server);
app.io = io;
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log('Listening on ' + bind);
// execute list of functions when app is ready
for(let listener of app.onListen){
listener()
}
}

View File

@ -0,0 +1,47 @@
const { hash, compareHash } = require("./bcrypt.js");
const { apikeyModel } = require("../database/model/apiKeyModel");
const { generateUUID } = require("./generateUUID.js");
/*
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 addAPIKey(userId, permission) {
let hashtoken = await generateUUID();
let apikey = await hash(hashtoken);
let token = await apikeyModel.create({
userid: userId,
apikey: apikey,
permission: permission,
});
//user token with - tokenid is table id
return token.id + "-" + hashtoken;
}
async function checkAPikey(SuppliedKey, rowid) {
try {
const retrivedKey = await apikeyModel.findOne({
raw: true,
attributes: ["apikey", "permission"],
where: {
id: rowid,
},
});
//console.log(retrivedKey.apikey);
if (compareHash(SuppliedKey, retrivedKey.apikey)) {
//return true;
return retrivedKey.permission;
}
} catch (error) {
console.error(error);
}
}
module.exports = { addAPIKey , checkAPikey };

View File

@ -2,44 +2,19 @@ const bcrypt = require('bcrypt');
const saltRounds = 10; const saltRounds = 10;
//https://github.com/kelektiv/node.bcrypt.js#readme //https://github.com/kelektiv/node.bcrypt.js#readme
/*
// Load hash from your password DB.
bcrypt.compare(myPlaintextPassword, hash, function(err, result) {
// result == true
});
bcrypt.compare(someOtherPlaintextPassword, hash, function(err, result) {
// result == false
});
*/
/*
//hash with salt
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB.
});
*/
//hash for pass or token lol doesnt matter //hash for pass or token lol doesnt matter
async function hashPassword(password) { async function hash(password) {
return await bcrypt.hash(password, saltRounds); return await bcrypt.hash(password, saltRounds);
} }
async function hashAPIKey(apikey) {
return await bcrypt.hash(apikey, saltRounds);
}
//can be used to compare password or token //can be used to compare password or token
async function comparePassword(password, hash) { async function compareHash(password, hash) {
return await bcrypt.compare(password, hash); return await bcrypt.compare(password, hash);
} }
module.exports = { module.exports = {
hashPassword, hash,
hashAPIKey, compareHash
comparePassword
}; };

View File

@ -0,0 +1,56 @@
const {locationModel} = require("../database/model/locationModel");
async function getLocation() {
const location = await locationModel.findAll();
return location;
}
async function addLocation(name, added_by, description) {
console.log(name, added_by, description);
const location = await locationModel.create({
name: name,
added_by: added_by,
description: description,
});
}
async function updateLocation(id, name, added_by, description) {
const location = await locationModel.update(
{
name: name,
added_by: added_by,
description: description,
},
{
where: {
id: id,
},
}
);
}
async function deleteLocation(id) {
//delete by id
const location = await locationModel.destroy({
where: {
id: id,
},
});
}
async function getLocationById(id) {
const location = await locationModel.findAll({
where: {
id: id,
},
});
return location;
}
module.exports = {
getLocation,
addLocation,
updateLocation,
deleteLocation,
getLocationById,
};

View File

@ -0,0 +1,23 @@
const { api_log_Model } = require("../database/model/apiLogModel.js");
async function insertLogData(log) {
try {
api_log_Model.create({
ip: log.ip,
time: log.time,
method: log.method,
host: log.host,
statusCode: log.statusCode,
Responsesize: log.Responsesize,
referrer: log.referrer,
userAgent: log.userAgent,
});
} catch (error) {
console.error(error);
}
}
module.exports = {
insertLogData,
};

View File

@ -0,0 +1,75 @@
const {sensorModel} = require("../database/model/sensorModel");
async function getSensor() {
const sensor = await sensorModel.findAll();
return sensor;
}
async function addSensor(
sensorname,
added_by,
mac_address,
description,
location
) {
const sensor = await sensorModel.create({
name: sensorname,
added_by: added_by,
mac_address: mac_address,
description: description,
location: location,
});
}
async function updateSensor(
id,
sensorname,
added_by,
mac_address,
description,
location
) {
const sensor = await sensorModel.update(
{
name: sensorname,
added_by: added_by,
mac_address: mac_address,
description: description,
location: location,
},
{
where: {
id: id,
},
}
);
}
async function deleteSensor(id) {
//delete by id
const sensor = await sensorModel.destroy({
where: {
id: id,
},
});
console.error(error);
}
async function getSensorById(id) {
const sensor = await sensorModel.findAll({
where: {
id: id,
},
});
return sensor;
}
module.exports = {
getSensor,
addSensor,
updateSensor,
deleteSensor,
getSensorById,
};

View File

@ -1,8 +1,7 @@
const { sequelize } = require("../Database/mySql.js");
const { locationModel } = require("../Database/model/locationModel.js");
const { sensorModel } = require("../Database/model/sensorModel.js");
const { sensorDataModel } = require("../Database/model/sensorDataModel.js");
const { Op, Sequelize } = require("sequelize"); const { Op, Sequelize } = require("sequelize");
const { sequelize } = require("../database/mySQL.js");
const { sensorDataModel } = require("../database/model/sensorDataModel.js");
const io = require('../functions/socket');
//helper function to convert month name to month number //helper function to convert month name to month number
//https://stackoverflow.com/questions/13566552/easiest-way-to-convert-month-name-to-month-number-in-js-jan-01 //https://stackoverflow.com/questions/13566552/easiest-way-to-convert-month-name-to-month-number-in-js-jan-01
@ -13,130 +12,22 @@ function getMonthFromString(mon) {
} }
return -1; return -1;
} }
async function getLocation() {
const location = await locationModel.findAll();
return location;
}
async function addLocation(name, added_by, description) {
const location = await locationModel.create({
name: name,
added_by: added_by,
description: description,
});
}
async function updateLocation(id, name, added_by, description) {
const location = await locationModel.update(
{
name: name,
added_by: added_by,
description: description,
},
{
where: {
id: id,
},
}
);
}
async function deleteLocation(id) {
//delete by id
const location = await locationModel.destroy({
where: {
id: id,
},
});
}
async function getLocationById(id) {
const location = await locationModel.findAll({
where: {
id: id,
},
});
return location;
}
async function getSensor() {
const sensor = await sensorModel.findAll();
return sensor;
console.error(error);
}
async function addSensor(
sensorname,
added_by,
mac_address,
description,
location
) {
const sensor = await sensorModel.create({
name: sensorname,
added_by: added_by,
mac_address: mac_address,
description: description,
location: location,
});
}
async function updateSensor(
id,
sensorname,
added_by,
mac_address,
description,
location
) {
const sensor = await sensorModel.update(
{
name: sensorname,
added_by: added_by,
mac_address: mac_address,
description: description,
location: location,
},
{
where: {
id: id,
},
}
);
}
async function deleteSensor(id) {
//delete by id
const sensor = await sensorModel.destroy({
where: {
id: id,
},
});
console.error(error);
}
async function getSensorById(id) {
const sensor = await sensorModel.findAll({
where: {
id: id,
},
});
return sensor;
}
async function getSensorData() { async function getSensorData() {
const sensorData = await sensorDataModel.findAll(); const sensorData = await sensorDataModel.findAll();
return sensorData; return sensorData;
} }
async function addSensorData(id, id_sensor, id_location, sensordata) { async function addSensorData(id_sensor, id_location, sensordata) {
const sensorData = await sensorDataModel.create({ const sensorData = await sensorDataModel.create({
id: id,
sensorid: id_sensor, sensorid: id_sensor,
locationid: id_location, locationid: id_location,
measurement: sensordata, measurement: sensordata,
}); });
io().emit('sensordata:new', sensorData)
return sensorData;
} }
async function updateSensorData(id, id_sensor, id_location, sensordata) { async function updateSensorData(id, id_sensor, id_location, sensordata) {
@ -826,16 +717,6 @@ async function getDatabyRange(queryString) {
} }
module.exports = { module.exports = {
getLocation,
addLocation,
updateLocation,
deleteLocation,
getLocationById,
getSensor,
addSensor,
updateSensor,
deleteSensor,
getSensorById,
getSensorData, getSensorData,
addSensorData, addSensorData,
updateSensorData, updateSensorData,
@ -843,4 +724,5 @@ module.exports = {
getSensorDataById, getSensorDataById,
getData, getData,
getDatabyRange, getDatabyRange,
}; };

View File

@ -0,0 +1,17 @@
const app = require("../app");
const io = ()=> app.io;
// We have to wait for the express HTTP server to be finished starting before we
// can use any of the socket.io stuff.
app.onListen.push(function(){
app.io.on('connection', (socket) => {
console.log('User connected via WebsSocket')
socket.on('disconnect', (socket) => {
console.log('User disconnect via WebsSocket')
});
});
});
module.exports = io;

View File

@ -1,13 +1,9 @@
const { sequelize } = require("../database/mySql.js"); const { Op } = require('sequelize')
const { apikeyModel } = require("../database/model/apikeyModel.js"); const { hash, compareHash } = require("./bcrypt.js");
const { userModel } = require("../database/model/userModel.js"); const { addAPIKey } = require("./api");
const { Op, Sequelize } = require("sequelize"); const { userModel } = require("../database/model/userModel");
const { generateUUID } = require("../functions/generateUUID.js");
const {
hashPassword,
comparePassword,
hashAPIKey,
} = require("../functions/bcrypt.js");
//getuser //getuser
//api/v0/user/me //api/v0/user/me
@ -24,6 +20,28 @@ async function getUserID(userid) {
return userRes; return userRes;
} }
//register
//api/v0/auth/register
async function addUser(user) {
//hash password
let hashed = await hash(user.password);
const addRes = await userModel.create({
firstname: user.firstname,
lastname: user.lastname,
username: user.username,
password: hashed,
email: user.email,
address: user.address,
phone: user.phone,
});
if (addRes) {
return true;
} else {
return false;
}
}
//api/v0/auth/register //api/v0/auth/register
/* Registering new user /* Registering new user
1) req.body is taken from html form or wtv 1) req.body is taken from html form or wtv
@ -32,13 +50,13 @@ async function getUserID(userid) {
*/ */
async function addUser(user) { async function addUser(user) {
//hash password //hash password
let hash = await hashPassword(user.password); let hashed = await hash(user.password);
const addRes = await userModel.create({ const addRes = await userModel.create({
firstname: user.firstname, firstname: user.firstname,
lastname: user.lastname, lastname: user.lastname,
username: user.username, username: user.username,
password: hash, password: hashed,
email: user.email, email: user.email,
address: user.address, address: user.address,
phone: user.phone, phone: user.phone,
@ -70,7 +88,7 @@ async function loginUser(user) {
if (!userRes) return false; if (!userRes) return false;
// Compare passwords // Compare passwords
let match = await comparePassword(user.password, userRes.password); let match = await compareHash(user.password, userRes.password);
if (!match) return false; if (!match) return false;
//console.log('loginUser', userRes.id, userRes.username); //console.log('loginUser', userRes.id, userRes.username);
@ -89,20 +107,6 @@ async function loginUser(user) {
6) store in database 6) store in database
*/ */
//can be used for api key or token. Both are the same logic
async function addAPIKey(userId, permission) {
let hashtoken = await generateUUID();
let apikey = await hashAPIKey(hashtoken);
let token = await apikeyModel.create({
userid: userId,
apikey: apikey,
permission: permission,
});
//user token with - tokenid is table id
return token.id + "-" + hashtoken;
}
//api/v0/user/update //api/v0/user/update
async function updateProfile(user, body) { async function updateProfile(user, body) {
@ -125,7 +129,7 @@ async function updateProfile(user, body) {
if (!updateUser) return false; if (!updateUser) return false;
return true; return true;
} else { } else {
let hash = await hashPassword(body.password); let hashed = await hash(body.password);
let updateUser = await userModel.update( let updateUser = await userModel.update(
{ {
firstname: body.first_name, firstname: body.first_name,
@ -134,7 +138,7 @@ async function updateProfile(user, body) {
email: body.email, email: body.email,
address: body.address, address: body.address,
phone: body.phone, phone: body.phone,
password: hash, password: hashed,
}, },
{ {
where: { where: {
@ -152,5 +156,4 @@ module.exports = {
addUser, addUser,
loginUser, loginUser,
updateProfile, updateProfile,
addAPIKey,
}; };

View File

@ -1,5 +1,27 @@
var validator = require("validator"); var validator = require("validator");
const dateRegex = /^[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/;
function isValidDateString(value) {
return dateRegex.test(value);
}
function isMacAddress(value) {
// Joi.string().regex(/^([0-9a-f]{2}-){5}([0-9a-f]{2})$/i).lowercase()
//return validator.isMACAddress(value, { no_separators: true, eui: 48 });
const macAddress = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
const valid = macAddress.test(value);
return valid;
}
function isNumber(value) {
if (typeof value === "number") {
return true;
}
}
function isAlphaNumericwithSpaces(value) { function isAlphaNumericwithSpaces(value) {
return validator.isAlphanumeric(value, ["en-US"], { ignore: " " }); return validator.isAlphanumeric(value, ["en-US"], { ignore: " " });
} }
@ -16,7 +38,6 @@ function isAlphaNumericWithSpacesAndDash(value) {
function isJson(value) { function isJson(value) {
//check if its object //check if its object
if(typeof value === "object"){ if(typeof value === "object"){
console.log("its an object")
return true return true
} }
@ -44,6 +65,8 @@ module.exports = {
isAlphaNumericwithSpaces, isAlphaNumericwithSpaces,
isAlphaNumericWithSpacesAndDash, isAlphaNumericWithSpacesAndDash,
isJson, isJson,
isAddress isAddress,
isValidDateString,
isMacAddress,
isNumber,
}; };

View File

@ -1,4 +1,4 @@
const { checkAPikey } = require("../functions/database.js"); const { checkAPikey } = require("../functions/api.js");
async function apikeyCheck(req, res, next) { async function apikeyCheck(req, res, next) {
//const authHeader = req.headers.authorization //const authHeader = req.headers.authorization
try { try {

View File

@ -1,4 +1,4 @@
const { insertLogData } = require("../functions/database.js"); const { insertLogData } = require("../functions/logger.js");
const APIlogger = (req, res, next) => { const APIlogger = (req, res, next) => {
try { try {
const log = { const log = {

View File

@ -1,6 +1,6 @@
const { apikeyModel } = require("../database/model/apiKeyModel"); const { apikeyModel } = require("../database/model/apiKeyModel");
const { userModel } = require("../database/model/userModel"); const { userModel } = require("../database/model/userModel");
const { comparePassword } = require("../functions/bcrypt"); const { compareHash } = require("../functions/bcrypt");
async function auth(req, res, next){ async function auth(req, res, next){
try{ try{
@ -15,7 +15,7 @@ async function auth(req, res, next){
if (!token) return false; if (!token) return false;
//compare //compare
let isMatch = await comparePassword(suppliedToken, token.apikey); let isMatch = await compareHash(suppliedToken, token.apikey);
if (!isMatch) return false; if (!isMatch) return false;
//else do logic //else do logic

View File

@ -10,13 +10,11 @@ const options = {
username: process.env.MQTT_USER, username: process.env.MQTT_USER,
password: process.env.MQTT_PASS, password: process.env.MQTT_PASS,
protocol: 'mqtts', // Use MQTT over TLS protocol: 'mqtts', // Use MQTT over TLS
key: fs.readFileSync(path.resolve(__dirname, '../../cert/privkey.pem')), key: fs.readFileSync(path.resolve(__dirname, '../cert/privkey.pem')),
cert: fs.readFileSync(path.resolve(__dirname, '../../cert/cert.pem')), cert: fs.readFileSync(path.resolve(__dirname, '../cert/cert.pem')),
}; };
const client = mqtt.connect(brokerUrl, options); const client = mqtt.connect(brokerUrl, options);
module.exports = client; module.exports = client;

View File

@ -1,7 +1,6 @@
const { app } = require("./modules/express.js"); const client = require("./modules/mqtt.js");
const client = require("./modules/mqtt");
const { isJson, isNumber } = require("./functions/validateData.js"); const { isJson, isNumber } = require("./functions/validateData.js");
const { insertDatatoDB } = require("./functions/database.js"); const { addSensorData } = require("./functions/sensorData");
// Event handlers // Event handlers
client.on("connect", () => { client.on("connect", () => {
@ -38,7 +37,7 @@ client.on("message", (topic, message) => {
if (isNumber(data)) { if (isNumber(data)) {
{ {
//pass datas to database //pass datas to database
insertDatatoDB(datas[key]); addSensorData(datas[key].sensorid, datas[key].locationid, datas[key]);
} }
} else { } else {
@ -64,3 +63,4 @@ client.on("end", () => {
console.log("Disconnected from MQTT broker"); console.log("Disconnected from MQTT broker");
client.reconnect(); client.reconnect();
}); });

View File

@ -144,6 +144,34 @@ app.api = (function (app) {
return { post: post, get: get, put: put, delete: remove }; return { post: post, get: get, put: put, delete: remove };
})(app); })(app);
//socket.io
//socket.io
app.socket = (function (app) {
//need to replace with domain name of server when published
var socket = io();
socket.on("disconnect", () => {
console.log("disconnected");
});
socket.on('connect', ()=>{
console.info('WS connected');
})
socket.io.on("reconnect", () => {
console.log("reconnected");
});
socket.io.on("connect_error", (err) => {
console.log(err);
});
return socket;
})(app);
//sensor data
app.sensordata = (function (app) {
})(app);
app.auth = (function (app) { app.auth = (function (app) {
var user = {}; var user = {};
function setToken(token) { function setToken(token) {
@ -154,18 +182,20 @@ app.auth = (function (app) {
return localStorage.getItem("APIToken"); return localStorage.getItem("APIToken");
} }
function isLoggedIn(callback) { function isLoggedIn(callback) {
if (getToken()) { if (getToken()) {
return app.api.get("user/me", function (error, data) { return app.api.get("user/me", function (error, data) {
if (!error) app.auth.user = data; if (!error) app.auth.user = data;
//for navbar to show username //for navbar to show username
if (!location.pathname === "/login")
{
$.scope.getUsername.update(data); $.scope.getUsername.update(data);
}
//for edit profile to show user details //for edit profile to show user details
//if not in edit profile page, it will not show //if not in edit profile page, it will not show
if (location.pathname === "/profile") if (location.pathname === "/profile") {
{
$.scope.getUserDetails.update(data); $.scope.getUserDetails.update(data);
} }
return callback(error, data); return callback(error, data);
@ -232,12 +262,9 @@ app.auth = (function (app) {
logInRedirect, logInRedirect,
homeRedirect, homeRedirect,
profileRedirect, profileRedirect,
//showUser,
//redirectIfLoggedIn,
}; };
})(app); })(app);
app.user = (function (app) { app.user = (function (app) {
//delete profile //delete profile
function deleteProfile() { function deleteProfile() {
@ -250,12 +277,10 @@ app.user = (function (app) {
}); });
} }
}); });
} }
return { return {
deleteProfile, deleteProfile,
}; };
})(app); })(app);
//ajax form submit and pass to api //ajax form submit and pass to api

View File

@ -1,6 +1,34 @@
'use strict'; 'use strict';
const router = require('express').Router(); const router = require('express').Router();
const { auth } = require("../middleware/authChecker") const { auth } = require("../middleware/authChecker")
const { APIlogger } = require('../middleware/apiLogger.js');
const { apikeyCheck } = require('../middleware/apiKey.js');
router.use('/auth', require('./auth'));
router.use('/apikey', require('./apikey'));
router.use('/user', [auth, APIlogger], require('./user'));
//TO REFACTOR INTO ONE MIDDLWARE
//location route
router.use('/location', [apikeyCheck , APIlogger], require('./location.js'));
//location route
router.use('/sensor', [apikeyCheck , APIlogger], require('./sensor.js'));
//location route
router.use('/sensor-data', [apikeyCheck, APIlogger], require('./sensorData.js'));
module.exports = router;
/*
'use strict';
const router = require('express').Router();
const { auth } = require("../middleware/authChecker")
router.use('/auth', require('./auth')); router.use('/auth', require('./auth'));
@ -12,3 +40,4 @@ router.use('/user', auth ,require('./user'));
module.exports = router; module.exports = router;
*/

View File

@ -1,19 +1,9 @@
const { getAPIKey , addAPIKey } = require("../functions/apiDatabase.js"); const { addAPIKey } = require("../functions/api");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
router.get("/", async (req, res, next) => {
try {
const location = await getAPIKey();
res.status(200).json(location);
} catch (error) {
console.error(error);
next(error);
}
});
/* /*
1) ensure user is logged in (frontend session validation blah or wtv) 1) ensure user is logged in (frontend session validation blah or wtv)
2) when user click on generate api key button, it will generate a random api key. how to get userid can be done by session or wtv 2) when user click on generate api key button, it will generate a random api key. how to get userid can be done by session or wtv
@ -34,9 +24,6 @@ router.post("/new", async (req, res, next) => {
} }
}); });
//update
//delete
//getbyid
module.exports = router; module.exports = router;

View File

@ -1,4 +1,4 @@
const { addUser, loginUser } = require("../functions/apiDatabase.js"); const { addUser, loginUser } = require("../functions/user");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();

View File

@ -4,7 +4,7 @@ const {
getLocationById, getLocationById,
updateLocation, updateLocation,
deleteLocation, deleteLocation,
} = require("../functions/apiDatabase.js"); } = require("../functions/location");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
@ -25,6 +25,7 @@ router.get("/", async (req, res, next) => {
//add location //add location
router.post("/new", async (req, res, next) => { router.post("/new", async (req, res, next) => {
try { try {
console.log(req.body);
const { name, added_by, description } = req.body; const { name, added_by, description } = req.body;
await addLocation(name, added_by, description); await addLocation(name, added_by, description);
res.sendStatus(200) res.sendStatus(200)

View File

@ -92,5 +92,9 @@ router.get("/api", function (req, res, next) {
res.render("api"); res.render("api");
}); });
// sensor data
router.get("/sensor-data", function (req, res, next) {
res.render("sensor-data");
});
module.exports = router; module.exports = router;

View File

@ -1,6 +1,6 @@
const { sequelize } = require("../Database/mySql.js"); const { sequelize } = require("../database/mySQL.js");
const { locationModel } = require("../Database/model/locationModel.js"); const { locationModel } = require("../database/model/locationModel.js");
const { sensorModel } = require("../Database/model/sensorModel.js"); const { sensorModel } = require("../database/model/sensorModel.js");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();

View File

@ -0,0 +1,14 @@
'use strict';
const router = require('express').Router();
//location route
router.use('/seedSensorData', require('./seedsensorData.js'));
router.use('/seed', require('./seedLocationAndSensor.js'));
module.exports = router;

View File

@ -1,7 +1,7 @@
const { sequelize } = require("../Database/mySql.js"); const { sequelize } = require("../database/mySQL.js");
const { locationModel } = require("../Database/model/locationModel.js"); const { locationModel } = require("../database/model/locationModel.js");
const { sensorModel } = require("../Database/model/sensorModel.js"); const { sensorModel } = require("../database/model/sensorModel.js");
const { sensorDataModel } = require("../Database/model/sensorDataModel.js"); const { sensorDataModel } = require("../database/model/sensorDataModel.js");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();

View File

@ -4,7 +4,7 @@ const {
updateSensor, updateSensor,
deleteSensor, deleteSensor,
getSensorById getSensorById
} = require("../functions/apiDatabase.js"); } = require("../functions/sensor.js");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();

View File

@ -6,10 +6,9 @@ const {
getSensorDataById, getSensorDataById,
getData, getData,
getDatabyRange, getDatabyRange,
} = require("../functions/apiDatabase.js"); } = require("../functions/sensorData");
const express = require("express"); const express = require("express");
const { json } = require("body-parser");
const router = express.Router(); const router = express.Router();
router.get("/", async (req, res, next) => { router.get("/", async (req, res, next) => {
@ -24,9 +23,9 @@ router.get("/", async (req, res, next) => {
router.post("/new", async (req, res, next) => { router.post("/new", async (req, res, next) => {
try { try {
const { id, id_sensor, id_location, sensordata } = req.body; const { id_sensor, id_location, sensordata } = req.body;
await addSensorData(id, id_sensor, id_location, sensordata); let data = await addSensorData(id_sensor, id_location, sensordata);
res.sendStatus(200).json({ message: "SensorData " + id + " added" }); res.json({ message: "SensorData " + data.id + " added", ...data });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
next(error); next(error);
@ -91,4 +90,3 @@ router.get("/:id", async (req, res, next) => {
}); });
module.exports = router; module.exports = router;

View File

@ -1,4 +1,4 @@
const { getUserID, updateProfile } = require("../functions/apiDatabase.js"); const { getUserID, updateProfile } = require("../functions/user");
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();

View File

@ -1,5 +1,12 @@
<%- include('top') %> <%- include('top') %>
<script>
//call socket.io
app.socket.on("sensorData:new", function (data) {
console.log("new data!!")
console.log(data);
});
</script>
<header class="slider-main"> <header class="slider-main">
@ -47,36 +54,47 @@
<!-- Page Content --> <!-- Page Content -->
<div class="container"> <div class="container">
<div class="services-bar"> <div class="services-bar">
<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-4 mb-4"> <div class="col-lg-3 mb-4">
<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 class="col-lg-4 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">15 - 18 PSI</p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a> <a href="/learnmore" class="btn btn-primary">Learn More</a>
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-4 mb-4"> <div class="col-lg-3 mb-4">
<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 class="col-lg-3 mb-4">
<div class="card"> <div class="card">
<h4 class="card-header">Temperature</h4> <h4 class="card-header">Temperature</h4>
<div class="card-body text-center"> <div class="card-body text-center">
<p class="card-text display-4"> 30&deg; - 37&deg; </p> <p class="card-text display-4">30&deg; - 37&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">Another Category</h4>
<div class="card-body text-center">
<p class="card-text display-4">values</p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a> <a href="/learnmore" class="btn btn-primary">Learn More</a>
@ -84,6 +102,7 @@
</div> </div>
</div> </div>
</div> </div>
<!-- /.row --> <!-- /.row -->
</div> </div>
<!-- About Section --> <!-- About Section -->
@ -121,4 +140,4 @@
</div> </div>
<hr> <hr>
</div> </div>
<%- include('bot') %> <%- include('bot') %>

View File

@ -26,6 +26,8 @@
<!-- Mustache JS --> <!-- Mustache JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.1/mustache.js"></script>
<!-- socket.io scriot -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script>
<!-- jquery app.js --> <!-- jquery app.js -->
<script src="js/app.js"></script> <script src="js/app.js"></script>

View File

@ -0,0 +1,41 @@
<%- include('top') %>
<style>
#sensorDataList{
padding-top: 2.5em;
}
</style>
<ul id="sensorDataList">
<li jq-repeat='sensorData'>
rowid: {{ id }}
sensorId: {{ sensorid }}
created: {{ createdAt }}
location: {{ locationid }}
<br/ >
co: {{ measurement.co }}
humidity: {{ measurement.humidity }}
no2: {{ measurement.no2 }}
o3: {{ measurement.o3 }}
psi: {{ measurement.psi }}
so2: {{ measurement.so2 }}
temperature: {{ measurement.temperature }}
windspeed: {{ measurement.windspeed }}
<hr />
</li>
<li jq-repeat-defualt='sensorData'>
Loading...
</li>
</ul>
<script type="text/javascript">
$(document).ready(async function(){
$.scope.sensorData.push(...await app.api.get('sensor-data/data?order=DESC&limit=40'));
app.socket.on('sensordata:new', function(data){
$.scope.sensorData.unshift(data);
});
})
</script>
<%- include('bot') %>

View File

@ -1,10 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" /> <meta name="description" content="" />
<meta name="author" content="" /> <meta name="author" content="" />
<meta http-equiv="cleartype" content="on" /> <meta http-equiv="cleartype" content="on" />
@ -14,11 +13,8 @@
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous" />
<!-- Custom styles for this template --> <!-- Custom styles for this template -->
<link href="css/all.css" rel="stylesheet" /> <link href="css/all.css" rel="stylesheet" />
@ -33,10 +29,8 @@
<script src="https://sso.theta42.com/static/js/mustache.min.js"></script> <script src="https://sso.theta42.com/static/js/mustache.min.js"></script>
<!-- jQuery library --> <!-- jQuery library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- Bootstrap 5 JavaScript --> <!-- Bootstrap 5 JavaScript -->
<script <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
@ -46,21 +40,25 @@
<script> <script>
hljs.initHighlightingOnLoad(); hljs.initHighlightingOnLoad();
</script> </script>
<!-- socket.io scriot -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script>
<!-- jq-repeat --> <!-- jq-repeat -->
<script src="js/jq-repeat.js"></script> <script src="js/jq-repeat.js"></script>
<!-- jquery app.js --> <!-- jquery app.js -->
<script src="js/app.js"></script> <script src="js/app.js"></script>
</head> </head>
<!-- javascript function to check if user is auth --> <!-- javascript function to check if user is auth -->
<script> <script>
//make document ready //make document ready
$(document).ready(function () { $(document).ready(function () {
//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 (data) { if (data) {
$("#cl-logout-button").show("fast"); $("#cl-logout-button").show("fast");
$("#cl-api-button").show("fast");
$("#cl-profile-button").show("fast"); $("#cl-profile-button").show("fast");
$("#cl-login-button").hide("fast"); $("#cl-login-button").hide("fast");
} else { } else {
@ -70,23 +68,16 @@
}); });
}); });
</script> </script>
<body> <body>
<nav <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container"> <div class="container">
<a class="navbar-brand" href="/"> <a class="navbar-brand" href="/">
<img src="images/logo.png" alt="logo" /> <img src="images/logo.png" alt="logo" />
</a> </a>
<button <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
class="navbar-toggler" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
type="button"
data-toggle="collapse"
data-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false"
aria-label="Toggle navigation">
<span class="fas fa-bars"></span> <span class="fas fa-bars"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarResponsive"> <div class="collapse navbar-collapse" id="navbarResponsive">
@ -103,36 +94,33 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/contact">Contact</a> <a class="nav-link" href="/contact">Contact</a>
</li> </li>
<!--
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/api">API Doc</a> <a class="nav-link" href="/api">API Doc</a>
</li> </li>
-->
<!-- profile button --> <!-- profile button -->
<div class="form-inline mt-2 mt-md-0"> <div class="form-inline mt-2 mt-md-0">
<a id="cl-api-button" class="btn btn-outline-info btn-sm my-2 my-sm-0" href="/api"
style="display: none">
<i class="fas fa-sign-out"></i> API
</a>
<!-- Profile Button --> <!-- Profile Button -->
<a <a id="cl-profile-button" class="btn btn-outline-info btn-sm my-2 my-sm-0" href="/profile"
id="cl-profile-button"
class="btn btn-outline-info btn-sm my-2 my-sm-0"
href="/profile"
style="display: none"> style="display: none">
<i class="fas fa-sign-out"></i> Profile <i class="fas fa-sign-out"></i> Profile
</a> </a>
<!-- Login Button --> <!-- Login Button -->
<a <a id="cl-login-button" class="btn btn-outline-danger btn-sm my-2 my-sm-0"
id="cl-login-button" onclick="app.auth.forceLogin()" style="display: none">
class="btn btn-outline-danger btn-sm my-2 my-sm-0"
onclick="app.auth.forceLogin()"
style="display: none">
<i class="fas fa-sign-out"></i> Login <i class="fas fa-sign-out"></i> Login
</a> </a>
<!-- Logout Button --> <!-- Logout Button -->
<button <button id="cl-logout-button" class="btn btn-outline-danger btn-sm my-2 my-sm-0" href="/"
id="cl-logout-button" onclick="app.auth.logOut(e => window.location.href='/')" style="display: none">
class="btn btn-outline-danger btn-sm my-2 my-sm-0"
href="/"
onclick="app.auth.logOut(e => window.location.href='/')"
style="display: none">
<i class="fas fa-sign-out"></i> Logout <i class="fas fa-sign-out"></i> Logout
</button> </button>
</div> </div>
@ -140,5 +128,6 @@
</div> </div>
</div> </div>
</nav> </nav>
</body> </body>
</html> </html>

56
package-lock.json generated
View File

@ -12,6 +12,7 @@
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"csurf": "^1.11.0", "csurf": "^1.11.0",
"date-fns": "^3.2.0", "date-fns": "^3.2.0",
"date-fns-tz": "^2.0.0", "date-fns-tz": "^2.0.0",
@ -35,6 +36,7 @@
"sequelize": "^6.35.2", "sequelize": "^6.35.2",
"sequelize-cli": "^6.6.2", "sequelize-cli": "^6.6.2",
"socket.io": "^4.7.4", "socket.io": "^4.7.4",
"socket.io-client": "^4.7.4",
"validator": "^13.11.0" "validator": "^13.11.0"
}, },
"devDependencies": { "devDependencies": {
@ -1227,6 +1229,38 @@
"node": ">=10.2.0" "node": ">=10.2.0"
} }
}, },
"node_modules/engine.io-client": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.11.0",
"xmlhttprequest-ssl": "~2.0.0"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": { "node_modules/engine.io-parser": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
@ -3606,6 +3640,20 @@
} }
} }
}, },
"node_modules/socket.io-client": {
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz",
"integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.5.2",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": { "node_modules/socket.io-parser": {
"version": "4.2.4", "version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
@ -4091,6 +4139,14 @@
} }
} }
}, },
"node_modules/xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",

View File

@ -20,6 +20,7 @@
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"csurf": "^1.11.0", "csurf": "^1.11.0",
"date-fns": "^3.2.0", "date-fns": "^3.2.0",
"date-fns-tz": "^2.0.0", "date-fns-tz": "^2.0.0",
@ -43,6 +44,7 @@
"sequelize": "^6.35.2", "sequelize": "^6.35.2",
"sequelize-cli": "^6.6.2", "sequelize-cli": "^6.6.2",
"socket.io": "^4.7.4", "socket.io": "^4.7.4",
"socket.io-client": "^4.7.4",
"validator": "^13.11.0" "validator": "^13.11.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,65 +0,0 @@
"use strict";
const { Sequelize, DataTypes } = require("sequelize");
const { sequelize } = require("../mySQL");
const { userModel } = require("./userModel");
//sequelize.sync();
const apikeyModel = sequelize.define(
"apikey",
{
id: {
type: DataTypes.INTEGER,
allowNull: true,
primaryKey: true,
autoIncrement: true,
validate: {
isNumeric: true,
},
},
userid: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
isNumeric: true,
},
//fk
references: {
model: userModel,
key: "id",
},
},
apikey: {
type: DataTypes.STRING,
allowNull: false,
length: 255,
unique: true,
validate: {
notEmpty: true,
len: [1, 255],
},
},
permission: {
type: DataTypes.STRING,
allowNull: false,
length: 255,
validate: {
notEmpty: true,
len: [1, 255],
isIn: [["canRead", "canWrite" , "auto-generated"]],
},
},
createdAt: {
type: DataTypes.DATE,
allowNull: true,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: true,
},
},
{
timestamps: true,
}
);
module.exports = { apikeyModel };

View File

@ -1,94 +0,0 @@
"use strict";
const { Sequelize, DataTypes } = require("sequelize");
const { sequelize } = require("../mySQL");
const {
isAlphaNumericWithSpacesAndDash,
isAddress,
} = require("../../functions/validateData");
//sequelize.sync();
const userModel = sequelize.define(
"user",
{
id: {
type: DataTypes.INTEGER,
allowNull: true,
primaryKey: true,
autoIncrement: true,
validate: {
isNumeric: true,
},
},
username: {
type: DataTypes.STRING,
allowNull: false,
length: 60,
validate: {
notEmpty: true,
len: [1, 60],
isAlphaNumericWithSpacesAndDash(value) {
if (!isAlphaNumericWithSpacesAndDash(value)) {
throw new Error("Invalid characters in username");
}
},
},
},
password: {
type: DataTypes.STRING,
allowNull: false,
length: 255,
validate: {
notEmpty: true,
len: [1, 255],
},
},
email: {
type: DataTypes.STRING,
allowNull: false,
length: 60,
unique: true,
validate: {
notEmpty: true,
len: [1, 60],
isEmail: true,
},
},
address: {
type: DataTypes.STRING,
allowNull: true,
length: 255,
validate: {
notEmpty: true,
len: [1, 255],
isAddress(value) {
if (!isAddress(value)) {
throw new Error("Invalid address");
}
},
},
},
phone: {
type: DataTypes.STRING,
allowNull: true,
length: 20,
validate: {
notEmpty: true,
len: [1, 20],
isNumeric: true,
},
},
//utc time
createdAt: {
type: DataTypes.DATE,
allowNull: true,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: true,
},
},
{
timestamps: true,
}
);
module.exports = { userModel };

View File

@ -1,35 +0,0 @@
const dotenv = require("dotenv");
const path = require('path')
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') })
const Sequelize = require("sequelize");
const fs = require('fs');
const { escape } = require("querystring");
const sequelize = new Sequelize(
"eco_saver",
process.env.DB_USER,
process.env.DB_PASS,
{
host: "mpsqldatabase.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')),
},
},
},
);
sequelize.authenticate().then(() => {
console.log('Connection has been established successfully.');
}).catch((error) => {
console.error('Unable to connect to the database: ', error);
});
module.exports = { sequelize };

View File

@ -1,55 +0,0 @@
const { sequelize } = require("../Database/mySql.js");
const { api_log_Model } = require("../Database/model/apiLogModel.js");
const { sensorDataModel } = require("../Database/model/sensorDataModel.js");
const { apikeyModel } = require("../Database/model/apiKeyModel.js");
const { compareAPIKey } = require("./bcrypt.js");
async function insertLogData(log) {
try {
api_log_Model.create({
ip: log.ip,
time: log.time,
method: log.method,
host: log.host,
statusCode: log.statusCode,
Responsesize: log.Responsesize,
referrer: log.referrer,
userAgent: log.userAgent,
});
} catch (error) {
console.error(error);
}
}
async function insertDatatoDB(data) {
try {
sensorDataModel.create({
sensorid: data.sensorid,
locationid: data.locationid,
measurement: data.measurement,
});
} catch (error) {
console.error(error);
}
}
async function checkAPikey(SuppliedKey, rowid) {
try {
const retrivedKey = await apikeyModel.findOne({
raw: true,
attributes: ["apikey" , "permission"],
where: {
userid: rowid,
},
});
//console.log(retrivedKey.apikey);
if (compareAPIKey(SuppliedKey, retrivedKey.apikey)) {
//return true;
return retrivedKey.permission;
}
} catch (error) {
console.error(error);
}
}
module.exports = { insertLogData, insertDatatoDB, checkAPikey };

View File

@ -1,38 +0,0 @@
const bcrypt = require('bcrypt');
const saltRounds = 10;
//https://github.com/kelektiv/node.bcrypt.js#readme
/*
// Load hash from your password DB.
bcrypt.compare(myPlaintextPassword, hash, function(err, result) {
// result == true
});
bcrypt.compare(someOtherPlaintextPassword, hash, function(err, result) {
// result == false
});
*/
/*
//hash with salt
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB.
});
*/
async function hashAPIKey(apikey) {
return await bcrypt.hash(apikey, saltRounds);
}
async function compareAPIKey(apikey, hash) {
return await bcrypt.compare(apikey, hash);
}
module.exports = {
hashAPIKey,
compareAPIKey
};

View File

@ -1,17 +0,0 @@
//model for getting API key from database
async function getAPIKey() {
}
module.exports = { getAPIKey }

View File

@ -1,51 +0,0 @@
var validator = require("validator");
const dateRegex = /^[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/;
function isValidDateString(value) {
return dateRegex.test(value);
}
function isAlphaNumericwithSpaces(value) {
return validator.isAlphanumeric(value, ["en-US"], { ignore: " " });
}
//allow alphanumeric and spaces and -
function isAlphaNumericWithSpacesAndDash(value) {
const alphanumeric = /^[a-zA-Z0-9]+$/;
const valid = value
.split("")
.every((char) => alphanumeric.test(char) || char === " " || char === "-");
return valid;
}
function isMacAddress(value) {
// Joi.string().regex(/^([0-9a-f]{2}-){5}([0-9a-f]{2})$/i).lowercase()
//return validator.isMACAddress(value, { no_separators: true, eui: 48 });
const macAddress = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
const valid = macAddress.test(value);
return valid;
}
function isJson(value) {
//check if its object
if(typeof value === "object"){
return true
}
}
function isNumber(value) {
if (typeof value === "number") {
return true;
}
}
module.exports = {
isValidDateString,
isAlphaNumericwithSpaces,
isAlphaNumericWithSpacesAndDash,
isMacAddress,
isJson,
isNumber,
};

View File

@ -1,80 +0,0 @@
const express = require("express");
const helmet = require("helmet");
const { rateLimit } = require("express-rate-limit");
const { APIlogger } = require('../middleware/apiLogger.js');
const { apikeyCheck } = require('../middleware/apiKey.js');
const app = express();
app.use(helmet());
const port = 80;
//express-rate-limit stolen from docs
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 600, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
})
// Apply the rate limiting middleware to all requests.
app.use(limiter)
//disable x-powered-by header for security reasons
app.disable("x-powered-by");
//parse json body format
app.use(express.json());
app.set("json spaces", 2);
/*
middleware logic ( called by next() )
*/
app.use("/api/seed/v0", [ apikeyCheck , APIlogger] ,require("../routes/seed_route.js"));
app.use('/api/v0', [apikeyCheck, APIlogger] ,require("../routes/api_route.js"));
// 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) {
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.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(err.status || 500);
console.log(keyErrors);
res.json({
name: err.name,
message: err.message,
keyErrors,
});
});
app.listen(port, () => {
console.log(`app listening on port ${port}`);
});
module.exports = { app };

View File

@ -1,77 +0,0 @@
const {
} = require("../functions/apiDatabase.js");
const express = require("express");
const router = express.Router();
module.exports = router;
/*
//get location
router.get("/", async (req, res, next) => {
try {
const location = await getLocation();
//res send json and status code
res.status(200).json(location);
} catch (error) {
console.error(error);
next(error);
}
});
//add location
router.post("/new", async (req, res, next) => {
try {
const { name, added_by, description } = req.body;
await addLocation(name, added_by, description);
res.sendStatus(200)
} catch (error) {
console.error(error);
next(error);
}
});
//update location
router.put("/update", async (req, res, next) => {
try {
const { id, name, added_by, description } = req.body;
await updateLocation(id, name, added_by, description);
res.status(200).json({ message: "Location " + id + " updated" });
} catch (error) {
console.error(error);
next(error);
}
});
//delete location
router.delete("/delete", async (req, res, next) => {
try {
const { id } = req.body;
await deleteLocation(id);
res.status(200).json({ message: "Location " + id + " deleted" });
} catch (error) {
console.error(error);
next(error);
}
});
//get location by id
router.get("/:id", async (req, res, next) => {
try {
//get params
const { id } = req.params;
const location = await getLocationById(id);
res.status(200).json(location);
} catch (error) {
console.error(error);
next(error);
}
});
*/

View File

@ -1,37 +0,0 @@
/*
'use strict';
const router = require('express').Router();
const middleware = require('../middleware/auth');
router.use('/runner', require('./runner'));
router.use('/worker', require('./worker'));
router.use('/auth', require('./auth'));
router.use('/user', middleware.auth, require('./user'));
router.use('/token',middleware.auth, require('./token'));
module.exports = router;
*/
'use strict';
const router = require('express').Router();
//location route
router.use('/location', require('./location'));
//sensor route
router.use('/sensor', require('./sensor'))
//sensor data route
router.use('/sensor-data', require('./sensorData'));
//log route
//router.use('/log', require('./logLog'));
module.exports = router;

View File

@ -1,14 +0,0 @@
'use strict';
const router = require('express').Router();
//location route
router.use('/seedSensorData', require('./seedSensorData.js'));
router.use('/seed', require('./seedLocationAndSensor'));
module.exports = router;