diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..3f047cd --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +node_modules +dist +*.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..ac5a94d --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,16 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@typescript-eslint/no-var-requires": 0 + } + + } \ No newline at end of file diff --git a/.gitignore b/.gitignore index 93040c6..3360f4e 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,4 @@ typings/ testingDONOTCOMMITME.json static/uploads/* +index.js diff --git a/index.js b/index.js index cce3aee..77458fc 100644 --- a/index.js +++ b/index.js @@ -1,49 +1,93 @@ // Imports -const express = require("express"); -const fs = require("fs"); -const Eta = require("eta"); -const winston = require("winston"); -const mysql = require("mysql"); -const bodyParser = require("body-parser"); -const bcrypt = require("bcrypt"); -const { verify } = require("hcaptcha"); -const csp = require(`helmet`); -const session = require("express-session"); -const nodemailer = require("nodemailer"); -const crypto = require("crypto"); -const multer = require("multer"); -const path = require("path"); - -const upload = multer({ dest: "static/uploads/" }); - +var express = require("express"); +var fs = require("fs"); +var Eta = require("eta"); +var winston = require("winston"); +var mysql = require("mysql"); +var bodyParser = require("body-parser"); +// const csp = require(`helmet`); +var session = require("express-session"); +var nodemailer = require("nodemailer"); // Inting the logger -const logger = winston.createLogger({ - level: "debug", - format: winston.format.json(), - defaultMeta: { service: "user-service" }, - transports: [ - // - // - Write all logs with level `error` and below to `error.log` - // - Write all logs with level `info` and below to `combined.log` - // - new winston.transports.File({ filename: "error.log", level: "error" }), - new winston.transports.File({ filename: "combined.log" }), - ], +var logger = winston.createLogger({ + level: "debug", + format: winston.format.json(), + defaultMeta: { service: "user-service" }, + transports: [ + // + // - Write all logs with level `error` and below to `error.log` + // - Write all logs with level `info` and below to `combined.log` + // + new winston.transports.File({ filename: "error.log", level: "error" }), + new winston.transports.File({ filename: "combined.log" }), + ] }); - -logger.add( - new winston.transports.Console({ - format: winston.format.simple(), - }) -); - -const app = express(); - +logger.add(new winston.transports.Console({ + format: winston.format.simple() +})); +var app = express(); app.use(express.static("static")); - app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); - +app.use(function (req, res, next) { + var pathesWhichRequireDB = ["map", "login", "register"]; + var pathesWhichRequireLogin = ["createElevator"]; + var path = req.path; + var pathesDes = path.split("/"); + var requiresDB = false; + var requiresLogin = false; + var allowContinue = true; + console.log(pathesDes); + if (pathesWhichRequireLogin.indexOf(pathesDes[1]) > -1) { + requiresLogin = true; + } + if (pathesDes[1] == "api") { + requiresDB = true; + } + if (pathesWhichRequireDB.indexOf(pathesDes[1]) > -1) { + requiresDB = true; + } + if (requiresDB) { + if (!mysqlIsUpAndOkay) { + allowContinue = false; + var data = fs.readFileSync("templates/dbError.html", "utf8"); + var displayText = "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; + if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { + displayText = + "The server failed to connect to the MySQL server. This means it was unable to load any data."; + } + if (mySQLstate == 1) { + displayText = + "There is a problem with the database servers setup. Please check the log for more info."; + } + res.send(Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Error", + fontawesomeKey: fontawesomeKey, + displayText: displayText + })); + } + } + if (requiresLogin) { + allowContinue = false; + var data = fs.readFileSync("templates/redirect.html", "utf8"); + res.send(Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Redirect", + fontawesomeKey: fontawesomeKey, + url: "/login?r=" + path + })); + } + console.log('Time:', Date.now()); + if (allowContinue) { + next(); + } + else { + console.log("Stopped further exec of route"); + } +}); /* app.use(csp.contentSecurityPolicy({ useDefaults: true, @@ -62,1138 +106,198 @@ app.use(csp.contentSecurityPolicy({ })) */ - // Settings -const port = 3000; -const startUpTime = Math.floor(new Date().getTime() / 1000); -const saltRounds = 10; - +var port = 3000; +var startUpTime = Math.floor(new Date().getTime() / 1000); +var fontawesomeKey = ""; +var mapboxAccessToken = ""; +var mysqlData = { "user": "", "password": "", "database": "", "allowCreation": false }; +var hCaptcha = { "sitekey": "", "secret": "" }; +var mailConf = { "host": "", "port": 0, "username": "", "password": "" }; +var serverAdress = ""; +var cookieSecret = ""; +var jsonConfigGlobal = {}; // Load config try { - const data = fs.readFileSync("config/default.json", "utf8"); - const jsonContent = JSON.parse(data); - let jsonConfig = jsonContent; - if (jsonContent.redirectConfig) { - const data = fs.readFileSync( - "config/" + jsonContent.redirectConfig, - "utf8" - ); - jsonConfig = JSON.parse(data); - } - var fontawesomeKey = jsonConfig.fontAwesome; - var mapboxAccessToken = jsonConfig.mapboxAccessToken; - var mysqlData = jsonConfig.mysql; - var hCaptcha = jsonConfig.hCaptcha; - var mailConf = jsonConfig.mail; - var serverAdress = jsonConfig.serverAdress; - var cookieSecret = - jsonConfig.cookieSecret || "saF0DSF65AS4DF0S4D6F0S54DF0Fad"; -} catch (error) { - logger.error( - "While reading the config an error occured. The error was: " + error - ); + var data = fs.readFileSync("config/default.json", "utf8"); + var jsonContent = JSON.parse(data); + var jsonConfig = jsonContent; + if (jsonContent.redirectConfig) { + var data_1 = fs.readFileSync("config/" + jsonContent.redirectConfig, "utf8"); + jsonConfig = JSON.parse(data_1); + } + fontawesomeKey = jsonConfig.fontAwesome; + mapboxAccessToken = jsonConfig.mapboxAccessToken; + mysqlData = jsonConfig.mysql; + mailConf = jsonConfig.mail; + serverAdress = jsonConfig.serverAdress; + cookieSecret = + jsonConfig.cookieSecret || "saF0DSF65AS4DF0S4D6F0S54DF0Fad"; + jsonConfigGlobal = jsonConfig; +} +catch (error) { + logger.error("While reading the config an error occured. The error was: " + error); } - var transport = nodemailer.createTransport({ - host: mailConf.host, - port: mailConf.port, - requireTLS: true, - secure: false, - debug: true, - disableFileAccess: true, - //authMethod: "START TLS", - auth: { - user: mailConf.username, - pass: mailConf.password, - }, + host: mailConf.host, + port: mailConf.port, + requireTLS: true, + secure: false, + debug: true, + disableFileAccess: true, + //authMethod: "START TLS", + auth: { + user: mailConf.username, + pass: mailConf.password + } }); - //let transporter = nodemailer.createTransport(transport) //console.log(transport.host) logger.info("Testing SMTP connection"); -transport.verify(function (error, success) { - if (error) { - logger.error(error); - } else { - logger.info("SMPT server is ready to accept messages"); - } +transport.verify(function (error) { + if (error) { + logger.error(error); + } + else { + logger.info("SMPT server is ready to accept messages"); + } }); - app.use(session({ secret: cookieSecret })); - // Basic defines for html -const author = "TheGreydiamond"; -const desc = "The Elevatormap. A map for elevator spotters!"; -const sitePrefix = "Elevatormap - "; -let mysqlIsUpAndOkay = false; -let mySQLstate = 0; // 0 -> Default failure 1 -> Missing strucutre - +var metainfo = { + author: "TheGreydiamond", + desc: "The Elevatormap. A map for elevator spotters!", + sitePrefix: "Elevatormap - " +}; +var mysqlIsUpAndOkay = false; +var mySQLstate = 0; // 0 -> Default failure 1 -> Missing strucutre // Prepare MYSQL var con = mysql.createConnection({ - host: "localhost", - user: mysqlData.user, - password: mysqlData.password, - database: mysqlData.database, + host: "localhost", + user: mysqlData.user, + password: mysqlData.password, + database: mysqlData.database }); - -// sendVerificationMail(2); -function sendVerificationMail(userId) { - // Query for the mail - const stmt = "SELECT * FROM mailverification WHERE id=" + userId; - con.query(stmt, function (err, result, fields) { - if (err) throw err; // TODO proper error handling - if (result.length == 0) { - logger.warn( - "sendVerificationMail failed because ID " + userId + " doesnt exist!" - ); - } else { - const emailContent = - "Hi! \n You have created an account for the open elevator map. To finalize the process please verify your E-Mail adress. Use this link: http://" + - serverAdress + - "/verify/" + - result[0].token; - let info = transport.sendMail({ - from: '"Elevator map " <' + mailConf.username + ">", // sender address - to: result[0].targetMail, // list of receivers - subject: "[Elevator map] Please verify your Mailadress", // Subject line - text: emailContent, // plain text body - html: emailContent.replace("\n", "
"), // html body - }); - } - - console.log(result); - }); - - /* - let info = await transporter.sendMail({ - from: '"Elevator map " <' + mysqlData.username + '>', // sender address - to: "bar@example.com, baz@example.com", // list of receivers - subject: "Hello ✔", // Subject line - text: "Hello world?", // plain text body - html: "Hello world?", // html body - });*/ -} - function checkIfMySQLStructureIsReady() { - if (mysqlIsUpAndOkay) { - // Only if MySQL is ready - logger.debug("Checking MySQL strucutre"); - con.query("SHOW TABLES;", function (err, result, fields) { - if (err) throw err; - if (result.length == 0) { - // There are no tables. Not good. - logger.warn("There are no tables found"); - if (mysqlData.allowCreation) { - // Lets create it then - logger.warn("Creating a new table"); - const sql = - "CREATE TABLE `" + - mysqlData.database + - "`.`elevators` ( `id` INT NOT NULL AUTO_INCREMENT , `lat` FLOAT NOT NULL , `lng` FLOAT NOT NULL , `manufacturer` VARCHAR(512) NOT NULL , `modell` VARCHAR(512) NOT NULL , `info` VARCHAR(512) NOT NULL , `visitabilty` INT NOT NULL , `technology` INT NOT NULL , `images` JSON NOT NULL , `amountOfFloors` INT NOT NULL , `maxPassangers` INT NOT NULL , `maxWeight` INT NOT NULL , `creator` INT NOT NULL, PRIMARY KEY (`id`)) ENGINE = InnoDB;"; - const newSql = - "CREATE TABLE `" + - mysqlData.database + - "`.`users` ( `id` INT NOT NULL AUTO_INCREMENT , `email` VARCHAR(255) NOT NULL , `username` VARCHAR(255) NOT NULL , `passwordHash` VARCHAR(512) NOT NULL , `permLevel` INT NOT NULL DEFAULT '0' , `verificationState` INT NOT NULL DEFAULT '0' , PRIMARY KEY (`id`), UNIQUE KEY (`email`)) ENGINE = InnoDB;"; - const newSqlMailVeri = - "CREATE TABLE `" + - mysqlData.database + - "`.`mailverification` ( `id` INT NOT NULL AUTO_INCREMENT , `targetMail` VARCHAR(512) NOT NULL , `userID` INT NOT NULL , `token` VARCHAR(255) NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;"; - con.query(sql, function (err, result) { - if (err) throw err; - logger.info("Table created"); - }); - - con.query(newSql, function (err, result) { - if (err) throw err; - logger.info("Usertable created"); - }); - - con.query(newSqlMailVeri, function (err, result) { - if (err) throw err; - logger.info("Email verification table created"); - }); - } else { - // We cannot do that. Welp. - logger.warn( - "MySQL tables are missing and the config denies creation of new ones." - ); - mysqlIsUpAndOkay = false; - mySQLstate = 1; - } - } - }); - } else { - logger.warn("Tried checking the tables even though MySQL wasn't ready."); - } -} - -con.connect(function (err) { - if (err) { - mysqlIsUpAndOkay = false; - logger.error("Connction to MySQL failed"); - console.log(err); - } else { - logger.info("Mysql is ready."); - mysqlIsUpAndOkay = true; - checkIfMySQLStructureIsReady(); - } -}); - -// Routes -app.get("/", function (req, res) { - const data = fs.readFileSync("templates/index.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Start", - fontawesomeKey: fontawesomeKey, - }) - ); -}); - -app.post("/login", function (req, res) { - const password = req.body.pass; - const mail = req.body.email; - var sess = req.session; - console.log(req.body.pass); - - // Check if okay - if ( - mail != undefined && - mail != "" && - password != undefined && - password != "" - ) { - const mailRegex = - /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; - if (mailRegex.test(mail)) { - const stmt = "SELECT * FROM users WHERE email='" + mail + "';"; - con.query(stmt, function (err, result, fields) { - if (err) throw err; // TODO proper error page - if (result.length == 0) { - const data = fs.readFileSync("templates/login.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Ok", - fontawesomeKey: fontawesomeKey, - error: true, - errorMessage: "This user does not exist!", - }) - ); - } else { - bcrypt.compare( - password, - result[0].passwordHash, - function (error, response) { - if (response) { - // Login okay - sess.username = result[0].username; - sess.uid = String(result[0].id); - sess.mail = result[0].email; - - const data = fs.readFileSync("templates/redirect.html", "utf8"); - if(req.query.r != undefined && req.query.r != ""){ - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Ok", - fontawesomeKey: fontawesomeKey, - url: req.query.r, - }) - ); - - }else{ - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Ok", - fontawesomeKey: fontawesomeKey, - url: "/profile", - }) - ); + if (mysqlIsUpAndOkay) { + // Only if MySQL is ready + logger.debug("Checking MySQL strucutre"); + con.query("SHOW TABLES;", function (err, result, fields) { + if (err) + throw err; + if (result.length == 0) { + // There are no tables. Not good. + logger.warn("There are no tables found"); + if (mysqlData.allowCreation) { + // Lets create it then + logger.warn("Creating a new table"); + var sql = "CREATE TABLE `" + + mysqlData.database + + "`.`elevators` ( `id` INT NOT NULL AUTO_INCREMENT , `lat` FLOAT NOT NULL , `lng` FLOAT NOT NULL , `manufacturer` VARCHAR(512) NOT NULL , `modell` VARCHAR(512) NOT NULL , `info` VARCHAR(512) NOT NULL , `visitabilty` INT NOT NULL , `technology` INT NOT NULL , `images` JSON NOT NULL , `amountOfFloors` INT NOT NULL , `maxPassangers` INT NOT NULL , `maxWeight` INT NOT NULL , `creator` INT NOT NULL, PRIMARY KEY (`id`)) ENGINE = InnoDB;"; + var newSql = "CREATE TABLE `" + + mysqlData.database + + "`.`users` ( `id` INT NOT NULL AUTO_INCREMENT , `email` VARCHAR(255) NOT NULL , `username` VARCHAR(255) NOT NULL , `passwordHash` VARCHAR(512) NOT NULL , `permLevel` INT NOT NULL DEFAULT '0' , `verificationState` INT NOT NULL DEFAULT '0' , PRIMARY KEY (`id`), UNIQUE KEY (`email`)) ENGINE = InnoDB;"; + var newSqlMailVeri = "CREATE TABLE `" + + mysqlData.database + + "`.`mailverification` ( `id` INT NOT NULL AUTO_INCREMENT , `targetMail` VARCHAR(512) NOT NULL , `userID` INT NOT NULL , `token` VARCHAR(255) NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;"; + con.query(sql, function (err, result) { + if (err) + throw err; + logger.info("Table created"); + }); + con.query(newSql, function (err, result) { + if (err) + throw err; + logger.info("Usertable created"); + }); + con.query(newSqlMailVeri, function (err, result) { + if (err) + throw err; + logger.info("Email verification table created"); + }); + } + else { + // We cannot do that. Welp. + logger.warn("MySQL tables are missing and the config denies creation of new ones."); + mysqlIsUpAndOkay = false; + mySQLstate = 1; } - - } else { - // Password falsch - const data = fs.readFileSync("templates/login.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Ok", - fontawesomeKey: fontawesomeKey, - error: true, - errorMessage: "The given password is wrong.", - }) - ); - } } - ); - } - }); - } else { - const data = fs.readFileSync("templates/login.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Ok", - fontawesomeKey: fontawesomeKey, - error: true, - errorMessage: "The given E-Mail is invalid.", - }) - ); - } - } else { - logger.warn( - "The login form did not sent all data. Dump: \n Password: " + - password + - " \n E-Mail: " + - mail - ); - const data = fs.readFileSync("templates/genericError.html", "utf8"); - var displayText = "The form did not sent all the information needed."; - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -app.get("/login", function (req, res) { - if (mysqlIsUpAndOkay) { - const data = fs.readFileSync("templates/login.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Login", - fontawesomeKey: fontawesomeKey, - }) - ); - } else { - const data = fs.readFileSync("templates/dbError.html", "utf8"); - var displayText = - "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; - if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { - displayText = - "The server failed to connect to the MySQL server. This means it was unable to load any data."; - } - if (mySQLstate == 1) { - displayText = - "There is a problem with the database servers setup. Please check the log for more info."; - } - - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -app.get("/profile", function (req, res) { - if (mysqlIsUpAndOkay) { - if (req.session.username != undefined) { - var Hour = new Date().getHours(); - var greeting = "Good night, "; - if (Hour > 18) { - greeting = "Good evening, "; - } else if (Hour > 13) { - greeting = "Good afternoon, "; - } else if (Hour > 5) { - greeting = "Good morning, "; - } - greeting += req.session.username; - const hash = crypto - .createHash("md5") - .update(req.session.mail.replace(" ", "").toLowerCase()) - .digest("hex"); - gravatarURL = "https://www.gravatar.com/avatar/" + hash; - const data = fs.readFileSync("templates/profile.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Profile", - fontawesomeKey: fontawesomeKey, - greeting: greeting, - gravatarURL: gravatarURL, - }) - ); - } else { - const data = fs.readFileSync("templates/redirect.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Profile", - fontawesomeKey: fontawesomeKey, - url: "/login", - }) - ); - } - } else { - const data = fs.readFileSync("templates/dbError.html", "utf8"); - var displayText = - "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; - if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { - displayText = - "The server failed to connect to the MySQL server. This means it was unable to load any data."; - } - if (mySQLstate == 1) { - displayText = - "There is a problem with the database servers setup. Please check the log for more info."; - } - - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -app.get("/register", function (req, res) { - if (mysqlIsUpAndOkay) { - const data = fs.readFileSync("templates/register.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Register", - fontawesomeKey: fontawesomeKey, - sitekey: hCaptcha.sitekey, - }) - ); - } else { - const data = fs.readFileSync("templates/dbError.html", "utf8"); - var displayText = - "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; - if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { - displayText = - "The server failed to connect to the MySQL server. This means it was unable to load any data."; - } - if (mySQLstate == 1) { - displayText = - "There is a problem with the database servers setup. Please check the log for more info."; - } - - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -app.post("/register", function (req, res) { - if (mysqlIsUpAndOkay) { - var sess = req.session; - var resu; - verify(hCaptcha.secret, req.body["g-recaptcha-response"]).then( - (data) => (resu = data) - ); - /*.catch(setTimeout(() => { - //if(resu.success == false){ - console.log("HERE"); - const data = fs.readFileSync("templates/genericError.html", "utf8"); - resu = "-1"; - con - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: "There was an issue with the Captcha", - }) - ); - //} - - }, 0) - );*/ - - if (req.body.pass == req.body.pass2) { - const mailRegex = - /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; - if (mailRegex.test(req.body.email)) { - setTimeout(() => { - console.log(resu); - if (resu.success == true) { - bcrypt.hash(req.body.pass, saltRounds, (err, hash) => { - const data = fs.readFileSync( - "templates/genericError.html", - "utf8" - ); - // SQL INSERT - - var stmt = - "INSERT INTO users(email, username, passwordHash) VALUES(?, ?, ?)"; - var stmt2 = - "INSERT INTO mailverification(targetMail, userID, token) VALUES(?, ?, ?)"; - crypto.randomBytes(48, function (err, buffer) { - var token = buffer.toString("hex"); - con.query( - stmt, - [req.body.email, req.body.username, hash], - (err, results1, fields) => { - if (err) { - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: - "An error occured while creating your account.", - }) - ); - return console.error(err.message); - } else { - // Create mail verification - con.query( - stmt2, - [req.body.email, results1.insertId, token], - (err, results, fields) => { - if (err) { - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: - "An error occured while creating your account.", - }) - ); - return console.error(err.message); - } else { - sess.username = req.body.username; - sess.id = results1.insertId; - sess.mail = req.body.email; - // get inserted id - logger.info("Inserted Id:" + results.insertId); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: "OK " + hash, - }) - ); - sendVerificationMail(results.insertId); - } - } - ); - } - } - ); - }); - }); - } else { - const data = fs.readFileSync("templates/register.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Register", - fontawesomeKey: fontawesomeKey, - sitekey: hCaptcha.sitekey, - error: true, - errorMessage: "You failed the captcha, please try again.", - }) - ); - } - }, 200); - } else { - // Passwords don't match up - const data = fs.readFileSync("templates/register.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Register", - fontawesomeKey: fontawesomeKey, - sitekey: hCaptcha.sitekey, - error: true, - errorMessage: "The E-Mail given is not valid", - }) - ); - } - } else { - // Passwords don't match up - const data = fs.readFileSync("templates/register.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Register", - fontawesomeKey: fontawesomeKey, - sitekey: hCaptcha.sitekey, - error: true, - errorMessage: "The password have to match up.", - }) - ); - } - } else { - const data = fs.readFileSync("templates/dbError.html", "utf8"); - var displayText = - "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; - if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { - displayText = - "The server failed to connect to the MySQL server. This means it was unable to load any data."; - } - if (mySQLstate == 1) { - displayText = - "There is a problem with the database servers setup. Please check the log for more info."; - } - - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -// Routes -app.get("/verify*", function (req, res) { - console.log(req.url.split("/")[2]); - const data = fs.readFileSync("templates/genericError.html", "utf8"); - const stmt = "SELECT * FROM mailverification WHERE token = ?;"; - - con.query(stmt, [req.url.split("/")[2]], function (err, result, fields) { - if (err) { - res.status(404); - res.send( - JSON.stringify({ state: "Failed", message: "Database error occured" }) - ); - logger.error(err); - } else { - if (result.length == 0) { - res.status(404); - res.send( - JSON.stringify({ state: "Failed", message: "Link already done" }) - ); - } else { - console.log(result); - res.status(200); - stmt2 = "DELETE FROM mailverification WHERE id=?"; - console.log(result[0].id); - con.query(stmt2, [result[0].id], function (err, result, fields) { - // TODO handling of this - //logger.debug(err) - //console.log(result) }); - stmt3 = "UPDATE users SET verificationState=1 WHERE email=?"; - con.query( - stmt3, - [result[0].targetMail], - function (err, result, fields) { - // TODO handling of this - //logger.debug(err) - //console.log(result) - } - ); - res.send(JSON.stringify({ state: "OK", message: "Done!" })); - } } - }); -}); - -app.get("/logout", function (req, res) { - req.session.destroy(); - const data = fs.readFileSync("templates/redirect.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Logout", - fontawesomeKey: fontawesomeKey, - url: "/", - }) - ); -}); - -app.get("/debug/showSessionInfo", function (req, res) { - res.send(JSON.stringify(req.session)); -}); - -app.get("/map", function (req, res) { - if (mysqlIsUpAndOkay) { - const data = fs.readFileSync("templates/map.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Map", - fontawesomeKey: fontawesomeKey, - mapboxAccessToken: mapboxAccessToken, - }) - ); - } else { - const data = fs.readFileSync("templates/dbError.html", "utf8"); - var displayText = - "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; - if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { - displayText = - "The server failed to connect to the MySQL server. This means it was unable to load any data."; + else { + logger.warn("Tried checking the tables even though MySQL wasn't ready."); } - if (mySQLstate == 1) { - displayText = - "There is a problem with the database servers setup. Please check the log for more info."; - } - - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -app.get("/createElevator", function (req, res) { - if (mysqlIsUpAndOkay) { - - if (req.session.username != undefined) { - const data = fs.readFileSync("templates/createElevator.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "New elevator", - fontawesomeKey: fontawesomeKey, - mapboxAccessToken: mapboxAccessToken, - }) - ); - } else { - const data = fs.readFileSync("templates/redirect.html", "utf8"); - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Profile", - fontawesomeKey: fontawesomeKey, - url: "/login?r=/createElevator", - }) - ); - } - - - - } else { - const data = fs.readFileSync("templates/dbError.html", "utf8"); - var displayText = - "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; - if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { - displayText = - "The server failed to connect to the MySQL server. This means it was unable to load any data."; - } - if (mySQLstate == 1) { - displayText = - "There is a problem with the database servers setup. Please check the log for more info."; - } - - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -app.get("/api/getElevators", function (req, res) { - console.log(req.query); - if ( - req.query.lan != undefined && - req.query.lat != undefined && - req.query.radius != undefined - ) { - // All parameters are there - res.setHeader("Content-Type", "application/json"); - try { - var lan = parseFloat(req.query.lan); - var lat = parseFloat(req.query.lat); - var radius = parseFloat(req.query.radius); - } catch (error) { - res.send( - JSON.stringify({ state: "Failed", message: "Invalid arguments" }) - ); - res.status(400); - return; - } - var lan = parseFloat(req.query.lan); - var lat = parseFloat(req.query.lat); - var radius = parseFloat(req.query.radius); - - // TODO: Return just the elevators in the viewers area - - con.query("SELECT * FROM elevators", function (err, result, fields) { - if (err) { - res.status(500); - res.send( - JSON.stringify({ - state: "Failed", - message: "A server side error occured.", - results: [], - }) - ); - logger.error("The server failed to execute a request"); - mysqlIsUpAndOkay = false; - } else { - console.log(result[0]); - res.status(200); - res.send(JSON.stringify({ state: "Ok", message: "", results: result })); - } - }); - } else { - // Welp something is missing - res.status(400); - res.setHeader("Content-Type", "application/json"); - res.send(JSON.stringify({ state: "Failed", message: "Missing arguments" })); - } -}); - -app.post("/api/uploadImage", upload.any(), function (req, res) { - //TODO: Fix file ending, add image to elevator in DBw - console.log(req.query.id) - i = 0; - const sql = 'SELECT id, images FROM elevators WHERE id=?'; - // req.query.id - allImages = [] - while (i < req.files.length) { - fObj = req.files[i]; - const currentPath = path.join(fObj["path"]); - const destinationPath = - currentPath + - "." + - fObj["originalname"].split(".")[ - fObj["originalname"].split(".").length - 1 - ]; // Add the file end - - fs.rename(currentPath, destinationPath, function (err) { - if (err) { - throw err; - } else { - console.log("Successfully moved the file!"); - } - }); - allImages.push({"path": destinationPath, "alt": "No alt was provided."}) - i++; - } - - con.query( - sql, [req.query.id], - function (err, result, fields) { - if (err) { - res.status(500); - res.send( - JSON.stringify({ - state: "Failed", - message: "A server side error occured.", - results: [], - }) - ); - logger.error("The server failed to execute a request"); - mysqlIsUpAndOkay = false; - } else { - jData = JSON.parse(result[0].images) - console.log(jData) - jData.images.push.apply(jData.images, allImages) - console.log(jData); - console.log(result); - var sql = "UPDATE elevators SET images = ? WHERE id = ?"; - con.query(sql, [JSON.stringify(jData), req.query.id], function(err, result, fields){ - if (err) { - console.log("Update failure") - }else{ - console.log("Okay") - } - - }) - } - } - ); - - // Save Image End -}); - -// returns an object with the cookies' name as keys -const getAppCookies = (req) => { - // We extract the raw cookies from the request headers - const rawCookies = req.headers.cookie.split("; "); - // rawCookies = ['myapp=secretcookie, 'analytics_cookie=beacon;'] - - const parsedCookies = {}; - rawCookies.forEach((rawCookie) => { - const parsedCookie = rawCookie.split("="); - // parsedCookie = ['myapp', 'secretcookie'], ['analytics_cookie', 'beacon'] - parsedCookies[parsedCookie[0]] = parsedCookie[1]; - }); - return parsedCookies; -}; - -app.post("/api/saveNewElevatorMeta", function (req, res) { - var sess = req.session; - console.log(req.headers.cookie); - tempJs = JSON.parse(decodeURIComponent(getAppCookies(req, res)["tempStore"])); - console.log(tempJs); - const sql = - "INSERT INTO elevators (lat, lng, manufacturer, modell, info, visitabilty, technology, amountOfFloors, maxPassangers, maxWeight, images, creator) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '{ \"images\": []}', ?)"; - con.query( - sql, - [ - tempJs.lat, - tempJs.lng, - tempJs.manuf, - tempJs.model, - tempJs.description, - tempJs.visit, - tempJs.type, - tempJs.flor, - tempJs.pepl, - tempJs.weig, - sess.uid - ], - function (err, result) { - if (err) throw err; - console.log("1 record inserted with id " + result.insertId); - res.setHeader("Content-Type", "application/json"); - - res.send( - JSON.stringify({ state: "Okay", message: "Ok. No fault!", id: result.insertId }) - ); - res.status(200); - } - ); -}); - -app.get("/api/getElevatorLocation", function (req, res) { - if ( - req.query.lan != undefined && - req.query.lat != undefined && - req.query.radius != undefined - ) { - // All parameters are there - res.setHeader("Content-Type", "application/json"); - try { - var lan = parseFloat(req.query.lan); - var lat = parseFloat(req.query.lat); - var radius = parseFloat(req.query.radius); - } catch (error) { - res.send( - JSON.stringify({ state: "Failed", message: "Invalid arguments" }) - ); - res.status(400); - return; - } - var lan = parseFloat(req.query.lan); - var lat = parseFloat(req.query.lat); - var radius = parseFloat(req.query.radius); - - // TODO: Return just the elevators in the viewers area - - con.query( - "SELECT id, lat, lng FROM elevators", - function (err, result, fields) { - if (err) { - res.status(500); - res.send( - JSON.stringify({ - state: "Failed", - message: "A server side error occured.", - results: [], - }) - ); - logger.error("The server failed to execute a request"); - mysqlIsUpAndOkay = false; - } else { - console.log(result[0]); - res.status(200); - res.send( - JSON.stringify({ state: "Ok", message: "", results: result }) - ); - } - } - ); - } else { - // Welp something is missing - res.status(400); - res.setHeader("Content-Type", "application/json"); - res.send(JSON.stringify({ state: "Failed", message: "Missing arguments" })); - } -}); - -app.get("/api/resolveNameById", function (req, res) { - if (mysqlIsUpAndOkay) { - if(req.query.id != undefined && req.query.id != ""){ - - const sql = "SELECT username FROM users WHERE id=?"; - con.query(sql, [req.query.id], function (err, result, fields) { - if (err) { - res.status(500); - res.send( - JSON.stringify({ - state: "Failed", - message: "A server side error occured.", - results: [], - }) - ); - logger.error("The server failed to execute a request"); - mysqlIsUpAndOkay = false; - } else { - console.log(result[0]); - res.status(200); - res.setHeader("Content-Type", "application/json"); - res.send( - JSON.stringify({ state: "Ok", message: "", results: result }) - ); - } - } - ); - }else{ - res.status(400); - res.setHeader("Content-Type", "application/json"); - res.send(JSON.stringify({ state: "Failed", message: "Missing argument: id" })); - } - } else { - const data = fs.readFileSync("templates/dbError.html", "utf8"); - var displayText = - "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; - if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { - displayText = - "The server failed to connect to the MySQL server. This means it was unable to load any data."; - } - if (mySQLstate == 1) { - displayText = - "There is a problem with the database servers setup. Please check the log for more info."; - } - - res.send( - Eta.render(data, { - author: author, - desc: desc, - siteTitel: sitePrefix + "Error", - fontawesomeKey: fontawesomeKey, - displayText: displayText, - }) - ); - } -}); - -app.get("/api/getElevatorById", function (req, res) { - console.log(req.query); - if (req.query.id != undefined) { - // All parameters are there - res.setHeader("Content-Type", "application/json"); - try { - var id = parseFloat(req.query.id); - } catch (error) { - res.send( - JSON.stringify({ state: "Failed", message: "Invalid arguments" }) - ); - res.status(400); - return; - } - var id = parseFloat(req.query.id); - - con.query( - "SELECT * FROM elevators WHERE id=" + id, - function (err, result, fields) { - if (err) { - res.status(500); - res.send( - JSON.stringify({ - state: "Failed", - message: "A server side error occured.", - results: [], - }) - ); - logger.error("The server failed to execute a request"); - console.log(err); - mysqlIsUpAndOkay = false; - } else { - console.log(result[0]); - res.status(200); - res.send( - JSON.stringify({ - state: "Ok", - message: "Successful.", - results: result, - }) - ); - } - } - ); - } else { - // Welp something is missing - res.status(400); - res.setHeader("Content-Type", "application/json"); - res.send(JSON.stringify({ state: "Failed", message: "Missing arguments" })); - } -}); - -// Some loops for handeling stuff -setInterval(() => { - if (mysqlIsUpAndOkay == false) { - logger.warn("Retrying to connect to MySQL"); - con = mysql.createConnection({ - host: "localhost", - user: mysqlData.user, - password: mysqlData.password, - database: mysqlData.database, - }); - - con.connect(function (err) { - if (err) { +} +con.connect(function (err) { + if (err) { mysqlIsUpAndOkay = false; logger.error("Connction to MySQL failed"); console.log(err); - } else { + } + else { logger.info("Mysql is ready."); mysqlIsUpAndOkay = true; - } - }); - } -}, 60000); - -// App start -app.listen(port, () => { - logger.info(`Elevator map ready at http://localhost:${port}`); + checkIfMySQLStructureIsReady(); + } +}); +// Routes +app.get("/", function (req, res) { + var data = fs.readFileSync("templates/index.html", "utf8"); + res.send(Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Start", + fontawesomeKey: fontawesomeKey + })); +}); +app.get("/map", function (req, res) { + var data = fs.readFileSync("templates/map.html", "utf8"); + res.send(Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Map", + fontawesomeKey: fontawesomeKey, + mapboxAccessToken: mapboxAccessToken + })); +}); +app.get("/createElevator", function (req, res) { + var data = fs.readFileSync("templates/createElevator.html", "utf8"); + res.send(Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "New elevator", + fontawesomeKey: fontawesomeKey, + mapboxAccessToken: mapboxAccessToken + })); +}); +require('./Routes/api.route.ts')(app, con, mysqlIsUpAndOkay, logger, metainfo); +require('./Routes/debug.route.ts')(app, con, logger, metainfo); +require('./Routes/auth.route.ts')(app, con, logger, metainfo, jsonConfigGlobal); +// Some loops for handeling stuff +setInterval(function () { + if (mysqlIsUpAndOkay == false) { + logger.warn("Retrying to connect to MySQL"); + con = mysql.createConnection({ + host: "localhost", + user: mysqlData.user, + password: mysqlData.password, + database: mysqlData.database + }); + con.connect(function (err) { + if (err) { + mysqlIsUpAndOkay = false; + logger.error("Connction to MySQL failed"); + console.log(err); + } + else { + logger.info("Mysql is ready."); + mysqlIsUpAndOkay = true; + } + }); + } +}, 60000); +// App start +app.listen(port, function () { + logger.info("Elevator map ready at http://localhost:" + port); }); diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..4f3d201 --- /dev/null +++ b/index.ts @@ -0,0 +1,357 @@ +// Imports +const express = require("express"); +const fs = require("fs"); +const Eta = require("eta"); +const winston = require("winston"); +const mysql = require("mysql"); +const bodyParser = require("body-parser"); +// const csp = require(`helmet`); +const session = require("express-session"); +const nodemailer = require("nodemailer"); + +// Inting the logger +const logger = winston.createLogger({ + level: "debug", + format: winston.format.json(), + defaultMeta: { service: "user-service" }, + transports: [ + // + // - Write all logs with level `error` and below to `error.log` + // - Write all logs with level `info` and below to `combined.log` + // + new winston.transports.File({ filename: "error.log", level: "error" }), + new winston.transports.File({ filename: "combined.log" }), + ], +}); + +logger.add( + new winston.transports.Console({ + format: winston.format.simple(), + }) +); + +const app = express(); + +app.use(express.static("static")); + +app.use(bodyParser.urlencoded({ extended: false })); +app.use(bodyParser.json()); + +app.use(function (req, res, next) { + const pathesWhichRequireDB = ["map", "login", "register"]; + const pathesWhichRequireLogin = ["createElevator"]; + const path = req.path + const pathesDes = path.split("/") + let requiresDB = false; + let requiresLogin = false; + let allowContinue = true; + console.log(pathesDes) + + if (pathesWhichRequireLogin.indexOf(pathesDes[1]) > -1) { + requiresLogin = true; + } + + if (pathesDes[1] == "api") { + requiresDB = true; + } + if (pathesWhichRequireDB.indexOf(pathesDes[1]) > -1) { + requiresDB = true; + } + + if (requiresDB) { + if (!mysqlIsUpAndOkay) { + allowContinue = false; + const data = fs.readFileSync("templates/dbError.html", "utf8"); + let displayText = + "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page."; + if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) { + displayText = + "The server failed to connect to the MySQL server. This means it was unable to load any data."; + } + if (mySQLstate == 1) { + displayText = + "There is a problem with the database servers setup. Please check the log for more info."; + } + + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Error", + fontawesomeKey: fontawesomeKey, + displayText: displayText, + }) + ); + } + } + + if (requiresLogin) { + allowContinue = false; + const data = fs.readFileSync("templates/redirect.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Redirect", + fontawesomeKey: fontawesomeKey, + url: "/login?r=" + path, + }) + ); + } + + console.log('Time:', Date.now()) + if (allowContinue) { + next() + } else { + console.log("Stopped further exec of route") + } + +}) + +/* +app.use(csp.contentSecurityPolicy({ + useDefaults: true, + contentSecurityPolicy: false, + crossOriginEmbedderPolicy: false, + directives: { + "default-src": [`'self'`], + "img-src": [`'self'`], + scriptSrc: [`'self'`, `https://hcaptcha.com`, `https://*.hcaptcha.com`, `https://*.fontawesome.com`, "unsafe-inline", "unsafe-eval","'unsafe-inline'"], + "script-src-attr": [`'self'`, `https://hcaptcha.com`, `https://*.hcaptcha.com`, `https://*.fontawesome.com`, "unsafe-inline", "unsafe-eval"], + "frame-src": [`'self'`, `https://hcaptcha.com`, `https://*.hcaptcha.com`], + "style-src": [`'self'`, `https://hcaptcha.com`, `https://*.hcaptcha.com`, `https://*.fontawesome.com`, `'unsafe-inline'`], + "connect-src": [`'self'`, `https://hcaptcha.com`, `https://*.hcaptcha.com`, `https://*.fontawesome.com`], + "font-src": [`'self'`, `https://*.fontawesome.com`], + }, + +})) +*/ + +// Settings +const port = 3000; +const startUpTime = Math.floor(new Date().getTime() / 1000); + + +let fontawesomeKey = ""; +let mapboxAccessToken = ""; +let mysqlData = { "user": "", "password": "", "database": "", "allowCreation": false }; +let hCaptcha = { "sitekey": "", "secret": "" }; +let mailConf = { "host": "", "port": 0, "username": "", "password": "" }; +let serverAdress = ""; +let cookieSecret = "" +let jsonConfigGlobal = {}; + +// Load config +try { + const data = fs.readFileSync("config/default.json", "utf8"); + const jsonContent = JSON.parse(data); + let jsonConfig = jsonContent; + if (jsonContent.redirectConfig) { + const data = fs.readFileSync( + "config/" + jsonContent.redirectConfig, + "utf8" + ); + jsonConfig = JSON.parse(data); + } + fontawesomeKey = jsonConfig.fontAwesome; + mapboxAccessToken = jsonConfig.mapboxAccessToken; + mysqlData = jsonConfig.mysql; + + mailConf = jsonConfig.mail; + serverAdress = jsonConfig.serverAdress; + cookieSecret = + jsonConfig.cookieSecret || "saF0DSF65AS4DF0S4D6F0S54DF0Fad"; + jsonConfigGlobal = jsonConfig; +} catch (error) { + logger.error( + "While reading the config an error occured. The error was: " + error + ); +} + +const transport = nodemailer.createTransport({ + host: mailConf.host, + port: mailConf.port, + requireTLS: true, + secure: false, + debug: true, + disableFileAccess: true, + //authMethod: "START TLS", + auth: { + user: mailConf.username, + pass: mailConf.password, + }, +}); + +//let transporter = nodemailer.createTransport(transport) +//console.log(transport.host) +logger.info("Testing SMTP connection"); +transport.verify(function (error) { + if (error) { + logger.error(error); + } else { + logger.info("SMPT server is ready to accept messages"); + } +}); + +app.use(session({ secret: cookieSecret })); + + +// Basic defines for html +const metainfo = { + author: "TheGreydiamond", + desc: "The Elevatormap. A map for elevator spotters!", + sitePrefix: "Elevatormap - " +} + +let mysqlIsUpAndOkay = false; +let mySQLstate = 0; // 0 -> Default failure 1 -> Missing strucutre + + + +// Prepare MYSQL +let con = mysql.createConnection({ + host: "localhost", + user: mysqlData.user, + password: mysqlData.password, + database: mysqlData.database, +}); + + + +function checkIfMySQLStructureIsReady() { + if (mysqlIsUpAndOkay) { + // Only if MySQL is ready + logger.debug("Checking MySQL strucutre"); + con.query("SHOW TABLES;", function (err, result, fields) { + if (err) throw err; + if (result.length == 0) { + // There are no tables. Not good. + logger.warn("There are no tables found"); + if (mysqlData.allowCreation) { + // Lets create it then + logger.warn("Creating a new table"); + const sql = + "CREATE TABLE `" + + mysqlData.database + + "`.`elevators` ( `id` INT NOT NULL AUTO_INCREMENT , `lat` FLOAT NOT NULL , `lng` FLOAT NOT NULL , `manufacturer` VARCHAR(512) NOT NULL , `modell` VARCHAR(512) NOT NULL , `info` VARCHAR(512) NOT NULL , `visitabilty` INT NOT NULL , `technology` INT NOT NULL , `images` JSON NOT NULL , `amountOfFloors` INT NOT NULL , `maxPassangers` INT NOT NULL , `maxWeight` INT NOT NULL , `creator` INT NOT NULL, PRIMARY KEY (`id`)) ENGINE = InnoDB;"; + const newSql = + "CREATE TABLE `" + + mysqlData.database + + "`.`users` ( `id` INT NOT NULL AUTO_INCREMENT , `email` VARCHAR(255) NOT NULL , `username` VARCHAR(255) NOT NULL , `passwordHash` VARCHAR(512) NOT NULL , `permLevel` INT NOT NULL DEFAULT '0' , `verificationState` INT NOT NULL DEFAULT '0' , PRIMARY KEY (`id`), UNIQUE KEY (`email`)) ENGINE = InnoDB;"; + const newSqlMailVeri = + "CREATE TABLE `" + + mysqlData.database + + "`.`mailverification` ( `id` INT NOT NULL AUTO_INCREMENT , `targetMail` VARCHAR(512) NOT NULL , `userID` INT NOT NULL , `token` VARCHAR(255) NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;"; + con.query(sql, function (err, result) { + if (err) throw err; + logger.info("Table created"); + }); + + con.query(newSql, function (err, result) { + if (err) throw err; + logger.info("Usertable created"); + }); + + con.query(newSqlMailVeri, function (err, result) { + if (err) throw err; + logger.info("Email verification table created"); + }); + } else { + // We cannot do that. Welp. + logger.warn( + "MySQL tables are missing and the config denies creation of new ones." + ); + mysqlIsUpAndOkay = false; + mySQLstate = 1; + } + } + }); + } else { + logger.warn("Tried checking the tables even though MySQL wasn't ready."); + } +} + +con.connect(function (err) { + if (err) { + mysqlIsUpAndOkay = false; + logger.error("Connction to MySQL failed"); + console.log(err); + } else { + logger.info("Mysql is ready."); + mysqlIsUpAndOkay = true; + checkIfMySQLStructureIsReady(); + } +}); + +// Routes +app.get("/", function (req, res) { + const data = fs.readFileSync("templates/index.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Start", + fontawesomeKey: fontawesomeKey, + }) + ); +}); + +app.get("/map", function (req, res) { + const data = fs.readFileSync("templates/map.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Map", + fontawesomeKey: fontawesomeKey, + mapboxAccessToken: mapboxAccessToken, + }) + ) + +}); + +app.get("/createElevator", function (req, res) { + const data = fs.readFileSync("templates/createElevator.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "New elevator", + fontawesomeKey: fontawesomeKey, + mapboxAccessToken: mapboxAccessToken, + }) + ); +}); + +require('./Routes/api.route.ts')(app, con, mysqlIsUpAndOkay, logger, metainfo); +require('./Routes/debug.route.ts')(app, con, logger, metainfo); +require('./Routes/auth.route.ts')(app, con, logger, metainfo, jsonConfigGlobal); + +// Some loops for handeling stuff +setInterval(() => { + if (mysqlIsUpAndOkay == false) { + logger.warn("Retrying to connect to MySQL"); + con = mysql.createConnection({ + host: "localhost", + user: mysqlData.user, + password: mysqlData.password, + database: mysqlData.database, + }); + + con.connect(function (err) { + if (err) { + mysqlIsUpAndOkay = false; + logger.error("Connction to MySQL failed"); + console.log(err); + } else { + logger.info("Mysql is ready."); + mysqlIsUpAndOkay = true; + } + }); + } +}, 60000); + +// App start +app.listen(port, () => { + logger.info(`Elevator map ready at http://localhost:${port}`); +}); diff --git a/package-lock.json b/package-lock.json index ae18b2f..a2df481 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,51 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + } + } + }, "@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -14,6 +59,80 @@ "kuler": "^2.0.0" } }, + "@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@mapbox/node-pre-gyp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz", @@ -30,6 +149,177 @@ "tar": "^6.1.0" } }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true + }, + "@types/node": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.0.0.tgz", + "integrity": "sha512-TmCW5HoZ2o2/z2EYi109jLqIaPIi9y/lc2LmDCWzuCi35bcaQ+OtUh6nwBiFK7SOu25FAU5+YKdqFZUwtqGSdg==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz", + "integrity": "sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.28.2", + "@typescript-eslint/scope-manager": "4.28.2", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz", + "integrity": "sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.28.2", + "@typescript-eslint/types": "4.28.2", + "@typescript-eslint/typescript-estree": "4.28.2", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.2.tgz", + "integrity": "sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.28.2", + "@typescript-eslint/types": "4.28.2", + "@typescript-eslint/typescript-estree": "4.28.2", + "debug": "^4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz", + "integrity": "sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.2", + "@typescript-eslint/visitor-keys": "4.28.2" + } + }, + "@typescript-eslint/types": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.2.tgz", + "integrity": "sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz", + "integrity": "sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.2", + "@typescript-eslint/visitor-keys": "4.28.2", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz", + "integrity": "sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.2", + "eslint-visitor-keys": "^2.0.0" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -44,6 +334,18 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -67,11 +369,38 @@ } } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, "append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -115,11 +444,32 @@ } } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "async": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", @@ -170,6 +520,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -212,6 +571,63 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -340,6 +756,17 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -348,6 +775,12 @@ "ms": "2.0.0" } }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -400,11 +833,35 @@ } } }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -415,15 +872,235 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.30.0.tgz", + "integrity": "sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "eslint-config-strongloop": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-strongloop/-/eslint-config-strongloop-2.1.0.tgz", + "integrity": "sha1-dj3Rmt/OiNewBR5uJV8a43eDtMY=", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "eta": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.1.tgz", - "integrity": "sha512-H8npoci2J/7XiPnVcCVulBSPsTNGvGaINyMjQDU8AFqp9LGsEYS88g2CiU+d01Sg44WtX7o4nb8wUJ9vnI+tiA==" + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.2.tgz", + "integrity": "sha512-Z05sK2DRWAfBhG/2cwAOWuMoQIYaVYJCQrz2g2O/ekUjzWHNBv9L1pnblVDoDkKSb/AZ5tWZ0N/v4iaIU4+HjA==" }, "etag": { "version": "1.8.1", @@ -499,16 +1176,74 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.6.tgz", + "integrity": "sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, + "fastq": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", + "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "fecha": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -523,6 +1258,22 @@ "unpipe": "~1.0.0" } }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.0.tgz", + "integrity": "sha512-XprP7lDrVT+kE2c2YlfiV+IfS9zxukiIOvNamPNsImNhXadSsQEbosItdL9bUQlCZXR13SvPk20BjWSWLA7m4A==", + "dev": true + }, "fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -551,6 +1302,12 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -579,6 +1336,49 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "greeting-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/greeting-time/-/greeting-time-1.0.0.tgz", + "integrity": "sha512-SjefO4MuSaL1xvFtxMZKnoROsh/t8s3UaWjNfsRA7IvD2o7picqySgYk7NAZmhEaCieQhXsrbjfP33aGRADxbg==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -638,6 +1438,28 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -662,6 +1484,12 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -670,6 +1498,21 @@ "number-is-nan": "^1.0.0" } }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -680,11 +1523,73 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "logform": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", @@ -737,11 +1642,27 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -860,6 +1781,12 @@ } } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -876,9 +1803,9 @@ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "nodemailer": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.1.tgz", - "integrity": "sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg==" + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.2.tgz", + "integrity": "sha512-YSzu7TLbI+bsjCis/TZlAXBoM4y93HhlIgo0P5oiA2ua9Z4k+E2Fod//ybIzdJxOlXGRcHIh/WaeCBehvxZb/Q==" }, "nopt": { "version": "5.0.0", @@ -938,6 +1865,29 @@ "fn.name": "1.x.x" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -948,16 +1898,46 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -967,11 +1947,23 @@ "ipaddr.js": "1.9.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -1003,6 +1995,30 @@ "util-deprecate": "^1.0.1" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -1011,6 +2027,15 @@ "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1077,6 +2102,21 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -1090,6 +2130,61 @@ "is-arrayish": "^0.3.1" } }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -1143,6 +2238,87 @@ "ansi-regex": "^2.0.0" } }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.1.tgz", + "integrity": "sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "tar": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", @@ -1161,6 +2337,21 @@ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -1171,6 +2362,36 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1185,6 +2406,12 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -1198,6 +2425,15 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1208,11 +2444,26 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -1270,6 +2521,12 @@ } } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 23f369b..eb8fcdf 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,13 @@ "description": "The elevatormap rewritten", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 0", - "start": "node index.js" + "makeJS": "tsc index.ts", + "lint": "eslint . --ext .ts", + "makeJSwatch": "tsc -w index.ts", + "start": "tsc index.ts && node index.js", + "preChecks": "npm outdated && npm audit", + "startBeforeMerge": "eslint . --ext .ts && tsc index.ts && node index.js", + "nodemon": "nodemon index.js" }, "repository": { "type": "git", @@ -20,14 +25,23 @@ "dependencies": { "bcrypt": "^5.0.1", "body-parser": "^1.19.0", - "eta": "^1.12.1", + "eta": "^1.12.2", "express": "^4.17.1", "express-session": "^1.17.2", + "greeting-time": "^1.0.0", "hcaptcha": "0.0.2", "helmet": "^4.6.0", "multer": "^1.4.2", "mysql": "^2.18.1", - "nodemailer": "^6.6.1", + "nodemailer": "^6.6.2", "winston": "^3.3.3" + }, + "devDependencies": { + "@types/node": "^16.0.0", + "@typescript-eslint/eslint-plugin": "^4.21.0", + "@typescript-eslint/parser": "^4.21.0", + "eslint": "^7.26.0", + "eslint-config-strongloop": "^2.1.0", + "typescript": "^4.2.4" } } diff --git a/routes/api.route.ts b/routes/api.route.ts new file mode 100644 index 0000000..dd6f74d --- /dev/null +++ b/routes/api.route.ts @@ -0,0 +1,312 @@ +module.exports = function (app, con, mysqlIsUpAndOkay, logger) { + const multer = require("multer"); + const upload = multer({ dest: "static/uploads/" }); + const fs = require("fs"); + const path = require("path"); + + app.get("/api/getElevatorById", function (req, res) { + console.log(req.query); + if (req.query.id != undefined) { + // All parameters are there + res.setHeader("Content-Type", "application/json"); + try { + const id = parseFloat(req.query.id); + } catch (error) { + res.send( + JSON.stringify({ state: "Failed", message: "Invalid arguments" }) + ); + res.status(400); + return; + } + const id = parseFloat(req.query.id); + + con.query( + "SELECT * FROM elevators WHERE id=" + id, + function (err, result) { + if (err) { + res.status(500); + res.send( + JSON.stringify({ + state: "Failed", + message: "A server side error occured.", + results: [], + }) + ); + logger.error("The server failed to execute a request"); + console.log(err); + mysqlIsUpAndOkay = false; + } else { + console.log(result[0]); + res.status(200); + res.send( + JSON.stringify({ + state: "Ok", + message: "Successful.", + results: result, + }) + ); + } + } + ); + } else { + // Welp something is missing + res.status(400); + res.setHeader("Content-Type", "application/json"); + res.send(JSON.stringify({ state: "Failed", message: "Missing arguments" })); + } + }); + + app.get("/api/resolveNameById", function (req, res) { + if (req.query.id != undefined && req.query.id != "") { + + const sql = "SELECT username FROM users WHERE id=?"; + con.query(sql, [req.query.id], function (err, result) { + if (err) { + res.status(500); + res.send( + JSON.stringify({ + state: "Failed", + message: "A server side error occured.", + results: [], + }) + ); + logger.error("The server failed to execute a request"); + mysqlIsUpAndOkay = false; + } else { + console.log(result[0]); + res.status(200); + res.setHeader("Content-Type", "application/json"); + res.send( + JSON.stringify({ state: "Ok", message: "", results: result }) + ); + } + } + ); + } else { + res.status(400); + res.setHeader("Content-Type", "application/json"); + res.send(JSON.stringify({ state: "Failed", message: "Missing argument: id" })); + } + }); + + + app.get("/api/getElevatorLocation", function (req, res) { + if ( + req.query.lan != undefined && + req.query.lat != undefined && + req.query.radius != undefined + ) { + // All parameters are there + res.setHeader("Content-Type", "application/json"); + try { + const lan = parseFloat(req.query.lan); + const lat = parseFloat(req.query.lat); + const radius = parseFloat(req.query.radius); + } catch (error) { + res.send( + JSON.stringify({ state: "Failed", message: "Invalid arguments" }) + ); + res.status(400); + return; + } + const lan = parseFloat(req.query.lan); + const lat = parseFloat(req.query.lat); + const radius = parseFloat(req.query.radius); + + // TODO: Return just the elevators in the viewers area + + con.query( + "SELECT id, lat, lng FROM elevators", + function (err, result, fields) { + if (err) { + res.status(500); + res.send( + JSON.stringify({ + state: "Failed", + message: "A server side error occured.", + results: [], + }) + ); + logger.error("The server failed to execute a request"); + mysqlIsUpAndOkay = false; + } else { + console.log(result[0]); + res.status(200); + res.send( + JSON.stringify({ state: "Ok", message: "", results: result }) + ); + } + } + ); + } else { + // Welp something is missing + res.status(400); + res.setHeader("Content-Type", "application/json"); + res.send(JSON.stringify({ state: "Failed", message: "Missing arguments" })); + } + }); + + // returns an object with the cookies' name as keys + const getAppCookies = (req) => { + // We extract the raw cookies from the request headers + const rawCookies = req.headers.cookie.split("; "); + // rawCookies = ['myapp=secretcookie, 'analytics_cookie=beacon;'] + + const parsedCookies = {}; + rawCookies.forEach((rawCookie) => { + const parsedCookie = rawCookie.split("="); + // parsedCookie = ['myapp', 'secretcookie'], ['analytics_cookie', 'beacon'] + parsedCookies[parsedCookie[0]] = parsedCookie[1]; + }); + return parsedCookies; + }; + + app.post("/api/saveNewElevatorMeta", function (req, res) { + const sess = req.session; + const tempJs = JSON.parse(decodeURIComponent(getAppCookies(req)["tempStore"])); + const sql = + "INSERT INTO elevators (lat, lng, manufacturer, modell, info, visitabilty, technology, amountOfFloors, maxPassangers, maxWeight, images, creator) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '{ \"images\": []}', ?)"; + con.query( + sql, + [ + tempJs.lat, + tempJs.lng, + tempJs.manuf, + tempJs.model, + tempJs.description, + tempJs.visit, + tempJs.type, + tempJs.flor, + tempJs.pepl, + tempJs.weig, + sess.uid + ], + function (err, result) { + if (err) throw err; + console.log("1 record inserted with id " + result.insertId); + res.setHeader("Content-Type", "application/json"); + + res.send( + JSON.stringify({ state: "Okay", message: "Ok. No fault!", id: result.insertId }) + ); + res.status(200); + } + ); + }); + + + app.post("/api/uploadImage", upload.any(), function (req, res) { + console.log(req.query.id) + let i = 0; + const sql = 'SELECT id, images FROM elevators WHERE id=?'; + const allImages = [] + while (i < req.files.length) { + const fObj = req.files[i]; + const currentPath = path.join(fObj["path"]); + const destinationPath = + currentPath + + "." + + fObj["originalname"].split(".")[ + fObj["originalname"].split(".").length - 1 + ]; // Add the file end + + fs.rename(currentPath, destinationPath, function (err) { + if (err) { + throw err; + } else { + console.log("Successfully moved the file!"); + } + }); + allImages.push({ "path": destinationPath, "alt": "No alt was provided." }) + i++; + } + + con.query( + sql, [req.query.id], + function (err, result, fields) { + if (err) { + res.status(500); + res.send( + JSON.stringify({ + state: "Failed", + message: "A server side error occured.", + results: [], + }) + ); + logger.error("The server failed to execute a request"); + mysqlIsUpAndOkay = false; + } else { + const jData = JSON.parse(result[0].images) + console.log(jData) + jData.images.push.spread(jData.images, allImages) + console.log(jData); + console.log(result); + const sql = "UPDATE elevators SET images = ? WHERE id = ?"; + con.query(sql, [JSON.stringify(jData), req.query.id], function (err) { + if (err) { + console.log("Update failure") + } else { + console.log("Okay") + } + + }) + } + } + ); + + // Save Image End + }); + + + app.get("/api/getElevators", function (req, res) { + console.log(req.query); + if ( + req.query.lan != undefined && + req.query.lat != undefined && + req.query.radius != undefined + ) { + // All parameters are there + res.setHeader("Content-Type", "application/json"); + try { + const lan = parseFloat(req.query.lan); + const lat = parseFloat(req.query.lat); + const radius = parseFloat(req.query.radius); + } catch (error) { + res.send( + JSON.stringify({ state: "Failed", message: "Invalid arguments" }) + ); + res.status(400); + return; + } + const lan = parseFloat(req.query.lan); + const lat = parseFloat(req.query.lat); + const radius = parseFloat(req.query.radius); + + // TODO: Return just the elevators in the viewers area + + con.query("SELECT * FROM elevators", function (err, result) { + if (err) { + res.status(500); + res.send( + JSON.stringify({ + state: "Failed", + message: "A server side error occured.", + results: [], + }) + ); + logger.error("The server failed to execute a request"); + mysqlIsUpAndOkay = false; + } else { + console.log(result[0]); + res.status(200); + res.send(JSON.stringify({ state: "Ok", message: "", results: result })); + } + }); + } else { + // Welp something is missing + res.status(400); + res.setHeader("Content-Type", "application/json"); + res.send(JSON.stringify({ state: "Failed", message: "Missing arguments" })); + } + }); +} \ No newline at end of file diff --git a/routes/auth.route.ts b/routes/auth.route.ts new file mode 100644 index 0000000..8d55b2e --- /dev/null +++ b/routes/auth.route.ts @@ -0,0 +1,443 @@ +module.exports = function (app, con, logger, metainfo, jsonConfig) { + const greetingTime = require("greeting-time"); + const fs = require("fs"); + const Eta = require("eta"); + const { verify } = require("hcaptcha"); + const bcrypt = require("bcrypt"); + const cryptoF = require("crypto"); + const saltRounds = 10; + + const mailRegex = + /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; + + app.get("/logout", function (req, res) { + req.session.destroy(); + const data = fs.readFileSync("templates/redirect.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Logout", + fontawesomeKey: jsonConfig.fontAwesome, + url: "/", + }) + ); + }); + app.get("/verify*", function (req, res) { + console.log(req.url.split("/")[2]); + const stmt = "SELECT * FROM mailverification WHERE token = ?;"; + + con.query(stmt, [req.url.split("/")[2]], function (err, result) { + if (err) { + res.status(404); + res.send( + JSON.stringify({ state: "Failed", message: "Database error occured" }) + ); + logger.error(err); + } else { + if (result.length == 0) { + res.status(404); + res.send( + JSON.stringify({ state: "Failed", message: "Link already done" }) + ); + } else { + console.log(result); + res.status(200); + const stmt2 = "DELETE FROM mailverification WHERE id=?"; + console.log(result[0].id); + con.query(stmt2, [result[0].id], function (err, result, fields) { + // TODO handling of this + //logger.debug(err) + //console.log(result) + }); + const stmt3 = "UPDATE users SET verificationState=1 WHERE email=?"; + con.query( + stmt3, + [result[0].targetMail], + function (err, result, fields) { + // TODO handling of this + //logger.debug(err) + //console.log(result) + } + ); + res.send(JSON.stringify({ state: "OK", message: "Done!" })); + } + } + }); + }); + + + + app.post("/register", function (req, res) { + const sess = req.session; + let resu; + verify(jsonConfig.hCaptcha.secret, req.body["g-recaptcha-response"]).then( + (data) => (resu = data) + ); + /*.catch(setTimeout(() => { + //if(resu.success == false){ + console.log("HERE"); + const data = fs.readFileSync("templates/genericError.html", "utf8"); + resu = "-1"; + con + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Error", + fontawesomeKey: jsonConfig.fontAwesome, + displayText: "There was an issue with the Captcha", + }) + ); + //} + + }, 0) + );*/ + + if (req.body.pass == req.body.pass2) { + + if (mailRegex.test(req.body.email)) { + setTimeout(() => { + console.log(resu); + if (resu.success == true) { + bcrypt.hash(req.body.pass, saltRounds, (err, hash) => { + const data = fs.readFileSync( + "templates/genericError.html", + "utf8" + ); + // SQL INSERT + + const stmt = + "INSERT INTO users(email, username, passwordHash) VALUES(?, ?, ?)"; + const stmt2 = + "INSERT INTO mailverification(targetMail, userID, token) VALUES(?, ?, ?)"; + cryptoF.randomBytes(48, function (err, buffer) { + const token = buffer.toString("hex"); + con.query( + stmt, + [req.body.email, req.body.username, hash], + (err, results1) => { + if (err) { + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Error", + fontawesomeKey: jsonConfig.fontAwesome, + displayText: + "An error occured while creating your account.", + }) + ); + return console.error(err.message); + } else { + // Create mail verification + con.query( + stmt2, + [req.body.email, results1.insertId, token], + (err, results) => { + if (err) { + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Error", + fontawesomeKey: jsonConfig.fontAwesome, + displayText: + "An error occured while creating your account.", + }) + ); + return console.error(err.message); + } else { + sess.username = req.body.username; + sess.uid = String(results1.insertId); + sess.mail = req.body.email; + // get inserted id + logger.info("Inserted Id:" + results.insertId); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Error", + fontawesomeKey: jsonConfig.fontAwesome, + displayText: "OK " + hash, + }) + ); + sendVerificationMail(results.insertId); + } + } + ); + } + } + ); + }); + }); + } else { + const data = fs.readFileSync("templates/register.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Register", + fontawesomeKey: jsonConfig.fontAwesome, + sitekey: jsonConfig.hCaptcha.sitekey, + error: true, + errorMessage: "You failed the captcha, please try again.", + }) + ); + } + }, 200); + } else { + // Passwords don't match up + const data = fs.readFileSync("templates/register.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Register", + fontawesomeKey: jsonConfig.fontAwesome, + sitekey: jsonConfig.hCaptcha.sitekey, + error: true, + errorMessage: "The E-Mail given is not valid", + }) + ); + } + } else { + // Passwords don't match up + const data = fs.readFileSync("templates/register.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Register", + fontawesomeKey: jsonConfig.fontAwesome, + sitekey: jsonConfig.hCaptcha.sitekey, + error: true, + errorMessage: "The password have to match up.", + }) + ); + } + }); + + + app.get("/register", function (req, res) { + const data = fs.readFileSync("templates/register.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Register", + fontawesomeKey: jsonConfig.fontAwesome, + sitekey: jsonConfig.hCaptcha.sitekey, + }) + ); + + }); + + + + app.get("/profile", function (req, res) { + if (req.session.username != undefined) { + let greeting = greetingTime(new Date()); + greeting += req.session.username; + const hash = cryptoF + .createHash("md5") + .update(req.session.mail.replace(" ", "").toLowerCase()) + .digest("hex"); + const gravatarURL = "https://www.gravatar.com/avatar/" + hash; + const data = fs.readFileSync("templates/profile.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Profile", + fontawesomeKey: jsonConfig.fontAwesome, + greeting: greeting, + gravatarURL: gravatarURL, + }) + ); + } else { + const data = fs.readFileSync("templates/redirect.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Profile", + fontawesomeKey: jsonConfig.fontAwesome, + url: "/login", + }) + ); + } + + }); + + app.get("/login", function (req, res) { + + const data = fs.readFileSync("templates/login.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Login", + fontawesomeKey: jsonConfig.fontAwesome, + }) + ); + + }); + + + app.post("/login", function (req, res) { + const password = req.body.pass; + const mail = req.body.email; + const sess = req.session; + console.log(req.body.pass); + + // Check if okay + if ( + mail != undefined && + mail != "" && + password != undefined && + password != "" + ) { + if (mailRegex.test(mail)) { + const stmt = "SELECT * FROM users WHERE email='?';"; + con.query(stmt, [mail], function (err, result) { + if (err) throw err; // TODO proper error page + if (result.length == 0) { + const data = fs.readFileSync("templates/login.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Ok", + fontawesomeKey: jsonConfig.fontAwesome, + error: true, + errorMessage: "This user does not exist!", + }) + ); + } else { + bcrypt.compare( + password, + result[0].passwordHash, + function (error, response) { + if (response) { + // Login okay + sess.username = result[0].username; + sess.uid = String(result[0].id); + sess.mail = result[0].email; + + const data = fs.readFileSync("templates/redirect.html", "utf8"); + if (req.query.r != undefined && req.query.r != "") { + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Ok", + fontawesomeKey: jsonConfig.fontAwesome, + url: req.query.r, + }) + ); + + } else { + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Ok", + fontawesomeKey: jsonConfig.fontAwesome, + url: "/profile", + }) + ); + } + + } else { + // Password falsch + const data = fs.readFileSync("templates/login.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Ok", + fontawesomeKey: jsonConfig.fontAwesome, + error: true, + errorMessage: "The given password is wrong.", + }) + ); + } + } + ); + } + }); + } else { + const data = fs.readFileSync("templates/login.html", "utf8"); + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Ok", + fontawesomeKey: jsonConfig.fontAwesome, + error: true, + errorMessage: "The given E-Mail is invalid.", + }) + ); + } + } else { + logger.warn( + "The login form did not sent all data. Dump: \n Password: " + + password + + " \n E-Mail: " + + mail + ); + const data = fs.readFileSync("templates/genericError.html", "utf8"); + const displayText = "The form did not sent all the information needed."; + res.send( + Eta.render(data, { + author: metainfo.author, + desc: metainfo.desc, + siteTitel: metainfo.sitePrefix + "Error", + fontawesomeKey: jsonConfig.fontAwesome, + displayText: displayText, + }) + ); + } + }); + + // sendVerificationMail(2); + function sendVerificationMail(userId) { + // Query for the mail + const stmt = "SELECT * FROM mailverification WHERE id=?";// + userId; + con.query(stmt, [userId], function (err, result, fields) { + if (err) throw err; // TODO proper error handling + if (result.length == 0) { + logger.warn( + "sendVerificationMail failed because ID " + userId + " doesnt exist!" + ); + } else { + const emailContent = + "Hi! \n You have created an account for the open elevator map. To finalize the process please verify your E-Mail adress. Use this link: http://" + + serverAdress + + "/verify/" + + result[0].token; + transport.sendMail({ + from: '"Elevator map " <' + mailConf.username + ">", // sender address + to: result[0].targetMail, // list of receivers + subject: "[Elevator map] Please verify your Mailadress", // Subject line + text: emailContent, // plain text body + html: emailContent.replace("\n", "
"), // html body + }); + } + + console.log(result); + }); + + /* + let info = await transporter.sendMail({ + from: '"Elevator map " <' + mysqlData.username + '>', // sender address + to: "bar@example.com, baz@example.com", // list of receivers + subject: "Hello ✔", // Subject line + text: "Hello world?", // plain text body + html: "Hello world?", // html body + });*/ + } + + +} \ No newline at end of file diff --git a/routes/debug.route.ts b/routes/debug.route.ts new file mode 100644 index 0000000..1a5e27c --- /dev/null +++ b/routes/debug.route.ts @@ -0,0 +1,6 @@ +module.exports = function (app) { + app.get("/debug/showSessionInfo", function (req, res) { + res.send(JSON.stringify(req.session)); + }); + +} \ No newline at end of file