backend validation wip

This commit is contained in:
newtbot 2024-01-02 02:53:44 +08:00
parent a058b1ba70
commit 6342d5a4cb
13 changed files with 410 additions and 211 deletions

View File

@ -1,62 +1,125 @@
"use strict"; "use strict";
const { Sequelize, DataTypes } = require("sequelize"); const { Sequelize, DataTypes } = require("sequelize");
const { sequelize } = require("../mySQL"); const { sequelize } = require("../mySQL");
const {
isValidDateString,
} = require("../../Web-Server/functions/validateData");
//sequelize.sync(); //sequelize.sync();
const api_log_Model = sequelize.define("api-logs",{ const api_log_Model = sequelize.define(
"api-logs",
{
// Model attributes are defined here // Model attributes are defined here
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true, allowNull: true,
primaryKey: true, primaryKey: true,
autoIncrement: true,
validator: {
isNumeric: true,
},
}, },
ip:{ ip: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
length: 45, length: 45,
}, validator: {
time: { isIP: true,
type: DataTypes.STRING, },
allowNull: false, },
length: 20, time: {
}, type: DataTypes.STRING,
method: { allowNull: false,
type: DataTypes.STRING, length: 20,
allowNull: false, validator: {
length: 10, //validate time: new Date().toUTCString(),
}, isValidDateString(value) {
host: { if (!isValidDateString(value)) {
type: DataTypes.STRING, throw new Error("Time must be a valid date string");
allowNull: false, }
length: 45, },
}, notEmpty: { msg: "Time cannot be empty" },
statusCode: { len: [1, 20],
type: DataTypes.STRING, },
allowNull: false, },
length: 10, method: {
}, type: DataTypes.STRING,
Responsesize: { allowNull: false,
type: DataTypes.STRING, length: 10,
allowNull: false, validator: {
length: 10, isIn: [["GET", "POST", "PUT", "DELETE"]],
}, },
referrer: { },
type: DataTypes.STRING, host: {
allowNull: false, type: DataTypes.STRING,
length: 45, allowNull: false,
}, length: 45,
userAgent: { validator: {
type: DataTypes.STRING, //http://localhost/api/test
allowNull: false, //remember to add domain name to the list
length: 100, 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 }; module.exports = { api_log_Model };

View File

@ -11,21 +11,60 @@ const locationModel = sequelize.define(
allowNull: true, allowNull: true,
primaryKey: true, primaryKey: true,
autoIncrement: true, autoIncrement: true,
validator: {
isNumeric: true,
},
}, },
name: { name: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
length: 10, 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: { added_by: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
length: 10, 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: { description: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,
length: 100, 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: { createdAt: {
type: DataTypes.DATE, type: DataTypes.DATE,

View File

@ -5,38 +5,39 @@ const { locationModel } = require("./locationModel");
const { sensorModel } = require("./sensorModel"); const { sensorModel } = require("./sensorModel");
//sequelize.sync(); //sequelize.sync();
const sensorDataModel = sequelize.define("sensorData", const sensorDataModel = sequelize.define(
"sensorData",
{ {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true, allowNull: true,
primaryKey: 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: { createdAt: {
type: DataTypes.DATE, type: DataTypes.DATE,
allowNull: true, allowNull: true,

View File

@ -12,7 +12,7 @@ const sensorModel = sequelize.define("sensors",
primaryKey: true, primaryKey: true,
autoIncrement: true, autoIncrement: true,
}, },
sensortype: { sensorname: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
length: 10, length: 10,
@ -22,6 +22,11 @@ const sensorModel = sequelize.define("sensors",
allowNull: false, allowNull: false,
length: 10, length: 10,
}, },
mac_address: {
type: DataTypes.STRING,
allowNull: false,
length: 12,
},
description: { description: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: true,

View File

@ -2,6 +2,8 @@ const { sequelize } = require("../../Database/mySql.js");
const { IoTModel } = require("../../Database/model/IoTModel.js"); const { IoTModel } = require("../../Database/model/IoTModel.js");
const { locationModel } = require("../../Database/model/locationModel.js"); const { locationModel } = require("../../Database/model/locationModel.js");
const { sensorModel } = require("../../Database/model/sensorModel.js"); const { sensorModel } = require("../../Database/model/sensorModel.js");
const { sensorDataModel } = require("../../Database/model/sensorDataModel.js");
async function getLocation() { async function getLocation() {
try { try {
@ -9,7 +11,6 @@ async function getLocation() {
return location; return location;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -22,7 +23,6 @@ async function addLocation(name, added_by, description) {
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -43,7 +43,6 @@ async function updateLocation(id, name, added_by, description) {
); );
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -57,7 +56,6 @@ async function deleteLocation(id) {
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -71,7 +69,6 @@ async function getLocationById(id) {
return location; return location;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -81,31 +78,31 @@ async function getSensor() {
return sensor; return sensor;
} catch (error) { } catch (error) {
console.error(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 { try {
const sensor = await sensorModel.create({ const sensor = await sensorModel.create({
sensortype: sensortype, sensorname: sensorname,
added_by: added_by, added_by: added_by,
mac_address: mac_address,
description: description, description: description,
location: location, location: location,
}); });
} catch (error) { } catch (error) {
console.error(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 { try {
//update by id //update by id
const sensor = await sensorModel.update( const sensor = await sensorModel.update(
{ {
sensortype: sensortype, sensorname: sensorname,
added_by: added_by, added_by: added_by,
mac_address: mac_address,
description: description, description: description,
location: location, location: location,
@ -118,7 +115,6 @@ async function updateSensor(id, sensortype, added_by, description, location) {
); );
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -133,7 +129,6 @@ async function deleteSensor(id) {
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -147,34 +142,88 @@ async function getSensorById(id) {
return sensor; return sensor;
} catch (error) { } catch (error) {
console.error(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() { async function getallData() {
try { try {
const allData = await IoTModel.findAll({ const allData = await IoTModel.findAll();
attributes: [
"id",
"psiData",
"humidityData",
"o3Data",
"no2Data",
"so2Data",
"coData",
"temperatureData",
"windspeedData",
"currentTime",
"regionData",
"createdAt",
"updatedAt",
],
});
return allData; return allData;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -187,7 +236,6 @@ async function getLatestData() {
return latestData; return latestData;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return null;
} }
} }
@ -204,4 +252,9 @@ module.exports = {
updateSensor, updateSensor,
deleteSensor, deleteSensor,
getSensorById, getSensorById,
getSensorData,
addSensorData,
updateSensorData,
deleteSensorData,
getSensorDataById,
}; };

View File

@ -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 };

View File

@ -15,11 +15,11 @@ app.disable("x-powered-by");
app.use(express.json()); app.use(express.json());
app.set("json spaces", 2); 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', 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 //route logic
app.use("/api/v0", require("../routes/api_route.js")); 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(err.stack);
console.error("========================================="); 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.status(err.status || 500);
res.json({ res.json({
name: err.name, name: err.name,
message: err.message, message: err.message,
runner: err.runner && err.runner.name,
duration: err.duration,
}); });
}); });
app.listen(port, () => { app.listen(port, () => {

View File

@ -23,8 +23,8 @@ router.get("/", async (req, res, next) => {
router.post("/new", async (req, res, next) => { router.post("/new", async (req, res, next) => {
try { try {
const { sensortype, added_by, description, location } = req.body; const { sensorname, added_by, mac_address , description, location } = req.body;
await addSensor(sensortype, added_by, description, location); await addSensor(sensorname, added_by, mac_address ,description, location);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
next(error); next(error);
@ -33,8 +33,8 @@ router.post("/new", async (req, res, next) => {
router.put("/update", async (req, res, next) => { router.put("/update", async (req, res, next) => {
try { try {
const { id, sensortype, added_by, description, location } = req.body; const { id, sensorname, added_by, mac_address ,description, location } = req.body;
await updateSensor(id, sensortype, added_by, description, location); await updateSensor(id, sensorname, added_by, mac_address , description, location);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
next(error); next(error);

View File

@ -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;

View File

@ -23,12 +23,14 @@ router.use('/location', require('./Location'));
//sensor route //sensor route
router.use('/sensor', require('./Sensor')) router.use('/sensor', require('./Sensor'))
//sensor data route
router.use('/sensor-data', require('./SensorData'));
router.use('/test' , require('./test')); router.use('/test' , require('./test'));
router.use('/latest-data', require('./latest-data')); router.use('/latest-data', require('./latest-data'));
router.use('/:month', require('./monthlyData'));

View File

@ -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;

47
api.MD
View File

@ -3,13 +3,13 @@
curl localhost/api/v0/location curl localhost/api/v0/location
//get id //get id
http://localhost/api/v0/location/3 curl http://localhost/api/v0/location/3
//post //post
curl localhost/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "test", "added_by": "noot" , "description": "test"}' curl localhost/api/v0/location/new -H "Content-Type: application/json" -X POST -d '{"name": "test", "added_by": "noot" , "description": "test"}'
//put //put
curl localhost/api/v0/location/update -X PUT -H "Content-Type: application/json" -d '{"id": "2" , "name": "test", "added_by": "noot", "description": "test12344444444"}' 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" }' curl localhost/api/v0/location/delete -X DELETE -H "Content-Type: application/json" -d '{"id": "6" }'
@ -22,12 +22,49 @@ curl localhost/api/v0/sensor
curl http://localhost/api/v0/sensor/3 curl http://localhost/api/v0/sensor/3
//POST //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 //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" }' 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 //delete
curl localhost/api/v0/sensor/delete -X DELETE -H "Content-Type: application/json" -d '{"id": "2" }' curl localhost/api/v0/sensor/delete -X DELETE -H "Content-Type: application/json" -d '{"id": "2" }'
//sensor data //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" }'

View File

@ -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