diff --git a/IoT-sensor/Database/locationModel.js b/IoT-sensor/Database/locationModel.js index 9163156..7bd55d2 100644 --- a/IoT-sensor/Database/locationModel.js +++ b/IoT-sensor/Database/locationModel.js @@ -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( diff --git a/IoT-sensor/Database/sensorModel.js b/IoT-sensor/Database/sensorModel.js index bed1d9b..1de32af 100644 --- a/IoT-sensor/Database/sensorModel.js +++ b/IoT-sensor/Database/sensorModel.js @@ -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( diff --git a/IoT-sensor/functions/dbFunctions.js b/IoT-sensor/functions/dbFunctions.js index f03c5a9..83993ec 100644 --- a/IoT-sensor/functions/dbFunctions.js +++ b/IoT-sensor/functions/dbFunctions.js @@ -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({ diff --git a/IoT-sensor/index.js b/IoT-sensor/index.js index 728cb30..a5cfb75 100644 --- a/IoT-sensor/index.js +++ b/IoT-sensor/index.js @@ -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); diff --git a/README.md b/README.md index 022ac48..3eaadc6 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/api.MD b/api.MD index b655bed..2f590e4 100644 --- a/api.MD +++ b/api.MD @@ -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"}' diff --git a/consumerWebsite/modules/app.js b/consumerWebsite/app.js similarity index 59% rename from consumerWebsite/modules/app.js rename to consumerWebsite/app.js index 3b3d8ba..ce7fee1 100644 --- a/consumerWebsite/modules/app.js +++ b/consumerWebsite/app.js @@ -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 }; diff --git a/consumerWebsite/bin/www b/consumerWebsite/bin/www new file mode 100644 index 0000000..dad62b9 --- /dev/null +++ b/consumerWebsite/bin/www @@ -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() + } +} \ No newline at end of file diff --git a/webserver/database/model/apiLogModel.js b/consumerWebsite/database/model/apiLogModel.js similarity index 100% rename from webserver/database/model/apiLogModel.js rename to consumerWebsite/database/model/apiLogModel.js diff --git a/webserver/database/model/locationModel.js b/consumerWebsite/database/model/locationModel.js similarity index 100% rename from webserver/database/model/locationModel.js rename to consumerWebsite/database/model/locationModel.js diff --git a/webserver/database/model/sensorDataModel.js b/consumerWebsite/database/model/sensorDataModel.js similarity index 100% rename from webserver/database/model/sensorDataModel.js rename to consumerWebsite/database/model/sensorDataModel.js diff --git a/webserver/database/model/sensorModel.js b/consumerWebsite/database/model/sensorModel.js similarity index 100% rename from webserver/database/model/sensorModel.js rename to consumerWebsite/database/model/sensorModel.js diff --git a/consumerWebsite/functions/api.js b/consumerWebsite/functions/api.js new file mode 100644 index 0000000..bbdbbe0 --- /dev/null +++ b/consumerWebsite/functions/api.js @@ -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 }; \ No newline at end of file diff --git a/consumerWebsite/functions/apiDatabase.js b/consumerWebsite/functions/apiDatabase.js deleted file mode 100644 index 1114c37..0000000 --- a/consumerWebsite/functions/apiDatabase.js +++ /dev/null @@ -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, -}; diff --git a/consumerWebsite/functions/bcrypt.js b/consumerWebsite/functions/bcrypt.js index f0b72a3..8a6bc7a 100644 --- a/consumerWebsite/functions/bcrypt.js +++ b/consumerWebsite/functions/bcrypt.js @@ -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 }; \ No newline at end of file diff --git a/consumerWebsite/functions/location.js b/consumerWebsite/functions/location.js new file mode 100644 index 0000000..4b5baf2 --- /dev/null +++ b/consumerWebsite/functions/location.js @@ -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, +}; \ No newline at end of file diff --git a/consumerWebsite/functions/logger.js b/consumerWebsite/functions/logger.js new file mode 100644 index 0000000..57a9c24 --- /dev/null +++ b/consumerWebsite/functions/logger.js @@ -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, +}; \ No newline at end of file diff --git a/consumerWebsite/functions/sensor.js b/consumerWebsite/functions/sensor.js new file mode 100644 index 0000000..a72512b --- /dev/null +++ b/consumerWebsite/functions/sensor.js @@ -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, +}; diff --git a/webserver/functions/APIDatabase.js b/consumerWebsite/functions/sensorData.js similarity index 88% rename from webserver/functions/APIDatabase.js rename to consumerWebsite/functions/sensorData.js index d357188..28cf3a0 100644 --- a/webserver/functions/APIDatabase.js +++ b/consumerWebsite/functions/sensorData.js @@ -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, -}; + +}; \ No newline at end of file diff --git a/consumerWebsite/functions/socket.js b/consumerWebsite/functions/socket.js new file mode 100644 index 0000000..acba88e --- /dev/null +++ b/consumerWebsite/functions/socket.js @@ -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; \ No newline at end of file diff --git a/consumerWebsite/functions/user.js b/consumerWebsite/functions/user.js new file mode 100644 index 0000000..ee5cdb0 --- /dev/null +++ b/consumerWebsite/functions/user.js @@ -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, +}; \ No newline at end of file diff --git a/consumerWebsite/functions/validateData.js b/consumerWebsite/functions/validateData.js index d95fa58..d3f255f 100644 --- a/consumerWebsite/functions/validateData.js +++ b/consumerWebsite/functions/validateData.js @@ -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, +}; \ No newline at end of file diff --git a/webserver/middleware/apiKey.js b/consumerWebsite/middleware/apiKey.js similarity index 96% rename from webserver/middleware/apiKey.js rename to consumerWebsite/middleware/apiKey.js index 042514d..4d91dcb 100644 --- a/webserver/middleware/apiKey.js +++ b/consumerWebsite/middleware/apiKey.js @@ -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 { diff --git a/webserver/middleware/apiLogger.js b/consumerWebsite/middleware/apiLogger.js similarity index 93% rename from webserver/middleware/apiLogger.js rename to consumerWebsite/middleware/apiLogger.js index bfe7f2f..21c3e33 100644 --- a/webserver/middleware/apiLogger.js +++ b/consumerWebsite/middleware/apiLogger.js @@ -1,4 +1,4 @@ -const { insertLogData } = require("../functions/database.js"); +const { insertLogData } = require("../functions/logger.js"); const APIlogger = (req, res, next) => { try { const log = { diff --git a/consumerWebsite/middleware/authChecker.js b/consumerWebsite/middleware/authChecker.js index b12ffe2..7855998 100644 --- a/consumerWebsite/middleware/authChecker.js +++ b/consumerWebsite/middleware/authChecker.js @@ -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 }; \ No newline at end of file diff --git a/webserver/modules/mqtt.js b/consumerWebsite/modules/mqtt.js similarity index 72% rename from webserver/modules/mqtt.js rename to consumerWebsite/modules/mqtt.js index 5266d7c..26b347b 100644 --- a/webserver/modules/mqtt.js +++ b/consumerWebsite/modules/mqtt.js @@ -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; \ No newline at end of file diff --git a/webserver/index.js b/consumerWebsite/mqttApp.js similarity index 87% rename from webserver/index.js rename to consumerWebsite/mqttApp.js index cf6da5d..2b13ef8 100644 --- a/webserver/index.js +++ b/consumerWebsite/mqttApp.js @@ -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(); }); + diff --git a/consumerWebsite/public/css/all.css b/consumerWebsite/public/css/all.css index d3173e3..a213d9c 100644 --- a/consumerWebsite/public/css/all.css +++ b/consumerWebsite/public/css/all.css @@ -3813,3 +3813,12 @@ .card-text { color: #000000; } + +/* edit profile */ +.profile { + margin: auto; + width: 40%; + border: 1px solid #000000; + padding: 10px; +} + diff --git a/consumerWebsite/public/css/api.css b/consumerWebsite/public/css/api.css index d3d44b1..f5d527e 100644 --- a/consumerWebsite/public/css/api.css +++ b/consumerWebsite/public/css/api.css @@ -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 } \ No newline at end of file +.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 */ +} \ No newline at end of file diff --git a/consumerWebsite/public/css/contact.css b/consumerWebsite/public/css/contact.css index 2137c19..524a5e4 100644 --- a/consumerWebsite/public/css/contact.css +++ b/consumerWebsite/public/css/contact.css @@ -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%; diff --git a/consumerWebsite/public/images/404.png b/consumerWebsite/public/images/404.png index bcf5936..6b34292 100644 Binary files a/consumerWebsite/public/images/404.png and b/consumerWebsite/public/images/404.png differ diff --git a/consumerWebsite/public/images/4044.png b/consumerWebsite/public/images/4044.png deleted file mode 100644 index 6b34292..0000000 Binary files a/consumerWebsite/public/images/4044.png and /dev/null differ diff --git a/consumerWebsite/public/images/logo.ico b/consumerWebsite/public/images/logo.ico new file mode 100644 index 0000000..8b55d25 Binary files /dev/null and b/consumerWebsite/public/images/logo.ico differ diff --git a/consumerWebsite/public/js/app.js b/consumerWebsite/public/js/app.js index b5a1a3d..5aed168 100644 --- a/consumerWebsite/public/js/app.js +++ b/consumerWebsite/public/js/app.js @@ -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) diff --git a/consumerWebsite/public/js/learnmore.js b/consumerWebsite/public/js/learnmore.js index c965824..2e4a440 100644 --- a/consumerWebsite/public/js/learnmore.js +++ b/consumerWebsite/public/js/learnmore.js @@ -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 = ` +
@@ -359,9 +358,41 @@
+ You can generate API Keys here: +
+Name | +Public Key | +Private Key | +Key Type | +Created | +
---|---|---|---|---|
API Key | +GR234-We34 | +greR-234-fEG | +Type | +2024-01-22 | +