Merge branch 'main' into sensor

This commit is contained in:
BIG2EYEZ 2024-01-24 16:38:43 +08:00
commit 92b0f86be6
70 changed files with 1231 additions and 1337 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);

View File

@ -13,3 +13,10 @@ i repeat DO NOT USE CREDS IN CODE! Please use .env files (https://www.npmjs.com/
* consumer website api and user function * consumer website api and user function
2) Sean 2) Sean
* Admin Website Microservice * 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

4
api.MD
View File

@ -169,9 +169,7 @@ http://localhost/api/v0/sensor-data/filter?windspeed=highest&limit=1
http://localhost/api/v0/sensor-data/data?week=1&sensorid=1&locationid=1&page=2&pagesize=10 http://localhost/api/v0/sensor-data/data?week=1&sensorid=1&locationid=1&page=2&pagesize=10
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,48 @@
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;
process.nextTick(() => require('./mqttApp'));
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 +88,4 @@ app.use(function (err, req, res, next) {
keyErrors, keyErrors,
}); });
}); });
app.listen(port, () => {
console.log(`app listening on port ${port}`);
});
module.exports = { app };

101
consumerWebsite/bin/www Normal file
View File

@ -0,0 +1,101 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
const app = require('../app');
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

@ -1,138 +0,0 @@
const { sequelize } = require("../database/mySql.js");
const { apikeyModel } = require("../database/model/apikeyModel.js");
const { userModel } = require("../database/model/userModel.js");
const { Op, Sequelize } = require("sequelize");
const { generateUUID } = require("../functions/generateUUID.js");
const {
hashPassword,
comparePassword,
hashAPIKey,
} = require("../functions/bcrypt.js");
//getuser
//api/v0/user/me
async function getUserID(userid) {
//console.log(userid);
//console.log(userid.id);
let userRes = await userModel.findByPk(userid.id, {
attributes: {
exclude: ["password"],
},
});
if (!userRes) return false;
return userRes;
}
//api/v0/auth/register
/* Registering new user
1) req.body is taken from html form or wtv
2) bcrpyt and hash the password on the server side
3) pass to db
*/
async function addUser(user) {
//hash password
let hash = await hashPassword(user.password);
const addRes = await userModel.create({
firstname: user.firstname,
lastname: user.lastname,
username: user.username,
password: hash,
email: user.email,
address: user.address,
phone: user.phone,
});
if (addRes) {
return true;
} else {
return false;
}
}
//api/v0/auth/login
async function loginUser(user) {
//console.log(user);
//look up username or email in db
const userRes = await userModel.findOne({
where: {
[Op.or]: [
{
username: user.username,
},
{
email: user.username,
},
],
},
});
// Make sure user exists
if (!userRes) return false;
// Compare passwords
let match = await comparePassword(user.password, userRes.password);
if (!match) return false;
//console.log('loginUser', userRes.id, userRes.username);
//generate token
let token = await addAPIKey(userRes.id, "auto-generated");
return { token: token, userid: userRes.id, username: userRes.username };
}
/*
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 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/logout
async function deleteUserToken(token) {
//get row id
let splitAuthToken = token.split("-");
let rowid = splitAuthToken[0];
//console.log(rowid);
//delete from db
let delRes = await apikeyModel.destroy({
where: {
id: rowid,
},
});
if (!delRes) return false;
return true;
}
module.exports = {
getUserID,
addUser,
loginUser,
addAPIKey,
deleteUserToken,
};

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,55 @@
const {locationModel} = require("../database/model/locationModel");
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) {
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,74 @@
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({
//cascade delete
onDelete: "cascade",
where: {
id: id,
},
});
}
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

@ -0,0 +1,137 @@
const { Op } = require('sequelize')
const { hash, compareHash } = require("./bcrypt.js");
const { addAPIKey } = require("./api");
const { userModel } = require("../database/model/userModel");
//getuser
//api/v0/user/me
async function getUserID(userid) {
//console.log(userid);
//console.log(userid.id);
let userRes = await userModel.findByPk(userid.id, {
attributes: {
exclude: ["password"],
},
});
if (!userRes) return false;
return userRes;
}
//api/v0/auth/register
/* Registering new user
1) req.body is taken from html form or wtv
2) bcrpyt and hash the password on the server side
3) pass to db
*/
async function addUser(user) {
//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/login
async function loginUser(user) {
//console.log(user);
//look up username or email in db
const userRes = await userModel.findOne({
where: {
[Op.or]: [
{
username: user.username,
},
{
email: user.username,
},
],
},
});
// Make sure user exists
if (!userRes) return false;
// Compare passwords
let match = await compareHash(user.password, userRes.password);
if (!match) return false;
//console.log('loginUser', userRes.id, userRes.username);
//generate token
let token = await addAPIKey(userRes.id, "auto-generated");
return { token: token, userid: userRes.id, username: userRes.username };
}
/*
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
*/
//api/v0/user/update
async function updateProfile(user, body) {
if (!body.password) {
let updateUser = await userModel.update(
{
firstname: body.first_name,
lastname: body.last_name,
username: body.username,
email: body.email,
address: body.address,
phone: body.phone,
},
{
where: {
id: user.id,
},
}
);
if (!updateUser) return false;
return true;
} else {
let hashed = await hash(body.password);
let updateUser = await userModel.update(
{
firstname: body.first_name,
lastname: body.last_name,
username: body.username,
email: body.email,
address: body.address,
phone: body.phone,
password: hashed,
},
{
where: {
id: user.id,
},
}
);
if (!updateUser) return false;
return true;
}
}
module.exports = {
getUserID,
addUser,
loginUser,
updateProfile,
};

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{
@ -12,15 +12,16 @@ async function auth(req, res, next){
//get from db //get from db
let token = await apikeyModel.findByPk(rowid, {include: userModel}); let token = await apikeyModel.findByPk(rowid, {include: userModel});
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
//pass hashed token to req.token (IMPORTANT ITS NOT PASSED TO CLIENT) //pass hashed token to req.token (IMPORTANT ITS NOT PASSED TO CLIENT)
req.token = token req.token = token
req.user = await token.getUser(); req.user = await token.getUser(); //taking user seq obj from usermodel
next(); next();
}catch(error){ }catch(error){
next(error); next(error);

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

@ -3813,3 +3813,12 @@
.card-text { .card-text {
color: #000000; color: #000000;
} }
/* edit profile */
.profile {
margin: auto;
width: 40%;
border: 1px solid #000000;
padding: 10px;
}

View File

@ -524,3 +524,26 @@ body.one-content-column-version .content thead {
.cf:before, .cf:after { content: ""; display: block; } .cf:before, .cf:after { content: ""; display: block; }
.cf:after { clear: both; } .cf:after { clear: both; }
.ie6 .cf { zoom: 1 } .ie6 .cf { zoom: 1 }
.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 {
background-color: #4caf50; /* Green background color */
color: white; /* White text color */
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 */
}
#content-get-api .generate-key-button:hover {
background-color: #45a049; /* Darker green on hover */
}

View File

@ -4,7 +4,7 @@
form { form {
background-color: #fff; background-color: #fff;
padding: 20px; padding: 25px;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 66%; width: 66%;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

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,12 +182,22 @@ 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;
//$.scope.getUsername.push(data); //for navbar to show username
if (!location.pathname === "/login")
{
$.scope.getUsername.update(data);
}
//for edit profile to show user details
//if not in edit profile page, it will not show
if (location.pathname === "/profile") {
$.scope.getUserDetails.update(data);
}
return callback(error, data); return callback(error, data);
}); });
} else { } else {
@ -167,16 +205,6 @@ app.auth = (function (app) {
} }
} }
function showUser(){
app.api.get("user/me", function (error, data) {
if (!error) app.auth.user = data;
$.scope.getUsername.push(data);
});
}
function logOut(callback) { function logOut(callback) {
//call logout route //call logout route
$.ajax({ $.ajax({
@ -217,19 +245,13 @@ app.auth = (function (app) {
} }
function homeRedirect() { function homeRedirect() {
window.location.href = location.href.replace(location.replace(`/`)) || "/"; //window.location.href = location.href.replace(location.replace(`/`)) || "/";
location.replace(`/`);
} }
/*
function redirectIfLoggedIn() {
if (getToken()){
homeRedirect();
}
logInRedirect();
function profileRedirect() {
location.replace(`/profile`);
} }
*/
return { return {
getToken: getToken, getToken: getToken,
@ -239,8 +261,25 @@ app.auth = (function (app) {
forceLogin, forceLogin,
logInRedirect, logInRedirect,
homeRedirect, homeRedirect,
showUser, profileRedirect,
//redirectIfLoggedIn, };
})(app);
app.user = (function (app) {
//delete profile
function deleteProfile() {
app.api.delete("user/delete", function (error, data) {
if (error) {
app.util.actionMessage(error.message, $("#deleteProfile"), "danger");
} else {
app.auth.logOut(function () {
location.replace(`/login`);
});
}
});
}
return {
deleteProfile,
}; };
})(app); })(app);
@ -251,11 +290,6 @@ function formAJAX(btn, del) {
var formData = $form.find("[name]").serializeObject(); // builds query formDataing var formData = $form.find("[name]").serializeObject(); // builds query formDataing
var method = $form.attr("method") || "post"; var method = $form.attr("method") || "post";
// if( !$form.validate()) {
// app.util.actionMessage('Please fix the form errors.', $form, 'danger')
// return false;
// }
app.util.actionMessage("Loading...", $form, "info"); app.util.actionMessage("Loading...", $form, "info");
//console.log('Data being sent to', $form.attr('action'), formData) //console.log('Data being sent to', $form.attr('action'), formData)

View File

@ -1,28 +1,60 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
function updateAdditionalInfo(region) { function updateAdditionalInfo(region) {
const infoContainer = document.getElementById("additional-info"); const infoContainer = document.getElementById("additional-info");
// Replace the following with actual data retrieval based on the region // Replace the following with actual data retrieval based on the region
const aqi = "15"; const aqi = "15";
const temperature = "25°C"; const temperature = "25°C";
const humidity = "60%"; const humidity = "60%";
const pm25 = "10";
const pm10 = "20";
const so2 = "5";
const o3 = "35";
const co = "0.5";
const no2 = "15";
infoContainer.innerHTML = `
<div class="additional-info-box">
<h3>Additional Information - ${region}</h3>
<button id="downloadCsvButton">Download CSV</button>
<div class="info-item">
<span class="info-label">Air Quality Index:</span>
<span class="info-value">${aqi}</span>
</div>
<div class="info-item">
<span class="info-label">Temperature:</span>
<span class="info-value">${temperature}</span>
</div>
<div class="info-item">
<span class="info-label">Humidity:</span>
<span class="info-value">${humidity}</span>
</div>
<div class="info-item">
<span class="info-label">PM2.5:</span>
<span class="info-value">${pm25}</span>
</div>
<div class="info-item">
<span class="info-label">PM10:</span>
<span class="info-value">${pm10}</span>
</div>
<div class="info-item">
<span class="info-label">SO2:</span>
<span class="info-value">${so2}</span>
</div>
<div class="info-item">
<span class="info-label">O3:</span>
<span class="info-value">${o3}</span>
</div>
<div class="info-item">
<span class="info-label">CO:</span>
<span class="info-value">${co}</span>
</div>
<div class="info-item">
<span class="info-label">NO2:</span>
<span class="info-value">${no2}</span>
</div>
</div>
`;
infoContainer.innerHTML = `
<div class="additional-info-box">
<h3>Additional Information - ${region}</h3>
<div class="info-item">
<span class="info-label">Air Quality Index:</span>
<span class="info-value">${aqi}</span>
</div>
<div class="info-item">
<span class="info-label">Temperature:</span>
<span class="info-value">${temperature}</span>
</div>
<div class="info-item">
<span class="info-label">Humidity:</span>
<span class="info-value">${humidity}</span>
</div>
</div>
`;
// Remove the 'active' class from all info-box elements // Remove the 'active' class from all info-box elements
const infoBoxes = document.querySelectorAll('.info-box'); const infoBoxes = document.querySelectorAll('.info-box');

View File

@ -1,3 +1,31 @@
'use strict';
const router = require('express').Router();
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'));
//sensor data route
router.use('/sensor-data', [ APIlogger], require('./sensorData.js'));
module.exports = router;
/*
'use strict'; 'use strict';
const router = require('express').Router(); const router = require('express').Router();
const { auth } = require("../middleware/authChecker") const { auth } = require("../middleware/authChecker")
@ -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();

View File

@ -1,42 +1,3 @@
/*
'use strict';
var router = require('express').Router();
const conf = require('../conf')
const values ={
title: conf.environment !== 'production' ? `<i class="fa-brands fa-dev"></i>` : ''
}
router.get('/', async function(req, res, next) {
res.render('runner', {...values});
});
router.get('/topics', function(req, res, next) {
res.render('topics', {...values});
});
router.get('/chat', function(req, res, next) {
res.render('chat', {...values});
});
router.get('/login*', function(req, res, next) {
res.render('login', {redirect: req.query.redirect, ...values});
});
router.get('/runner', function(req, res, next) {
res.render('runner', {...values});
});
router.get('/worker', function(req, res, next) {
res.render('worker', {...values});
});
module.exports = router;
*/
"use strict"; "use strict";
var router = require("express").Router(); var router = require("express").Router();
@ -66,11 +27,17 @@ router.get("/profile", function (req, res, next) {
res.render("profile"); res.render("profile");
}); });
//forgot password page //forgot password page
router.get("/forgotpassword", function (req, res, next) { router.get("/forgotpassword", function (req, res, next) {
res.render("forgotpassword"); res.render("forgotpassword");
}); });
//resetted password page
router.get("/resetpassword", function (req, res, next) {
res.render("resetpassword");
});
//contact page //contact page
router.get("/contact", function (req, res, next) { router.get("/contact", function (req, res, next) {
res.render("contact"); res.render("contact");
@ -81,6 +48,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 , deleteUserToken } = 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();
@ -6,27 +6,25 @@ const router = express.Router();
//api/v0/user/me //api/v0/user/me
//getbyid //getbyid
router.get("/me", async function (req, res, next) { router.get("/me", async function (req, res, next) {
try { try {
let user = await getUserID(req.user); let user = await getUserID(req.user);
if (!user) { if (!user) {
let error = new Error("User not found"); let error = new Error("User not found");
error.status = 400; error.status = 400;
return next(error); return next(error);
} }
res.json({ res.json({
user: user, user: user,
}); });
} catch (error) {
} catch (error) { next(error);
next(error); }
}
}); });
//logout //logout
router.delete('/logout', async function(req, res, next){ router.delete("/logout", async function (req, res, next) {
try{ try {
/* /*
let authToken = req.header('auth-token'); let authToken = req.header('auth-token');
let userDel = await deleteUserToken(authToken); let userDel = await deleteUserToken(authToken);
if (!userDel) { if (!userDel) {
@ -35,23 +33,51 @@ router.delete('/logout', async function(req, res, next){
return next(error); return next(error);
} }
*/ */
//destroy method call on seq object //destroy method call on seq object
req.token.destroy(); req.token.destroy();
// DO NOT CALL THIS!!! IT WILL DESTROY USERMODEL SEQ OBJ // DO NOT CALL THIS!!! IT WILL DESTROY USERMODEL SEQ OBJ
//req.user.destroy(); //req.user.destroy();
res.json({ res.json({
message: "User logged out successfully", message: "User logged out successfully",
}); });
} catch (error) {
}catch(error){ next(error);
next(error); }
} });
});
//update //update
router.put("/update", async function (req, res, next) {
try {
if (!req.body.password) {
let updateRes = await updateProfile(req.user, req.body);
if (!updateRes) return next(error);
console.log(updateRes);
res.json({
message: "User updated successfully",
});
} else {
let updateRes = await updateProfile(req.user, req.body);
if (!updateRes) return next(error);
res.json({
message: "User updated successfully",
});
}
} catch (error) {
next(error);
}
});
//delete //delete
router.delete("/delete", async function (req, res, next) {
//https://stackoverflow.com/questions/23128816/sequelize-js-ondelete-cascade-is-not-deleting-records-sequelize
//destroy method call on seq object
req.token.destroy({
onDelete: "cascade",
});
req.user.destroy();
res.json({
message: "User deleted successfully",
});
});
module.exports = router; module.exports = router;

View File

@ -6,6 +6,7 @@
!--> !-->
<%- include('top') %> <%- include('top') %>
<link rel="stylesheet" href="css/api.css" media="all">
<body class="one-content-column-version"> <body class="one-content-column-version">
<div class="left-menu"> <div class="left-menu">
@ -38,8 +39,8 @@
<li class="scroll-to-link active" data-target="content-get-started"> <li class="scroll-to-link active" data-target="content-get-started">
<a>GET STARTED</a> <a>GET STARTED</a>
</li> </li>
<li class="scroll-to-link" data-target="content-get-characters"> <li class="scroll-to-link" data-target="content-get-api">
<a>Get Data From API</a> <a>Generate API</a>
</li> </li>
<li class="scroll-to-link" data-target="content-errors"> <li class="scroll-to-link" data-target="content-errors">
<a>Errors</a> <a>Errors</a>
@ -86,7 +87,8 @@
<td>Authorization</td> <td>Authorization</td>
<td>JSON</td> <td>JSON</td>
<td>Your API key.</td> <td>Your API key.</td>
<td>(Required) Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: {provide your <td>(Required) Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization:
{provide your
API key here}"</td> API key here}"</td>
</td> </td>
</tr> </tr>
@ -118,7 +120,8 @@
<td>Authorization</td> <td>Authorization</td>
<td>JSON</td> <td>JSON</td>
<td>(Required) Your API key.</td> <td>(Required) Your API key.</td>
<td>Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: {provide your <td>Example: curl https://api.teeseng.uk/api/v0/location -H "Authorization: {provide
your
API key here}"</td> API key here}"</td>
</td> </td>
</tr> </tr>
@ -133,7 +136,8 @@
<br> <br>
<br> <br>
Example :<br> Example :<br>
<code class="higlighted break-word">curl https://api.teeseng.uk/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "SAMPLE", "added_by": "system" , "description": "test"}'</code> <code
class="higlighted break-word">curl https://api.teeseng.uk/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "SAMPLE", "added_by": "system" , "description": "test"}'</code>
<br> <br>
<br> <br>
Return Response :<br> Return Response :<br>
@ -154,7 +158,8 @@
<td>Authorization</td> <td>Authorization</td>
<td>JSON</td> <td>JSON</td>
<td>Your API key.</td> <td>Your API key.</td>
<td>(Required) Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: {provide your <td>(Required) Example: curl https://api.teeseng.uk/api/v0/location/new -H
"Authorization: {provide your
API key here}"</td> API key here}"</td>
</td> </td>
</tr> </tr>
@ -162,7 +167,8 @@
<td>Location name</td> <td>Location name</td>
<td>JSON</td> <td>JSON</td>
<td>Location name.</td> <td>Location name.</td>
<td>(Required) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide <td>(Required) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new
-H "Authorization: provide
your API key here" -d '{"name":"Location name"}'</td> your API key here" -d '{"name":"Location name"}'</td>
</td> </td>
</tr> </tr>
@ -170,7 +176,8 @@
<td>Added by </td> <td>Added by </td>
<td>JSON</td> <td>JSON</td>
<td>System or Admin</td> <td>System or Admin</td>
<td>(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide <td>(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"}'</td> your API key here" -d '{"added_by":"system"}'</td>
</td> </td>
</tr> </tr>
@ -178,7 +185,8 @@
<td>Description</td> <td>Description</td>
<td>JSON</td> <td>JSON</td>
<td>Description of Location</td> <td>Description of Location</td>
<td>(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide <td>(Required) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new
-H "Authorization: provide
your API key here" -d '{"description":"test"}'</td> your API key here" -d '{"description":"test"}'</td>
</td> </td>
</tr> </tr>
@ -194,7 +202,8 @@
<br> <br>
<br> <br>
Example :<br> Example :<br>
<code class="higlighted break-word">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"}'</code> <code
class="higlighted break-word">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"}'</code>
<br> <br>
<br> <br>
Return Response :<br> Return Response :<br>
@ -215,7 +224,8 @@
<td>Authorization</td> <td>Authorization</td>
<td>JSON</td> <td>JSON</td>
<td>Your API key.</td> <td>Your API key.</td>
<td>(Required) example: curl https://api.teeseng.uk/api/v0/location/update -H "Authorization: {provide your <td>(Required) example: curl https://api.teeseng.uk/api/v0/location/update -H
"Authorization: {provide your
API key here}"</td> API key here}"</td>
</td> </td>
</tr> </tr>
@ -223,7 +233,8 @@
<td>ID</td> <td>ID</td>
<td>JSON</td> <td>JSON</td>
<td>Location ID</td> <td>Location ID</td>
<td>(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/update -H "Authorization: provide <td>(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/update
-H "Authorization: provide
your API key here" -d '{"id": "7"}'</td> your API key here" -d '{"id": "7"}'</td>
</td> </td>
</tr> </tr>
@ -231,7 +242,8 @@
<td>Location name</td> <td>Location name</td>
<td>JSON</td> <td>JSON</td>
<td>Location name.</td> <td>Location name.</td>
<td>(Optional) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide <td>(Optional) Location name. Example: curl https://api.teeseng.uk/api/v0/location/new
-H "Authorization: provide
your API key here" -d '{"name":"Location name"}'</td> your API key here" -d '{"name":"Location name"}'</td>
</td> </td>
</tr> </tr>
@ -239,7 +251,8 @@
<td>Added by </td> <td>Added by </td>
<td>JSON</td> <td>JSON</td>
<td>System or Admin</td> <td>System or Admin</td>
<td>(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide <td>(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"}'</td> your API key here" -d '{"added_by":"system"}'</td>
</td> </td>
</tr> </tr>
@ -247,7 +260,8 @@
<td>Description</td> <td>Description</td>
<td>JSON</td> <td>JSON</td>
<td>Description of Location</td> <td>Description of Location</td>
<td>(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new -H "Authorization: provide <td>(Optional) System or Admin Example: curl https://api.teeseng.uk/api/v0/location/new
-H "Authorization: provide
your API key here" -d '{"description":"test"}'</td> your API key here" -d '{"description":"test"}'</td>
</td> </td>
</tr> </tr>
@ -263,7 +277,8 @@
<br> <br>
<br> <br>
Example :<br> Example :<br>
<code class="higlighted break-word">curl https://api.teeseng.uk/api/v0/location/delete -H "Content-Type: application/json" -X POST -d '{"id": "7"}'</code> <code
class="higlighted break-word">curl https://api.teeseng.uk/api/v0/location/delete -H "Content-Type: application/json" -X POST -d '{"id": "7"}'</code>
</p> </p>
<br> <br>
<h4>QUERY PARAMETERS</h4> <h4>QUERY PARAMETERS</h4>
@ -280,7 +295,8 @@
<td>Authorization</td> <td>Authorization</td>
<td>JSON</td> <td>JSON</td>
<td>Your API key.</td> <td>Your API key.</td>
<td>(Required) example: curl https://api.teeseng.uk/api/v0/location/delete -H "Authorization: {provide your <td>(Required) example: curl https://api.teeseng.uk/api/v0/location/delete -H
"Authorization: {provide your
API key here}"</td> API key here}"</td>
</td> </td>
</tr> </tr>
@ -288,7 +304,8 @@
<td>ID</td> <td>ID</td>
<td>JSON</td> <td>JSON</td>
<td>Location ID</td> <td>Location ID</td>
<td>(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/delete -H "Authorization: provide <td>(Required) Location ID Example: curl https://api.teeseng.uk/api/v0/location/delete
-H "Authorization: provide
your API key here" -d '{"id": "7"}'</td> your API key here" -d '{"id": "7"}'</td>
</td> </td>
</tr> </tr>
@ -296,24 +313,6 @@
</table> </table>
</div> </div>
<div class="overflow-hidden content-section" id="content-errors"> <div class="overflow-hidden content-section" id="content-errors">
<h2>Errors</h2> <h2>Errors</h2>
<p> <p>
@ -359,9 +358,41 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="overflow-hidden content-section" id="content-get-api">
<div class="api-keys-header">
<h2>API Keys</h2>
<button class="generate-key-button">Generate Key</button>
<p>
You can generate API Keys here:
</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Public Key</th>
<th>Private Key</th>
<th>Key Type</th>
<th>Created</th>
</tr>
</thead>
<tbody>
<tr>
<td>API Key</td>
<td>GR234-We34</td>
<td>greR-234-fEG</td>
<td>Type</td>
<td>2024-01-22</td>
</tr>
</tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>
</body> </body>
<script src="js/api.js"></script>
</html> </html>

View File

@ -81,12 +81,7 @@
</p> </p>
</div> </div>
</footer> </footer>
<script src="js/learnmore.js"></script>
<script src="js/search.js"></script> <script src="js/search.js"></script>
<script src="js/api.js"></script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,5 @@
<%- include('top') %> <%- include('top') %>
<link href="css/contact.css" rel="stylesheet" />
<!-- full Title --> <!-- full Title -->
<div class="full-title"> <div class="full-title">

View File

@ -4,7 +4,7 @@
<section class="wrapper"> <section class="wrapper">
<div class="form"> <div class="form">
<header>Reset Password</header> <header>Reset Password</header>
<form action="resetpassword.html"> <form action="/resetpassword">
<input type="text" id="email" placeholder="Email" required /> <input type="text" id="email" placeholder="Email" required />
<input type="password" id="password" placeholder="Password" required /> <input type="password" id="password" placeholder="Password" required />
<input type="password" id="confirmPassword" placeholder="Confirm Password" required /> <input type="password" id="confirmPassword" placeholder="Confirm Password" required />

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,43 +54,55 @@
<!-- 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"> <div class="card">
<h4 class="card-header">Humidity</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"> 70% - 75% </p> <p class="card-text display-4">15 - 18 PSI</p>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div> </div>
<div class="card-footer"> </div>
<a href="/learnmore" class="btn btn-primary">Learn More</a> <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> </div>
</div> <div class="col-lg-3 mb-4">
<div class="col-lg-4 mb-4"> <div class="card">
<div class="card"> <h4 class="card-header">Temperature</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">30&deg; - 37&deg;</p>
<p class="card-text display-4"> 15 - 18 PSI </p> </div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div> </div>
<div class="card-footer"> </div>
<a href="/learnmore" class="btn btn-primary">Learn More</a> <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 class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-4 mb-4">
<div class="card">
<h4 class="card-header">Temperature</h4>
<div class="card-body text-center">
<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>
<!-- /.row --> <!-- /.row -->
</div> </div>
<!-- About Section --> <!-- About Section -->
@ -121,4 +140,4 @@
</div> </div>
<hr> <hr>
</div> </div>
<%- include('bot') %> <%- include('bot') %>

View File

@ -1,4 +1,5 @@
<%- include('top') %> <%- include('top') %>
<link href="css/learnmore.css" rel="stylesheet" />
<br> <br>
<br> <br>
@ -37,5 +38,7 @@
<br> <br>
<br> <br>
<script src="js/learnmore.js"></script>
<%- include('bot') %> <%- include('bot') %>

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta 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="">
<link rel="shortcut icon" type="images/logo.ico" href="images/logo.ico" />
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
@ -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

@ -1,64 +1,111 @@
<%- include('top') %> <%- include('top') %>
<script type="text/javascript"> <br>
// Require login to see this page. <br>
app.auth.forceLogin() <br>
</script> <div class="profile">
<!-- <li jq-repeat="getUsername" class="nav-item"> -->
<div class="edit_information" jq-repeat="getUserDetails">
<div class="card-header shadow actionMessage" style="display:none"></div>
<form id="profileForm" action="user/update" method="put" onsubmit="formAJAX(this)"
evalAJAX="app.auth.profileRedirect();">
<h3 class="text-center">Edit Personal Information</h3>
<br>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="form-group">
<label class="profile_details_text">First Name:</label>
<input type="text" name="first_name" value="{{ user.firstname }}" class="form-control"
pattern="^[a-zA-Z\s]+$">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="form-group">
<label class="profile_details_text">Last Name: </label>
<input type="text" name="last_name" value="{{ user.lastname }}" class="form-control"
pattern="^[a-zA-Z\s]+$">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="form-group">
<label class="profile_details_text">Username:</label>
<input type="text" name="username" value="{{ user.username }}" class="form-control"
pattern="^\w+$">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="form-group">
<label class="profile_details_text">Password:</label>
<input type="password" name="password" placeholder="Password" class="form-control" value=""
pattern="^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="form-group">
<label class="profile_details_text">Email Address:</label>
<input type="email" name="email" value="{{user.email}}" class="form-control"
pattern="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="form-group">
<label class="profile_details_text">Address:</label>
<input type="text" name="address" value="{{ user.address }}" class="form-control"
pattern="^(\d{1,3}.)?.+\s(\d{6})$">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="form-group">
<label class="profile_details_text">Phone Number:</label>
<input type="tel" name="phone" value="{{ user.phone }}" class="form-control" value=""
pattern="^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,8}$">
<div class="full-title"> </div>
<div class="container"> </div>
<h1 class="mt-4 mb-3">Profile </div>
</h1> <br>
</div> <div class="row justify-content-center mt-3">
</div> <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 submit">
<div class="form-group text-center">
<br> <input type="submit" class="btn btn-success updateButton" value="Update Profile">
<br> </div>
</div>
<div class="centered-content"> <div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 text-center">
<div class="container"> <button type="button" class="btn btn-danger deleteButton" onclick="return myConfirm();">Delete
<div class="row"> Profile</button>
<div class="col-md-3 border-right"> </div>
<div class="d-flex flex-column align-items-center text-center p-3 py-5"><img </div>
class="rounded-circle mt-5" width="150px" </form>
src="https://st3.depositphotos.com/15648834/17930/v/600/depositphotos_179308454-stock-illustration-unknown-person-silhouette-glasses-profile.jpg"> </div>
</div> </div>
</div> <br>
<div class="col-md-5 border-right"> <br>
<div class="p-3 py-5"> <br>
<div class="d-flex justify-content-between align-items-center mb-3"> <br>
<h4 class="text-right">Profile Settings</h4> <br>
</div> <br>
<div class="row mt-2"> <script>
<div class="col-md-6"><label class="labels">Name</label><input type="text" function myConfirm() {
class="form-control" placeholder="first name" value=""></div> var result = confirm("Do you wish to Delete your Profile");
<div class="col-md-6"><label class="labels">Surname</label><input type="text" if (result == true) {
class="form-control" value="" placeholder="surname"></div> app.user.deleteProfile();
</div> } else {
<div class="row mt-3"> return false;
<div class="col-md-12"><label class="labels">Mobile Number</label><input type="text" }
class="form-control" placeholder="enter phone number" value=""></div> }
<div class="col-md-12"><label class="labels">Address</label><input type="text"
class="form-control" placeholder="enter address" value=""></div>
<div class="col-md-12"><label class="labels">Email</label><input type="text"
class="form-control" placeholder="enter email" value=""></div>
<div class="col-md-12"><label class="labels">Password</label><input type="text"
class="form-control" placeholder="enter password" value=""></div>
<div class="col-md-12"><label class="labels">Password</label><input type="text"
class="form-control" placeholder="re enter password" value=""></div>
</div>
<div class="mt-2 text-center">
<button class="btn btn-sm btn-secondary change-password-button" type="button">Change
Password</button>
</div>
<div class="mt-5 text-center"><button class="btn btn-primary profile-button" type="button">Save
Profile</button></div>
</div>
</div>
</div>
</div>
</div>
<br>
<%- include('bot') %>
</script>
<%- include('bot') %>

View File

@ -0,0 +1,39 @@
<%- 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 () {
app.api.get('sensor-data/data?order=DESC&limit=40', function(error, data){
$.scope.sensorData.push(...data);
})
})
</script>
<%- include('bot') %>

View File

@ -9,6 +9,7 @@
<div class="form signup iot-card"> <div class="form signup iot-card">
<!--<div class="form signup card" --> <!--<div class="form signup card" -->
<header>Signup</header> <header>Signup</header>
<div class="card-header shadow actionMessage" style="display:none"></div>
<!-- localhost/api/v0/user/register --> <!-- localhost/api/v0/user/register -->
<!-- evalAjax Fires when status 200 is returned --> <!-- evalAjax Fires when status 200 is returned -->
<form action="auth/register" onsubmit="formAJAX(this)" evalAJAX="app.auth.logInRedirect();"> <form action="auth/register" onsubmit="formAJAX(this)" evalAJAX="app.auth.logInRedirect();">
@ -35,7 +36,7 @@
<form action="auth/login" onsubmit="formAJAX(this)" evalAJAX="app.auth.homeRedirect(); <form action="auth/login" onsubmit="formAJAX(this)" evalAJAX="app.auth.homeRedirect();
app.auth.setToken(data.token);"> app.auth.setToken(data.token);">
<input type="text" name="username" placeholder="Email address | Username" required <input type="text" name="username" placeholder="Email address | Username" required
pattern="^\w+$" /> pattern="^(\w+@\w+\.\w+)$|^\w+$"/>
<input type="password" name="password" placeholder="Password" required /> <input type="password" name="password" placeholder="Password" required />
<a href="/forgotpassword">Forgot password?</a> <a href="/forgotpassword">Forgot password?</a>
<input type="submit" value="Login" /> <input type="submit" value="Login" />

View File

@ -1,146 +1,130 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<meta http-equiv="cleartype" content="on" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Bootstrap core CSS --> <head>
<link <meta charset="utf-8" />
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
rel="stylesheet" <meta name="description" content="" />
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" <meta name="author" content="" />
crossorigin="anonymous" /> <meta http-equiv="cleartype" content="on" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" type="images/logo.ico" href="images/logo.ico" />
<!-- Custom styles for this template -->
<link href="css/all.css" rel="stylesheet" />
<link href="css/style.css" rel="stylesheet" />
<link href="css/learnmore.css" rel="stylesheet" />
<link href="css/contact.css" rel="stylesheet" />
<link rel="stylesheet" href="css/api.css" media="all" />
<!-- weird api page cdn -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;1,300&family=Source+Code+Pro:wght@300&display=swap"
rel="stylesheet" />
<!-- Mustache JS -->
<script src="https://sso.theta42.com/static/js/mustache.min.js"></script>
<!-- jQuery library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- Bootstrap 5 JavaScript --> <!-- Bootstrap core CSS -->
<script <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>
<!-- weird api page cdn --> <!-- Custom styles for this template -->
<!-- https://github.com/floriannicolas/API-Documentation-HTML-Template/tree/master --> <link href="css/all.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/highlight.min.js"></script> <link href="css/style.css" rel="stylesheet" />
<script> <!-- weird api page cdn -->
hljs.initHighlightingOnLoad(); <link rel="preconnect" href="https://fonts.googleapis.com" />
</script> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;1,300&family=Source+Code+Pro:wght@300&display=swap"
rel="stylesheet" />
<!-- Mustache JS -->
<script src="https://sso.theta42.com/static/js/mustache.min.js"></script>
<!-- jQuery library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<!-- Bootstrap 5 JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>
<!-- jq-repeat --> <!-- weird api page cdn -->
<script src="js/jq-repeat.js"></script> <!-- https://github.com/floriannicolas/API-Documentation-HTML-Template/tree/master -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/highlight.min.js"></script>
<!-- jquery app.js -->
<script src="js/app.js"></script>
</head>
<!-- javascript function to check if user is auth -->
<script> <script>
//make document ready hljs.initHighlightingOnLoad();
$(document).ready(function () {
//check if user is logged in
app.auth.isLoggedIn(function (error, data) {
if (data) {
app.auth.showUser();
$("#cl-logout-button").show("fast");
$("#cl-profile-button").show("fast");
$("#cl-login-button").hide("fast");
} else {
$("#cl-login-button").show("fast");
}
$("body").show("fast");
});
});
</script> </script>
<!-- socket.io scriot -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.2.0/socket.io.min.js"></script>
<body>
<nav
class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container">
<a class="navbar-brand" href="/">
<img src="images/logo.png" alt="logo" />
</a>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false"
aria-label="Toggle navigation">
<span class="fas fa-bars"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto">
<li jq-repeat="getUsername" class="nav-item">
<a class="nav-link"> Welcome {{ user.username }} </a>
</li>
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/news">News</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/contact">Contact</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/api">API Doc</a>
</li>
<!-- profile button -->
<div class="form-inline mt-2 mt-md-0">
<!-- Profile Button -->
<a
id="cl-profile-button"
class="btn btn-outline-info btn-sm my-2 my-sm-0"
href="/profile"
style="display: none">
<i class="fas fa-sign-out"></i> Profile
</a>
<!-- Login Button --> <!-- jq-repeat -->
<a <script src="js/jq-repeat.js"></script>
id="cl-login-button"
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
</a>
<!-- Logout Button --> <!-- jquery app.js -->
<button <script src="js/app.js"></script>
id="cl-logout-button" </head>
class="btn btn-outline-danger btn-sm my-2 my-sm-0" <!-- javascript function to check if user is auth -->
href="/" <script>
onclick="app.auth.logOut(e => window.location.href='/')" //make document ready
style="display: none"> $(document).ready(function () {
<i class="fas fa-sign-out"></i> Logout //check if user is logged in
</button> app.auth.isLoggedIn(function (error, data) {
</div> if (data) {
</ul> $("#cl-logout-button").show("fast");
</div> $("#cl-api-button").show("fast");
$("#cl-profile-button").show("fast");
$("#cl-login-button").hide("fast");
} else {
$("#cl-login-button").show("fast");
}
$("body").show("fast");
});
});
</script>
<body>
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-light top-nav fixed-top">
<div class="container">
<a class="navbar-brand" href="/">
<img src="images/logo.png" alt="logo" />
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive"
aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="fas fa-bars"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto">
<li jq-repeat="getUsername" class="nav-item">
<a class="nav-link"> Welcome {{ user.username }} </a>
</li>
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/news">News</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/contact">Contact</a>
</li>
<!--
<li class="nav-item">
<a class="nav-link" href="/api">API Doc</a>
</li>
-->
<!-- profile button -->
<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 -->
<a id="cl-profile-button" class="btn btn-outline-info btn-sm my-2 my-sm-0" href="/profile"
style="display: none">
<i class="fas fa-sign-out"></i> Profile
</a>
<!-- Login Button -->
<a id="cl-login-button" 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
</a>
<!-- Logout Button -->
<button id="cl-logout-button" 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
</button>
</div>
</ul>
</div> </div>
</nav> </div>
</body> </nav>
</html>

View File

@ -21,6 +21,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,8 @@
"sanitize-html": "^2.11.0", "sanitize-html": "^2.11.0",
"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-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;