diff --git a/Database/model/apiLog.js b/Database/model/apiLog.js index 91cb51c..fd7fcb9 100644 --- a/Database/model/apiLog.js +++ b/Database/model/apiLog.js @@ -1,62 +1,125 @@ "use strict"; const { Sequelize, DataTypes } = require("sequelize"); const { sequelize } = require("../mySQL"); - +const { + isValidDateString, +} = require("../../Web-Server/functions/validateData"); //sequelize.sync(); -const api_log_Model = sequelize.define("api-logs",{ +const api_log_Model = sequelize.define( + "api-logs", + { // Model attributes are defined here id: { type: DataTypes.INTEGER, allowNull: true, primaryKey: true, + autoIncrement: true, + validator: { + isNumeric: true, + }, }, - ip:{ - type: DataTypes.STRING, - allowNull: false, - length: 45, - }, - time: { - type: DataTypes.STRING, - allowNull: false, - length: 20, - }, - method: { - type: DataTypes.STRING, - allowNull: false, - length: 10, - }, - host: { - type: DataTypes.STRING, - allowNull: false, - length: 45, - }, - statusCode: { - type: DataTypes.STRING, - allowNull: false, - length: 10, - }, - Responsesize: { - type: DataTypes.STRING, - allowNull: false, - length: 10, - }, - referrer: { - type: DataTypes.STRING, - allowNull: false, - length: 45, - }, - userAgent: { - type: DataTypes.STRING, - allowNull: false, - length: 100, - }, - }, + ip: { + type: DataTypes.STRING, + allowNull: false, + length: 45, + validator: { + isIP: true, + }, + }, + time: { + type: DataTypes.STRING, + allowNull: false, + length: 20, + validator: { + //validate time: new Date().toUTCString(), + isValidDateString(value) { + if (!isValidDateString(value)) { + throw new Error("Time must be a valid date string"); + } + }, + notEmpty: { msg: "Time cannot be empty" }, + len: [1, 20], + }, + }, + method: { + type: DataTypes.STRING, + allowNull: false, + length: 10, + validator: { + isIn: [["GET", "POST", "PUT", "DELETE"]], + }, + }, + host: { + type: DataTypes.STRING, + allowNull: false, + length: 45, + validator: { + //http://localhost/api/test + //remember to add domain name to the list + isIn: [["localhost"]], + //isFqdn: true, + }, + }, + statusCode: { + type: DataTypes.STRING, + allowNull: false, + length: 10, + validator: { + isNumeric: true, + len: [1, 3], + }, + }, + Responsesize: { + type: DataTypes.STRING, + allowNull: false, + length: 10, + validator: { + isNumeric: true, + len: [1, 100], + }, + }, + referrer: { + type: DataTypes.STRING, + allowNull: false, + length: 45, + validator: { + isString(value) { + if (typeof value !== "string") { + throw new Error("Referrer must be a string"); + } + }, + notEmpty: { msg: "Referrer cannot be empty" }, + len: [1, 45], // Length between 1 and 45 characters + }, + }, + userAgent: { + type: DataTypes.STRING, + allowNull: false, + length: 100, + validator: { + isString(value) { + if (typeof value !== "string") { + throw new Error("UserAgent must be a string"); + } + }, + notEmpty: { msg: "UserAgent cannot be empty" }, + len: [1, 100], // Length between 1 and 100 characters + }, + }, + createdAt: { + type: DataTypes.DATE, + allowNull: true, + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: true, + }, + }, + { - timestamps: false, + timestamps: true, } ); - - module.exports = { api_log_Model }; diff --git a/Database/model/locationModel.js b/Database/model/locationModel.js index d0381c7..b8b712c 100644 --- a/Database/model/locationModel.js +++ b/Database/model/locationModel.js @@ -11,21 +11,60 @@ const locationModel = sequelize.define( allowNull: true, primaryKey: true, autoIncrement: true, + validator: { + isNumeric: true, + }, }, name: { type: DataTypes.STRING, allowNull: false, length: 10, + validator: { + notEmpty: { msg: "Name cannot be empty" }, + len: [1, 20], + /* + //will not validate this and fail it + "hello world" (contains a space) + "hello@123" (contains a symbol) + "" (empty string) + + */ + is: ["^[a-z0-9]+$", "i"] + }, }, added_by: { type: DataTypes.STRING, allowNull: false, length: 10, + validator: { + notEmpty: { msg: "Added by cannot be empty" }, + len: [1, 20], + /* + //will not validate this and fail it + "hello world" (contains a space) + "hello@123" (contains a symbol) + "" (empty string) + + */ + is: ["^[a-z0-9]+$", "i"] + }, }, description: { type: DataTypes.STRING, allowNull: true, length: 100, + validator: { + notEmpty: { msg: "Description cannot be empty" }, + len: [1, 100], + /* + //will not validate this and fail it + "hello world" (contains a space) + "hello@123" (contains a symbol) + "" (empty string) + + */ + is: ["^[a-z0-9]+$", "i"] + }, }, createdAt: { type: DataTypes.DATE, diff --git a/Database/model/sensorDataModel.js b/Database/model/sensorDataModel.js index ef1862d..419c844 100644 --- a/Database/model/sensorDataModel.js +++ b/Database/model/sensorDataModel.js @@ -5,38 +5,39 @@ const { locationModel } = require("./locationModel"); const { sensorModel } = require("./sensorModel"); //sequelize.sync(); -const sensorDataModel = sequelize.define("sensorData", +const sensorDataModel = sequelize.define( + "sensorData", { id: { type: DataTypes.INTEGER, allowNull: true, primaryKey: true, - autoIncrement: true, + autoIncrement: true, + }, + id_sensor: { + type: DataTypes.INTEGER, + allowNull: false, + length: 100, + //FK + references: { + model: sensorModel, + key: "id", + }, + }, + id_location: { + type: DataTypes.INTEGER, + allowNull: false, + length: 100, + //FK + references: { + model: locationModel, + key: "id", + }, + }, + sensordata: { + type: DataTypes.JSON, + allowNull: false, }, - id_sensor: { - type: DataTypes.INTEGER, - allowNull: false, - length: 100, - //FK - references: { - model: sensorModel, - key: 'id' - } - }, - id_location: { - type: DataTypes.INTEGER, - allowNull: false, - length: 100, - //FK - references: { - model: locationModel, - key: 'id' - } - }, - Sensordata: { - type: DataTypes.JSON, - allowNull: false, - }, createdAt: { type: DataTypes.DATE, allowNull: true, diff --git a/Database/model/sensorModel.js b/Database/model/sensorModel.js index fdbd4c9..0bdda0a 100644 --- a/Database/model/sensorModel.js +++ b/Database/model/sensorModel.js @@ -12,7 +12,7 @@ const sensorModel = sequelize.define("sensors", primaryKey: true, autoIncrement: true, }, - sensortype: { + sensorname: { type: DataTypes.STRING, allowNull: false, length: 10, @@ -22,6 +22,11 @@ const sensorModel = sequelize.define("sensors", allowNull: false, length: 10, }, + mac_address: { + type: DataTypes.STRING, + allowNull: false, + length: 12, + }, description: { type: DataTypes.STRING, allowNull: true, diff --git a/Web-Server/functions/APIDatabase.js b/Web-Server/functions/APIDatabase.js index b20f2f1..5992ac2 100644 --- a/Web-Server/functions/APIDatabase.js +++ b/Web-Server/functions/APIDatabase.js @@ -2,6 +2,8 @@ const { sequelize } = require("../../Database/mySql.js"); const { IoTModel } = require("../../Database/model/IoTModel.js"); const { locationModel } = require("../../Database/model/locationModel.js"); const { sensorModel } = require("../../Database/model/sensorModel.js"); +const { sensorDataModel } = require("../../Database/model/sensorDataModel.js"); + async function getLocation() { try { @@ -9,7 +11,6 @@ async function getLocation() { return location; } catch (error) { console.error(error); - return null; } } @@ -22,7 +23,6 @@ async function addLocation(name, added_by, description) { }); } catch (error) { console.error(error); - return null; } } @@ -43,7 +43,6 @@ async function updateLocation(id, name, added_by, description) { ); } catch (error) { console.error(error); - return null; } } @@ -57,7 +56,6 @@ async function deleteLocation(id) { }); } catch (error) { console.error(error); - return null; } } @@ -71,7 +69,6 @@ async function getLocationById(id) { return location; } catch (error) { console.error(error); - return null; } } @@ -81,31 +78,31 @@ async function getSensor() { return sensor; } catch (error) { console.error(error); - return null; } } -async function addSensor(sensortype, added_by, description, location) { +async function addSensor(sensorname, added_by, mac_address , description, location) { try { const sensor = await sensorModel.create({ - sensortype: sensortype, + sensorname: sensorname, added_by: added_by, + mac_address: mac_address, description: description, location: location, }); } catch (error) { console.error(error); - return null; } } -async function updateSensor(id, sensortype, added_by, description, location) { +async function updateSensor(id, sensorname, added_by, mac_address ,description, location) { try { //update by id const sensor = await sensorModel.update( { - sensortype: sensortype, + sensorname: sensorname, added_by: added_by, + mac_address: mac_address, description: description, location: location, @@ -118,7 +115,6 @@ async function updateSensor(id, sensortype, added_by, description, location) { ); } catch (error) { console.error(error); - return null; } } @@ -133,7 +129,6 @@ async function deleteSensor(id) { }); } catch (error) { console.error(error); - return null; } } @@ -147,34 +142,88 @@ async function getSensorById(id) { return sensor; } catch (error) { console.error(error); - return null; } } +async function getSensorData() { + try { + const sensorData = await sensorDataModel.findAll(); + return sensorData; + } catch (error) { + console.error(error); + } +} + +async function addSensorData(id , id_sensor , id_location , sensordata){ + try{ + console.log(typeof sensordata); + console.log(sensordata); + if (!sensordata){ + console.log("Sensor Data is null"); + } + const sensorData = await sensorDataModel.create({ + id: id, + id_sensor: id_sensor, + id_location: id_location, + sensordata: sensordata, + }); + }catch(error){ + console.error(error); + } + +} + +async function updateSensorData(id, id_sensor, id_location, sensordata) { + try { + const sensorData = await sensorDataModel.update( + { + id_sensor: id_sensor, + id_location: id_location, + sensordata: sensordata, + }, + { + where: { + id: id, + }, + } + ); + } catch (error) { + console.error(error); + } + +} + +async function deleteSensorData(id) { + try { + const sensorData = await sensorDataModel.destroy({ + where: { + id: id, + }, + }); + } catch (error) { + console.error(error); + } +} + +async function getSensorDataById(id) { + try { + const sensorData = await sensorDataModel.findAll({ + where: { + id: id, + }, + }); + return sensorData; + } catch (error) { + console.error(error); + } +} async function getallData() { try { - const allData = await IoTModel.findAll({ - attributes: [ - "id", - "psiData", - "humidityData", - "o3Data", - "no2Data", - "so2Data", - "coData", - "temperatureData", - "windspeedData", - "currentTime", - "regionData", - "createdAt", - "updatedAt", - ], - }); + const allData = await IoTModel.findAll(); return allData; } catch (error) { console.error(error); - return null; } } @@ -187,7 +236,6 @@ async function getLatestData() { return latestData; } catch (error) { console.error(error); - return null; } } @@ -204,4 +252,9 @@ module.exports = { updateSensor, deleteSensor, getSensorById, + getSensorData, + addSensorData, + updateSensorData, + deleteSensorData, + getSensorDataById, }; diff --git a/Web-Server/functions/validateData.js b/Web-Server/functions/validateData.js index d26fcd4..8cd0e17 100644 --- a/Web-Server/functions/validateData.js +++ b/Web-Server/functions/validateData.js @@ -22,4 +22,11 @@ function validateData(data) { ); } -module.exports = { validateData }; +const dateRegex = /^[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT$/; + +function isValidDateString(){ + return dateRegex.test(value); +} + + +module.exports = { validateData , isValidDateString }; diff --git a/Web-Server/modules/express.js b/Web-Server/modules/express.js index 0f4c8ab..8c7057c 100644 --- a/Web-Server/modules/express.js +++ b/Web-Server/modules/express.js @@ -15,11 +15,11 @@ app.disable("x-powered-by"); app.use(express.json()); app.set("json spaces", 2); -//const { APIlogger } = require('../middleware/ApiLogger.js'); +const { APIlogger } = require('../middleware/ApiLogger.js'); -//middleware logic +//middleware logic ( called by next() ) //app.use('/api/v0', require('../middleware/ApiKey.js')); -//app.use('/api/v0', APIlogger, require('../routes/api_route.js')); +app.use('/api/v0', APIlogger, require('../routes/api_route.js')); //route logic app.use("/api/v0", require("../routes/api_route.js")); @@ -41,13 +41,20 @@ app.use(function (err, req, res, next) { console.error(err.stack); console.error("========================================="); } + //validation error + if (err.name === "SequelizeValidationError") { + err.status = 400; + err.message = "Validation Error"; + } + else if (err.name === "SequelizeUniqueConstraintError") + { + console.log("this is my custom error!" + err); + } res.status(err.status || 500); res.json({ name: err.name, message: err.message, - runner: err.runner && err.runner.name, - duration: err.duration, }); }); app.listen(port, () => { diff --git a/Web-Server/routes/Sensor.js b/Web-Server/routes/Sensor.js index 2b034ab..a031d1e 100644 --- a/Web-Server/routes/Sensor.js +++ b/Web-Server/routes/Sensor.js @@ -23,8 +23,8 @@ router.get("/", async (req, res, next) => { router.post("/new", async (req, res, next) => { try { - const { sensortype, added_by, description, location } = req.body; - await addSensor(sensortype, added_by, description, location); + const { sensorname, added_by, mac_address , description, location } = req.body; + await addSensor(sensorname, added_by, mac_address ,description, location); } catch (error) { console.error(error); next(error); @@ -33,8 +33,8 @@ router.post("/new", async (req, res, next) => { router.put("/update", async (req, res, next) => { try { - const { id, sensortype, added_by, description, location } = req.body; - await updateSensor(id, sensortype, added_by, description, location); + const { id, sensorname, added_by, mac_address ,description, location } = req.body; + await updateSensor(id, sensorname, added_by, mac_address , description, location); } catch (error) { console.error(error); next(error); diff --git a/Web-Server/routes/SensorData.js b/Web-Server/routes/SensorData.js new file mode 100644 index 0000000..d56f53c --- /dev/null +++ b/Web-Server/routes/SensorData.js @@ -0,0 +1,68 @@ +const { sequelize } = require("../../Database/mySql.js"); +const { sensorDataModel } = require("../../Database/model/sensorDataModel.js"); +const { + getSensorData, + addSensorData, + updateSensorData, + deleteSensorData, + getSensorDataById, + +} = require("../functions/APIDatabase.js"); + +const express = require("express"); +const { json } = require("body-parser"); +const router = express.Router(); + +router.get("/", async (req, res, next) => { + try { + const sensor = await getSensorData(); + res.json(sensor); + } catch (error) { + console.error(error); + next(error); + } +}); + +router.post("/new", async (req, res, next) => { + try { + //JSON.parse(d) /* d is the parameter of the method 'add()' */ + + const { id, id_sensor, id_location, sensordata } = req.body; + await addSensorData(id , id_sensor , id_location , sensordata); + } catch (error) { + console.error(error); + next(error); + } +}); + +router.put("/update", async (req, res, next) => { + try { + const { id , id_sensor , id_location , sensordata } = req.body; + await updateSensorData( id , id_sensor , id_location , sensordata ); + } catch (error) { + console.error(error); + next(error); + } +}); + +router.delete("/delete", async (req, res, next) => { + try { + const { id } = req.body; + await deleteSensorData(id); + } catch (error) { + console.error(error); + next(error); + } +}); + +router.get("/:id", async (req, res, next) => { + try { + const sensor = await getSensorDataById(req.params.id); + res.json(sensor); + } catch (error) { + console.error(error); + next(error); + } +}); + +module.exports = router; diff --git a/Web-Server/routes/api_route.js b/Web-Server/routes/api_route.js index 898d228..6f8428f 100644 --- a/Web-Server/routes/api_route.js +++ b/Web-Server/routes/api_route.js @@ -23,12 +23,14 @@ router.use('/location', require('./Location')); //sensor route router.use('/sensor', require('./Sensor')) +//sensor data route +router.use('/sensor-data', require('./SensorData')); + router.use('/test' , require('./test')); router.use('/latest-data', require('./latest-data')); -router.use('/:month', require('./monthlyData')); diff --git a/Web-Server/routes/monthlyData.js b/Web-Server/routes/monthlyData.js deleted file mode 100644 index 7c7b80e..0000000 --- a/Web-Server/routes/monthlyData.js +++ /dev/null @@ -1,21 +0,0 @@ -const { sequelize } = require("../../Database/mySql.js"); -const { IoTModel } = require("../../Database/model/IoTModel.js"); -const { getallData } = require("../functions/APIDatabase.js"); - -const express = require('express'); -const router = express.Router(); - -router.get('/', async (req, res) => { - try { - //get month from url - console.log(req.params.month); - - - - } catch (error) { - console.error(error); - } - }); - - // Export the router - module.exports = router; \ No newline at end of file diff --git a/api.MD b/api.MD index b1ce947..7fe886d 100644 --- a/api.MD +++ b/api.MD @@ -1,33 +1,70 @@ //location //get all -curl localhost/api/v0/location +curl localhost/api/v0/location -//get id -http://localhost/api/v0/location/3 +//get id +curl http://localhost/api/v0/location/3 //post curl localhost/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "test", "added_by": "noot" , "description": "test"}' -//put - curl localhost/api/v0/location/update -X PUT -H "Content-Type: application/json" -d '{"id": "2" , "name": "test", "added_by": "noot", "description": "test12344444444"}' +//put +curl localhost/api/v0/location/update -X PUT -H "Content-Type: application/json" -d '{"id": "2" , "name": "test", "added_by": "noot", "description": "test12344444444"}' -//delete +//delete curl localhost/api/v0/location/delete -X DELETE -H "Content-Type: application/json" -d '{"id": "6" }' -//sensor +//sensor //GET all curl localhost/api/v0/sensor -//get id +//get id curl http://localhost/api/v0/sensor/3 //POST - curl localhost/api/v0/sensor/new -H "Content-Type: application/json" -X POST -d '{"sensortype": "test", "added_by": "noot" , "description": "test" , "location": "2"}' +curl localhost/api/v0/sensor/new -H "Content-Type: application/json" -X POST -d '{"sensorname": "test", "added_by": "noot" , "mac_address": "99-6A-F8-7D-B4-90", "description": "test" , "location": "2"}' - //put - curl localhost/api/v0/sensor/update -H "Content-Type: application/json" -X PUT -d '{"id": "2" ,"sensortype": "test", "added_by": "noot" , "description": "test123333333" , "location": "3" }' +//put +curl localhost/api/v0/sensor/update -H "Content-Type: application/json" -X PUT -d '{"id": "2" ,"sensorname": "test", "added_by": "noot" , "mac_address": "99-6A-F8-7D-B4-90" , "description": "test123333333" , "location": "3" }' //delete curl localhost/api/v0/sensor/delete -X DELETE -H "Content-Type: application/json" -d '{"id": "2" }' //sensor data +curl http://localhost/api/v0/sensor-data/ + +//sensor data by id +curl http://localhost/api/v0/sensor-data/1 + + +//post +curl localhost/api/v0/sensor-data/new -H "Content-Type: application/json" -X POST -d '{"id": "2", "id_sensor": "1" , "id_location": "2" , "sensordata": { + "psi": "34", + "humidity": "11%", + "o3": "326ppm", + "no2": "445ppm", + "so2": "511ppm", + "co": "16ppm", + "temperature": "25C", + "windspeed": "2km/h", + "time": "2023-12-21 14:24:44", + "region": "east" +}}' + +//put +curl localhost/api/v0/sensor-data/update -H "Content-Type: application/json" -X PUT -d '{"id": "1", "id_sensor": "1" , "id_location": "3" , "sensordata": { + "psi": "500", + "humidity": "11%", + "o3": "326ppm", + "no2": "445ppm", + "so2": "511ppm", + "co": "16ppm", + "temperature": "25C", + "windspeed": "2km/h", + "time": "2023-12-21 14:24:44", + "region": "east" +}}' + +//delete +curl localhost/api/v0/sensor-data/delete -X DELETE -H "Content-Type: application/json" -d '{"id": "3" }' + diff --git a/schema/adminusers_iot-data.sql b/schema/adminusers_iot-data.sql deleted file mode 100644 index 9121e81..0000000 --- a/schema/adminusers_iot-data.sql +++ /dev/null @@ -1,62 +0,0 @@ --- MySQL dump 10.13 Distrib 8.0.27, for Win64 (x86_64) --- --- Host: mpsqldatabasean.mysql.database.azure.com Database: adminusers --- ------------------------------------------------------ --- Server version 8.0.34 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!50503 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `iot-data` --- - -DROP TABLE IF EXISTS `iot-data`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `iot-data` ( - `id` int NOT NULL AUTO_INCREMENT, - `psiData` varchar(8) NOT NULL, - `humidityData` varchar(8) NOT NULL, - `o3Data` varchar(8) NOT NULL, - `no2Data` varchar(8) NOT NULL, - `so2Data` varchar(8) NOT NULL, - `coData` varchar(8) NOT NULL, - `temperatureData` varchar(8) NOT NULL, - `windspeedData` varchar(8) NOT NULL, - `currentTime` varchar(20) NOT NULL, - `regionData` varchar(10) NOT NULL, - `createdAt` varchar(20) DEFAULT NULL, - `updatedAt` varchar(20) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `iot-data` --- - -LOCK TABLES `iot-data` WRITE; -/*!40000 ALTER TABLE `iot-data` DISABLE KEYS */; -INSERT INTO `iot-data` VALUES (1,'426','9%','231ppm','812ppm','653ppm','39ppm','25°C','34km/h','2023-12-19 16:52:38','central','2023-12-19 18:00:00',''); -/*!40000 ALTER TABLE `iot-data` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2023-12-27 21:22:58