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";
const { Sequelize, DataTypes } = require("sequelize");
const { sequelize } = require("./mySQL");
const { isAlphaNumericwithSpaces } = require('../../Web-Server/functions/validateData')
//sequelize.sync();
const locationModel = sequelize.define(

View File

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

View File

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

View File

@ -27,9 +27,9 @@ client.on("error", (err) => {
});
//every 15 minutes
setInterval(publishData, 900000);
//setInterval(publishData, 900000);
//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
2) Sean
* Admin Website Microservice
Micro Service
1) api.blah handle api
2) admin.blah admin website
3) consumer.blah comsumer
4) proxy.blah reverproxy
5) mqtt.blah mqtt service

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
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 { rateLimit } = require("express-rate-limit");
const path = require("path");
const app = express();
const port = 3000;
const ejs = require("ejs");
module.exports = app;
process.nextTick(() => require('./mqttApp'));
app.use(express.json());
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.
app.set("views", path.join(__dirname, "../views"));
app.set("views", path.join(__dirname, "./views"));
app.set("view engine", "ejs");
// Have express server static content( images, CSS, browser JS) from the public
app.use(express.static(path.join(__dirname, "../public")));
//middleware logic ( called by next() )
const auth = require("../middleware/authChecker");
app.use(express.static(path.join(__dirname, "./public")));
//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
app.use("/", require("../routes/render"));
app.use("/", require("./routes/render"));
// Catch 404 and forward to error handler. If none of the above routes are
// used, this is what will be called.
@ -69,8 +88,4 @@ app.use(function (err, req, res, next) {
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;
//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
async function hashPassword(password) {
async function hash(password) {
return await bcrypt.hash(password, saltRounds);
}
async function hashAPIKey(apikey) {
return await bcrypt.hash(apikey, saltRounds);
}
//can be used to compare password or token
async function comparePassword(password, hash) {
async function compareHash(password, hash) {
return await bcrypt.compare(password, hash);
}
module.exports = {
hashPassword,
hashAPIKey,
comparePassword
hash,
compareHash
};

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 { 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
//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;
}
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() {
const sensorData = await sensorDataModel.findAll();
return sensorData;
}
async function addSensorData(id, id_sensor, id_location, sensordata) {
async function addSensorData(id_sensor, id_location, sensordata) {
const sensorData = await sensorDataModel.create({
id: id,
sensorid: id_sensor,
locationid: id_location,
measurement: sensordata,
});
io().emit('sensorData:new', sensorData)
return sensorData;
}
async function updateSensorData(id, id_sensor, id_location, sensordata) {
@ -826,16 +717,6 @@ async function getDatabyRange(queryString) {
}
module.exports = {
getLocation,
addLocation,
updateLocation,
deleteLocation,
getLocationById,
getSensor,
addSensor,
updateSensor,
deleteSensor,
getSensorById,
getSensorData,
addSensorData,
updateSensorData,
@ -843,4 +724,5 @@ module.exports = {
getSensorDataById,
getData,
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");
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) {
return validator.isAlphanumeric(value, ["en-US"], { ignore: " " });
}
@ -16,7 +38,6 @@ function isAlphaNumericWithSpacesAndDash(value) {
function isJson(value) {
//check if its object
if(typeof value === "object"){
console.log("its an object")
return true
}
@ -44,6 +65,8 @@ module.exports = {
isAlphaNumericwithSpaces,
isAlphaNumericWithSpacesAndDash,
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) {
//const authHeader = req.headers.authorization
try {

View File

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

View File

@ -1,6 +1,6 @@
const { apikeyModel } = require("../database/model/apiKeyModel");
const { userModel } = require("../database/model/userModel");
const { comparePassword } = require("../functions/bcrypt");
const { compareHash } = require("../functions/bcrypt");
async function auth(req, res, next){
try{
@ -12,19 +12,20 @@ async function auth(req, res, next){
//get from db
let token = await apikeyModel.findByPk(rowid, {include: userModel});
if (!token) return false;
//compare
let isMatch = await comparePassword(suppliedToken, token.apikey);
let isMatch = await compareHash(suppliedToken, token.apikey);
if (!isMatch) return false;
//else do logic
//pass hashed token to req.token (IMPORTANT ITS NOT PASSED TO CLIENT)
req.token = token
req.user = await token.getUser();
req.user = await token.getUser(); //taking user seq obj from usermodel
next();
}catch(error){
next(error);
}
}
module.exports = { auth };
module.exports = { auth };

View File

@ -10,13 +10,11 @@ const options = {
username: process.env.MQTT_USER,
password: process.env.MQTT_PASS,
protocol: 'mqtts', // Use MQTT over TLS
key: fs.readFileSync(path.resolve(__dirname, '../../cert/privkey.pem')),
cert: fs.readFileSync(path.resolve(__dirname, '../../cert/cert.pem')),
key: fs.readFileSync(path.resolve(__dirname, '../cert/privkey.pem')),
cert: fs.readFileSync(path.resolve(__dirname, '../cert/cert.pem')),
};
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");
const client = require("./modules/mqtt.js");
const { isJson, isNumber } = require("./functions/validateData.js");
const { insertDatatoDB } = require("./functions/database.js");
const { addSensorData } = require("./functions/sensorData");
// Event handlers
client.on("connect", () => {
@ -38,7 +37,7 @@ client.on("message", (topic, message) => {
if (isNumber(data)) {
{
//pass datas to database
insertDatatoDB(datas[key]);
addSensorData(datas[key].sensorid, datas[key].locationid, datas[key]);
}
} else {
@ -64,3 +63,4 @@ client.on("end", () => {
console.log("Disconnected from MQTT broker");
client.reconnect();
});

View File

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

View File

@ -523,4 +523,27 @@ body.one-content-column-version .content thead {
.cf:before, .cf:after { content: ""; display: block; }
.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 {
background-color: #fff;
padding: 20px;
padding: 25px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
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 };
})(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) {
var user = {};
function setToken(token) {
@ -154,12 +182,22 @@ app.auth = (function (app) {
return localStorage.getItem("APIToken");
}
function isLoggedIn(callback) {
if (getToken()) {
return app.api.get("user/me", function (error, 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);
});
} 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) {
//call logout route
$.ajax({
@ -217,19 +245,13 @@ app.auth = (function (app) {
}
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 {
getToken: getToken,
@ -239,8 +261,25 @@ app.auth = (function (app) {
forceLogin,
logInRedirect,
homeRedirect,
showUser,
//redirectIfLoggedIn,
profileRedirect,
};
})(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);
@ -251,11 +290,6 @@ function formAJAX(btn, del) {
var formData = $form.find("[name]").serializeObject(); // builds query formDataing
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");
//console.log('Data being sent to', $form.attr('action'), formData)

View File

@ -1,28 +1,60 @@
document.addEventListener("DOMContentLoaded", function () {
function updateAdditionalInfo(region) {
const infoContainer = document.getElementById("additional-info");
// Replace the following with actual data retrieval based on the region
const aqi = "15";
const temperature = "25°C";
const humidity = "60%";
// Replace the following with actual data retrieval based on the region
const aqi = "15";
const temperature = "25°C";
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
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';
const router = require('express').Router();
const { auth } = require("../middleware/authChecker")
@ -12,3 +40,4 @@ router.use('/user', auth ,require('./user'));
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 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)
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;

View File

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

View File

@ -4,7 +4,7 @@ const {
getLocationById,
updateLocation,
deleteLocation,
} = require("../functions/apiDatabase.js");
} = require("../functions/location");
const express = require("express");
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";
var router = require("express").Router();
@ -66,11 +27,17 @@ router.get("/profile", function (req, res, next) {
res.render("profile");
});
//forgot password page
router.get("/forgotpassword", function (req, res, next) {
res.render("forgotpassword");
});
//resetted password page
router.get("/resetpassword", function (req, res, next) {
res.render("resetpassword");
});
//contact page
router.get("/contact", function (req, res, next) {
res.render("contact");
@ -81,6 +48,9 @@ router.get("/api", function (req, res, next) {
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 { locationModel } = require("../Database/model/locationModel.js");
const { sensorModel } = require("../Database/model/sensorModel.js");
const { sequelize } = require("../database/mySQL.js");
const { locationModel } = require("../database/model/locationModel.js");
const { sensorModel } = require("../database/model/sensorModel.js");
const express = require("express");
const router = express.Router();
@ -38,4 +38,4 @@ router.post("/new", async (req, res, next) => {
module.exports = router;
module.exports = 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 { locationModel } = require("../Database/model/locationModel.js");
const { sensorModel } = require("../Database/model/sensorModel.js");
const { sensorDataModel } = require("../Database/model/sensorDataModel.js");
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 express = require("express");
const router = express.Router();

View File

@ -4,7 +4,7 @@ const {
updateSensor,
deleteSensor,
getSensorById
} = require("../functions/apiDatabase.js");
} = require("../functions/sensor.js");
const express = require("express");
const router = express.Router();
@ -63,4 +63,4 @@ router.get("/:id", async (req, res, next) => {
}
});
module.exports = router;
module.exports = router;

View File

@ -6,10 +6,9 @@ const {
getSensorDataById,
getData,
getDatabyRange,
} = require("../functions/apiDatabase.js");
} = require("../functions/sensorData");
const express = require("express");
const { json } = require("body-parser");
const router = express.Router();
router.get("/", async (req, res, next) => {
@ -24,9 +23,9 @@ router.get("/", async (req, res, next) => {
router.post("/new", async (req, res, next) => {
try {
const { id, id_sensor, id_location, sensordata } = req.body;
await addSensorData(id, id_sensor, id_location, sensordata);
res.sendStatus(200).json({ message: "SensorData " + id + " added" });
const { id_sensor, id_location, sensordata } = req.body;
let data = await addSensorData(id_sensor, id_location, sensordata);
res.json({ message: "SensorData " + data.id + " added", ...data });
} catch (error) {
console.error(error);
next(error);
@ -91,4 +90,3 @@ router.get("/:id", async (req, res, next) => {
});
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 router = express.Router();
@ -6,27 +6,25 @@ const router = express.Router();
//api/v0/user/me
//getbyid
router.get("/me", async function (req, res, next) {
try {
let user = await getUserID(req.user);
if (!user) {
let error = new Error("User not found");
error.status = 400;
return next(error);
}
res.json({
user: user,
});
} catch (error) {
next(error);
}
try {
let user = await getUserID(req.user);
if (!user) {
let error = new Error("User not found");
error.status = 400;
return next(error);
}
res.json({
user: user,
});
} catch (error) {
next(error);
}
});
//logout
router.delete('/logout', async function(req, res, next){
try{
/*
//logout
router.delete("/logout", async function (req, res, next) {
try {
/*
let authToken = req.header('auth-token');
let userDel = await deleteUserToken(authToken);
if (!userDel) {
@ -35,23 +33,51 @@ router.delete('/logout', async function(req, res, next){
return next(error);
}
*/
//destroy method call on seq object
req.token.destroy();
// DO NOT CALL THIS!!! IT WILL DESTROY USERMODEL SEQ OBJ
//req.user.destroy();
res.json({
message: "User logged out successfully",
});
}catch(error){
next(error);
}
});
//destroy method call on seq object
req.token.destroy();
// DO NOT CALL THIS!!! IT WILL DESTROY USERMODEL SEQ OBJ
//req.user.destroy();
res.json({
message: "User logged out successfully",
});
} catch (error) {
next(error);
}
});
//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
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;

View File

@ -6,6 +6,7 @@
!-->
<%- include('top') %>
<link rel="stylesheet" href="css/api.css" media="all">
<body class="one-content-column-version">
<div class="left-menu">
@ -38,8 +39,8 @@
<li class="scroll-to-link active" data-target="content-get-started">
<a>GET STARTED</a>
</li>
<li class="scroll-to-link" data-target="content-get-characters">
<a>Get Data From API</a>
<li class="scroll-to-link" data-target="content-get-api">
<a>Generate API</a>
</li>
<li class="scroll-to-link" data-target="content-errors">
<a>Errors</a>
@ -86,7 +87,8 @@
<td>Authorization</td>
<td>JSON</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>
</td>
</tr>
@ -118,7 +120,8 @@
<td>Authorization</td>
<td>JSON</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>
</td>
</tr>
@ -129,11 +132,12 @@
<h2>Add Location (Only for system or admin API key)</h2>
<p>
To add an Location you need to make a POST call to the following url :<br>
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/new</code>
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/new</code>
<br>
<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>
Return Response :<br>
@ -154,7 +158,8 @@
<td>Authorization</td>
<td>JSON</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>
</td>
</tr>
@ -162,7 +167,8 @@
<td>Location name</td>
<td>JSON</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>
</td>
</tr>
@ -170,7 +176,8 @@
<td>Added by </td>
<td>JSON</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>
</td>
</tr>
@ -178,7 +185,8 @@
<td>Description</td>
<td>JSON</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>
</td>
</tr>
@ -190,13 +198,14 @@
<h2>Update Location (Only for system or admin API key)</h2>
<p>
To update an Location you need to make a PUT call to the following url :<br>
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/update</code>
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/update</code>
<br>
<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>
Return Response :<br>
<code class="higlighted break-word">{"status":"200","message":"Location 7 updated"}</code>
</p>
@ -215,7 +224,8 @@
<td>Authorization</td>
<td>JSON</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>
</td>
</tr>
@ -223,7 +233,8 @@
<td>ID</td>
<td>JSON</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>
</td>
</tr>
@ -231,7 +242,8 @@
<td>Location name</td>
<td>JSON</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>
</td>
</tr>
@ -239,7 +251,8 @@
<td>Added by </td>
<td>JSON</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>
</td>
</tr>
@ -247,7 +260,8 @@
<td>Description</td>
<td>JSON</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>
</td>
</tr>
@ -259,11 +273,12 @@
<h2>Delete Location (Only for system or admin API key)</h2>
<p>
To delete an Location you need to make a DELETE call to the following url :<br>
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/delete</code>
<code class="higlighted break-word">https://api.teeseng.uk/api/v0/location/delete</code>
<br>
<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>
<br>
<h4>QUERY PARAMETERS</h4>
@ -280,7 +295,8 @@
<td>Authorization</td>
<td>JSON</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>
</td>
</tr>
@ -288,7 +304,8 @@
<td>ID</td>
<td>JSON</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>
</td>
</tr>
@ -296,24 +313,6 @@
</table>
</div>
<div class="overflow-hidden content-section" id="content-errors">
<h2>Errors</h2>
<p>
@ -359,9 +358,41 @@
</tbody>
</table>
</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>
</body>
</html>
<script src="js/api.js"></script>
</html>

View File

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

View File

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

View File

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

View File

@ -1,5 +1,12 @@
<%- 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">
@ -47,43 +54,55 @@
<!-- Page Content -->
<div class="container">
<div class="services-bar">
<h1 class="my-4">Services </h1>
<!-- Services Section -->
<div class="row">
<div class="col-lg-4 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>
<h1 class="my-4">Services</h1>
<!-- Services Section -->
<div class="row">
<div class="col-lg-3 mb-4">
<div class="card">
<h4 class="card-header">Air Quality Index</h4>
<div class="card-body text-center">
<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 class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
<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 class="col-lg-4 mb-4">
<div class="card">
<h4 class="card-header">Air Quality Index</h4>
<div class="card-body text-center">
<p class="card-text display-4"> 15 - 18 PSI </p>
</div>
<div class="col-lg-3 mb-4">
<div class="card">
<h4 class="card-header">Temperature</h4>
<div class="card-body text-center">
<p class="card-text display-4">30&deg; - 37&deg;</p>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</div>
</div>
<div class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</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 class="card-footer">
<a href="/learnmore" class="btn btn-primary">Learn More</a>
</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>
</div>
</div>
<!-- /.row -->
</div>
<!-- About Section -->
@ -121,4 +140,4 @@
</div>
<hr>
</div>
<%- include('bot') %>
<%- include('bot') %>

View File

@ -1,4 +1,5 @@
<%- include('top') %>
<link href="css/learnmore.css" rel="stylesheet" />
<br>
<br>
@ -37,5 +38,7 @@
<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="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" type="images/logo.ico" href="images/logo.ico" />
<!-- Bootstrap core CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
@ -26,6 +26,8 @@
<!-- 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.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 -->
<script src="js/app.js"></script>

View File

@ -1,64 +1,111 @@
<%- include('top') %>
<script type="text/javascript">
// Require login to see this page.
app.auth.forceLogin()
</script>
<br>
<br>
<br>
<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 class="container">
<h1 class="mt-4 mb-3">Profile
</h1>
</div>
</div>
</div>
</div>
</div>
<br>
<div class="row justify-content-center mt-3">
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 submit">
<div class="form-group text-center">
<input type="submit" class="btn btn-success updateButton" value="Update Profile">
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6 text-center">
<button type="button" class="btn btn-danger deleteButton" onclick="return myConfirm();">Delete
Profile</button>
</div>
</div>
</form>
</div>
</div>
<br>
<br>
<br>
<br>
<br>
<br>
<script>
function myConfirm() {
var result = confirm("Do you wish to Delete your Profile");
if (result == true) {
app.user.deleteProfile();
} else {
return false;
}
}
<br>
<br>
<div class="centered-content">
<div class="container">
<div class="row">
<div class="col-md-3 border-right">
<div class="d-flex flex-column align-items-center text-center p-3 py-5"><img
class="rounded-circle mt-5" width="150px"
src="https://st3.depositphotos.com/15648834/17930/v/600/depositphotos_179308454-stock-illustration-unknown-person-silhouette-glasses-profile.jpg">
</div>
</div>
<div class="col-md-5 border-right">
<div class="p-3 py-5">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="text-right">Profile Settings</h4>
</div>
<div class="row mt-2">
<div class="col-md-6"><label class="labels">Name</label><input type="text"
class="form-control" placeholder="first name" value=""></div>
<div class="col-md-6"><label class="labels">Surname</label><input type="text"
class="form-control" value="" placeholder="surname"></div>
</div>
<div class="row mt-3">
<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>
</script>
<br>
<%- include('bot') %>
<%- 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 card" -->
<header>Signup</header>
<div class="card-header shadow actionMessage" style="display:none"></div>
<!-- localhost/api/v0/user/register -->
<!-- evalAjax Fires when status 200 is returned -->
<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();
app.auth.setToken(data.token);">
<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 />
<a href="/forgotpassword">Forgot password?</a>
<input type="submit" value="Login" />

View File

@ -1,146 +1,130 @@
<!DOCTYPE html>
<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 -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous" />
<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" />
<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 -->
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"></script>
<!-- Bootstrap core CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
<!-- weird api page cdn -->
<!-- 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>
<script>
hljs.initHighlightingOnLoad();
</script>
<!-- Custom styles for this template -->
<link href="css/all.css" rel="stylesheet" />
<link href="css/style.css" rel="stylesheet" />
<!-- 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 -->
<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 -->
<script src="js/jq-repeat.js"></script>
<!-- jquery app.js -->
<script src="js/app.js"></script>
</head>
<!-- javascript function to check if user is auth -->
<!-- weird api page cdn -->
<!-- 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>
<script>
//make document ready
$(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");
});
});
hljs.initHighlightingOnLoad();
</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 -->
<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>
<!-- jq-repeat -->
<script src="js/jq-repeat.js"></script>
<!-- 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>
<!-- jquery app.js -->
<script src="js/app.js"></script>
</head>
<!-- javascript function to check if user is auth -->
<script>
//make document ready
$(document).ready(function () {
//check if user is logged in
app.auth.isLoggedIn(function (error, data) {
if (data) {
$("#cl-logout-button").show("fast");
$("#cl-api-button").show("fast");
$("#cl-profile-button").show("fast");
$("#cl-login-button").hide("fast");
} else {
$("#cl-login-button").show("fast");
}
$("body").show("fast");
});
});
</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>
</nav>
</body>
</html>
</div>
</nav>

View File

@ -21,6 +21,7 @@
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"csurf": "^1.11.0",
"date-fns": "^3.2.0",
"date-fns-tz": "^2.0.0",
@ -43,6 +44,8 @@
"sanitize-html": "^2.11.0",
"sequelize": "^6.35.2",
"sequelize-cli": "^6.6.2",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4",
"validator": "^13.11.0"
},
"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;