Files
elevatormapRewritten/index.ts

333 lines
9.8 KiB
TypeScript

// 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 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();
const startUpTime = Math.floor(new Date().getTime() / 1000);
// Skeleton Variables
let fontawesomeKey = "";
let mapboxAccessToken = "";
let mysqlData = { "user": "", "password": "", "database": "", "allowCreation": false };
let mailConf = { "host": "", "port": 0, "username": "", "password": "" };
let serverAdress = "";
let cookieSecret = ""
let jsonConfigGlobal = {};
let port = 3000;
// 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;
port = jsonConfig.port;
cookieSecret =
jsonConfig.cookieSecret || "saF0DSF65AS4DF0S4D6F0S54DF0Fad";
jsonConfigGlobal = jsonConfig;
} catch (error) {
logger.error(
"While reading the config an error occured. The error was: " + error
);
}
// Express (server) preperation
app.use(express.static("static"));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(session({ secret: cookieSecret }));
app.use(function (req, res, next) {
// PreShow Errorpage handler
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) {
if (req.session.username == undefined) {
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")
}
})
// Mail preperation
const transport = nodemailer.createTransport({
host: mailConf.host,
port: mailConf.port,
requireTLS: true,
secure: false,
debug: true,
disableFileAccess: true,
auth: {
user: mailConf.username,
pass: mailConf.password,
},
});
logger.info("Testing SMTP connection");
transport.verify(function (error) {
if (error) {
logger.error(error);
} else {
logger.info("SMPT server is ready to accept messages");
}
});
// 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) { // Index page
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) { // Map page showing all elevators
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) { // Page to create a new elvator
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) { // SQL reconnect
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;
}
});
}
}, 60*1000); // Every minute
// App start
app.listen(port, () => {
logger.info(`Elevator map ready at http://localhost:${port}`);
});