27 Commits

Author SHA1 Message Date
dependabot[bot]
9523ed49b6 Bump async from 3.2.1 to 3.2.4
Bumps [async](https://github.com/caolan/async) from 3.2.1 to 3.2.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/master/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v3.2.1...v3.2.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-22 17:28:53 +00:00
TheGreyDiamond
fe5090a382 Merge pull request #4 from TheGreyDiamond/dependabot/npm_and_yarn/minimist-1.2.6
Bump minimist from 1.2.5 to 1.2.6
2022-03-30 12:48:30 +02:00
dependabot[bot]
4e576841f8 Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-30 02:33:35 +00:00
TheGreyDiamond
467e635145 Merge pull request #3 from TheGreyDiamond/refactor
General code maintanace
2021-10-13 14:27:59 +02:00
TheGreyDiamond
81b9d0b26f Removed unused lines, Fixed up some linting errors, more code documentation, updated some dep versions, and fixed error in package.json 2021-10-13 14:23:01 +02:00
TheGreyDiamond
2e1eec4b7a Merge pull request #2 from TheGreyDiamond/dependabot/npm_and_yarn/tar-6.1.11
Bump tar from 6.1.5 to 6.1.11
2021-09-01 08:17:33 +02:00
dependabot[bot]
7134466431 Bump tar from 6.1.5 to 6.1.11
Bumps [tar](https://github.com/npm/node-tar) from 6.1.5 to 6.1.11.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.5...v6.1.11)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-31 23:31:45 +00:00
TheGreyDiamond
808fa94a1a Merge pull request #1 from TheGreyDiamond/dependabot/npm_and_yarn/tar-6.1.5
Bump tar from 6.1.0 to 6.1.5
2021-08-04 22:33:35 +02:00
dependabot[bot]
f2bef7aa43 Bump tar from 6.1.0 to 6.1.5
Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.5.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.5)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-04 03:58:06 +00:00
TheGreyDiamond
3c0b455341 Update map.html 2021-07-11 21:47:32 +02:00
TheGreyDiamond
5a87784ceb Update map.html 2021-07-11 21:44:59 +02:00
TheGreyDiamond
e69e40fec8 Update README.md 2021-07-09 14:42:16 +02:00
TheGreyDiamond
eadf729190 Update README.md 2021-07-09 14:42:04 +02:00
TheGreyDiamond
1377ae15ba Update README.md 2021-07-06 21:10:47 +02:00
TheGreyDiamond
e33a58e5ab Fixed middelware, put createElevator code into new file, fixed greeting format 2021-07-06 17:53:03 +02:00
TheGreyDiamond
fe33f7f316 Added a proper favicon 2021-07-06 17:12:10 +02:00
TheGreyDiamond
ec01e7128b Delete index.js 2021-07-06 17:07:34 +02:00
TheGreyDiamond
7eb0558e96 Get port from config 2021-07-06 17:01:34 +02:00
TheGreyDiamond
5f8c998aaa Updated some stuff 2021-07-06 16:57:39 +02:00
TheGreyDiamond
d1aa2868d7 Update index.ts 2021-07-06 14:24:01 +02:00
TheGreyDiamond
ee13ae9cb2 Patched a broken / missing link 2021-07-06 14:14:57 +02:00
TheGreyDiamond
0dfd47e258 Cleanup 2021-07-06 14:13:14 +02:00
TheGreyDiamond
bc2a66236e Fixed an issues with account creation. How did this ever work? 2021-07-05 21:36:20 +02:00
TheGreyDiamond
7d5fdcef9b Wooops fix 2021-07-05 21:12:09 +02:00
TheGreyDiamond
b2708329c1 Added a license 2021-07-05 20:58:35 +02:00
TheGreyDiamond
51d92c139d Added buttons to create a new elevator 2021-07-05 20:45:57 +02:00
TheGreyDiamond
b05241f493 Added names to submissions and made create elevator login protected 2021-07-05 20:43:01 +02:00
29 changed files with 9093 additions and 1785 deletions

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules
dist
*.js

17
.eslintrc.json Normal file
View File

@@ -0,0 +1,17 @@
{
"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-control-regex": 0
}
}

4
.gitignore vendored
View File

@@ -80,7 +80,6 @@ typings/
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
@@ -104,3 +103,6 @@ dist
.tern-port
testingDONOTCOMMITME.json
static/uploads/*
index.js

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright © 2021 TheGreydiamond
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,2 +1,20 @@
# Elevatormap Rewritten
The elevator map at thegreydiamond.de/elevatormap has been offline for some time now, as it was very ineffective and didn't accept user contributions. This is an attempted by the original author to rewrite the project in Node.js.
## Host your own
Requirements:
- Node
- MySQL Server (MariaDB)
- Accounts for Fontawesome & hCaptcha
Setup steps:
1. Setup DB access
2. Change config file to your needs
3. Install dependencies with `npm install`
4. Make .js file with `npm run makeJS` (skip if you want to test it)
5. Start it with `npm start` (or use pm2)
It will autogenerate all tables needed. And then startup.
ToDo:
- [ ] Allow user edits
- [ ] Allow moderation

View File

@@ -1,9 +1,24 @@
{
"fontAwesome": "",
"mapboxAccessToken": "",
"cookieSecret": "",
"serverAdress": "",
"port": 3000,
"mysql": {
"user": "",
"password": "",
"database": "",
"allowCreation": true
},
"hCaptcha":{
"sitekey": "",
"secret": ""
},
"mail": {
"host": "",
"port": 0,
"username": "",
"password": ""
}
}

BIN
etc/elevatormapLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
etc/elevatormapLogo.xcf Normal file

Binary file not shown.

1109
index.js

File diff suppressed because it is too large Load Diff

332
index.ts Normal file
View File

@@ -0,0 +1,332 @@
// 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}`);
});

4884
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,30 +4,44 @@
"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",
"url": "git+https://github.com/TheGreyDiamond/elevatormapRewritten.git"
},
"author": "TheGreydiamond",
"license": "ISC",
"license": "MIT",
"bugs": {
"url": "https://github.com/TheGreyDiamond/elevatormapRewritten/issues"
},
"homepage": "https://github.com/TheGreyDiamond/elevatormapRewritten#readme",
"homepage": "https://github.com/TheGreyDiamond/elevatormapRewritten",
"dependencies": {
"bcrypt": "^5.0.1",
"body-parser": "^1.19.0",
"eta": "^1.12.1",
"eta": "^1.12.3",
"express": "^4.17.1",
"express-session": "^1.17.2",
"hcaptcha": "0.0.2",
"greeting-time": "^1.0.0",
"hcaptcha": "0.1.0",
"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.33.0",
"@typescript-eslint/parser": "^4.33.0",
"eslint": "^7.32.0",
"eslint-config-strongloop": "^2.1.0",
"typescript": "^4.2.4"
}
}

311
routes/api.route.ts Normal file
View File

@@ -0,0 +1,311 @@
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" }));
}
});
}

443
routes/auth.route.ts Normal file
View File

@@ -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://" +
jsonConfig.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", "<br>"), // 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: "<b>Hello world?</b>", // html body
});*/
}
}

6
routes/debug.route.ts Normal file
View File

@@ -0,0 +1,6 @@
module.exports = function (app) {
app.get("/debug/showSessionInfo", function (req, res) {
res.send(JSON.stringify(req.session));
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 B

After

Width:  |  Height:  |  Size: 9.2 KiB

267
static/js/createElevator.js Normal file
View File

@@ -0,0 +1,267 @@
var lockMap = false;
function noRestore() {
off();
Cookies.remove("tempStore")
}
function restoreFunc() {
try {
dataBlock = JSON.parse(dataBlock)
document.getElementById("lat").value = dataBlock["lat"]
document.getElementById("lng").value = dataBlock["lng"]
document.getElementById("type").value = dataBlock["type"]
document.getElementById("visit").value = dataBlock["visit"]
document.getElementById("pepl").value = dataBlock["pepl"]
document.getElementById("weig").value = dataBlock["weig"]
document.getElementById("manuf").value = dataBlock["manuf"]
document.getElementById("model").value = dataBlock["model"]
document.getElementById("flor").value = dataBlock["flor"]
document.getElementById("description").value = dataBlock["description"]
} catch (ex) {
dataBlock = {}
}
off()
}
dataBlock = Cookies.get("tempStore");
if (dataBlock == undefined) {
dataBlock = {};
} else {
on()
}
var currentPage = 0;
function saveValues() {
dataBlock["lat"] = document.getElementById("lat").value
dataBlock["lng"] = document.getElementById("lng").value
dataBlock["type"] = document.getElementById("type").value
dataBlock["visit"] = document.getElementById("visit").value
dataBlock["pepl"] = document.getElementById("pepl").value
dataBlock["weig"] = document.getElementById("weig").value
dataBlock["manuf"] = document.getElementById("manuf").value
dataBlock["model"] = document.getElementById("model").value
dataBlock["flor"] = document.getElementById("flor").value
dataBlock["description"] = document.getElementById("description").value
Cookies.set('tempStore', JSON.stringify(dataBlock))
}
function submit() {
currentPage = 6;
updateDialog()
saveValues()
const options = {
method: 'POST',
body: JSON.stringify({})
};
fetch('/api/saveNewElevatorMeta', options)
.then(response => response.json())
.then(response => {
console.warn("!!!!!!!!!!!!", response)
document.getElementById("imageUploadInfo").style.display = 'block';
var filesToSend = $('#myFile').prop('files').length;
var i = 0;
while (i < filesToSend) {
document.getElementById("imageUploadInfo").innerHTML = "Uploading image " + String(i) + "/" + String(filesToSend)
console.log("Files left to send: ", filesToSend - i)
var file_data = $('#myFile').prop('files')[i];
var form_data = new FormData();
form_data.append('file', file_data);
console.log(file_data)
if (String(file_data.type).includes("image/")) {
$.ajax({
url: '/api/uploadImage?id=' + response.id,
dataType: 'json',
cache: false,
contentType: false,
processData: false,
data: form_data,
type: 'post',
success: function (data) {
alert(data);
}
});
} else {
console.log("Skipping nonimage file")
}
i++;
}
console.log("DONE!")
});
}
function updateDialog() {
if (currentPage == 0) {
document.getElementById("step1").style.display = 'block';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
}
if (currentPage == 1) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'block';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
}
if (currentPage == 2) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'block';
document.getElementById("step4").style.display = 'none';
lockMap = false;
}
if (currentPage == 3) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'block';
document.getElementById("step5").style.display = 'none';
document.getElementById("missingAlert").style.display = 'none';
lockMap = true;
}
if (currentPage == 4) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
containsEmpt = false;
for (const [key, value] of Object.entries(dataBlock)) {
if (value == "" || value == undefined) {
console.log("hi")
console.log(key, value)
containsEmpt = true;
document.getElementById("missingAlert").style.display = 'block';
}
}
if (containsEmpt == false) {
document.getElementById("missingAlert").style.display = 'none';
currentPage = 5;
}
console.log(containsEmpt);
}
if (currentPage == 5) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
document.getElementById("step5").style.display = 'block';
document.getElementById("missingAlert").style.display = 'none';
}
if (currentPage == 6) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
document.getElementById("step5").style.display = 'none';
document.getElementById("step6").style.display = 'block';
document.getElementById("missingAlert").style.display = 'none';
}
saveValues();
console.log(dataBlock)
}
function nextDialogePage() {
currentPage++;
updateDialog();
}
function prevPage() {
currentPage--;
updateDialog();
}
var latElm = document.getElementById("lat");
var lngElm = document.getElementById("lng");
latElm.addEventListener('input', function (evt) {
if(!lockMap){
markers.clearLayers();
console.log(evt.target.value)
const lat = evt.target.value;
const lng = lngElm.value;
var marker = new theMarker([lat, lng])
//marker.addTo(mymap)
markers.addLayer(marker);
markers.addTo(mymap);
}
});
lngElm.addEventListener('input', function (evt) {
if(!lockMap){
markers.clearLayers();
console.log(evt.target.value)
const lat = latElm.value;
const lng = evt.target.value;
var marker = new theMarker([lat, lng])
//marker.addTo(mymap)
markers.addLayer(marker);
markers.addTo(mymap);
}
});
var amountOfImages = 0;
var markers = L.markerClusterGroup();
slideIndex = 1;
var mymap = L.map("map").setView([51.505, -0.09], 50);
theMarker = L.Marker.extend({
options: {
id: "-1",
},
});
function showPosition(position) {
console.log(position.coords);
mymap.setView(
new L.LatLng(position.coords.latitude, position.coords.longitude),
10
);
// mymap.setView(new L.LatLng(10.737, -73.923), 8);
}
home()
mymap.on('click', function (e) {
if(!lockMap){
markers.clearLayers();
var coord = e.latlng;
var lat = coord.lat;
var lng = coord.lng;
var marker = new theMarker([lat, lng])
//marker.addTo(mymap)
markers.addLayer(marker);
markers.addTo(mymap);
document.getElementById("lat").value = lat
document.getElementById("lng").value = lng
console.log("You clicked the map at latitude: " + lat + " and longitude: " + lng);
}else{
console.log("The map is locked.")
}
});
function addPin(item, index) {
var marker = new theMarker([item.lat, item.lng], {
id: item.id,
}).on("click", onClick);
// var marker = new L.Marker()
//marker.addTo(mymap).on('click', onClick);
markers.on("clusterclick", function (a) {
//alert('cluster ' + a.layer.getAllChildMarkers().length);
});
markers.addLayer(marker);
}

192
static/js/map.js Normal file
View File

@@ -0,0 +1,192 @@
function createNewElev() {
window.location.href = "/createElevator";
}
var amountOfImages = 0;
var markers = L.markerClusterGroup();
slideIndex = 1;
theMarker = L.Marker.extend({
options: {
id: "-1",
},
});
function showPosition(position) {
console.log(position.coords);
mymap.setView(
new L.LatLng(position.coords.latitude, position.coords.longitude),
10
);
// mymap.setView(new L.LatLng(10.737, -73.923), 8);
}
function onClick(e) {
slideIndex = 1;
document.getElementById("inspector").innerHTML =
'<br><br><center><div class="lds-ripple"><div></div><div></div></div></center>';
res = JSON.parse(httpGet("/api/getElevatorById?id=" + this.options.id));
if (res.state == "Ok") {
visitStates = [
"Test elevator",
"Public",
"On private property",
"Public but locked",
];
typeStates = [
"Hydraulic",
"Wiredriven, motor in shaft",
"Wiredriven, motor in motorroom",
];
// Prepare the template
inspector = httpGet("/templates/inspectorContent.html");
inspector = inspector.replace("#MODELL", res.results[0].modell);
inspector = inspector.replace("#MANUF", res.results[0].manufacturer);
inspector = inspector.replace("#DESC", res.results[0].info);
inspector = inspector.replace(
"#TYPE",
typeStates[res.results[0].technology]
);
inspector = inspector.replace("#MAXPASS", res.results[0].maxPassangers);
inspector = inspector.replace("#MASSWEIGH", res.results[0].maxWeight);
inspector = inspector.replace(
"#VISIT",
visitStates[res.results[0].visitabilty]
);
try {
var username = JSON.parse(
httpGet("/api/resolveNameById?id=" + res.results[0].creator)
).results[0].username;
} catch {
username = "Unknown";
}
inspector = inspector.replace("#CREATOR", username);
document.getElementById("inspector").innerHTML = inspector;
// Make gallery
document.getElementById("imageGallery").innerHTML =
"<div class='slideshow-container'>";
imgs = JSON.parse(res.results[0].images);
amountOfImages = imgs.images.length;
console.log(imgs);
var iH = 0;
while (amountOfImages > iH) {
newBox = "<center><div class='mySlides fade'><div class='numbertext'>";
newBox += iH + 1;
newBox += "/";
newBox += amountOfImages;
newBox += "</div><img src='";
newBox += imgs.images[iH].path;
newBox +=
"' alt='" +
imgs.images[iH].alt +
"' class=\"elevatorPhoto\"><div class='text'> </div></div></center>";
document.getElementById("imageGallery").innerHTML += newBox;
iH++;
}
document.getElementById("imageGallery").innerHTML +=
"<br><a class='prev' onclick='plusSlides(-1)''>&#10094;</a><a class='next' onclick='plusSlides(1)'>&#10095;</a></div><br><div style='text-align:center'></div><br><br><br>";
showSlides(1);
} else {
document.getElementById("inspector").innerHTML =
' \
<center> \
<h1><i style="color: red;" class="fas fa-exclamation-triangle"></i></h1> \
<h1>Oh no!</h1> \
The website failed to fetch the information about this elevator. It responded with the error code: \
<br><code> \
' +
res.message +
"</code><center>";
}
console.log(res);
}
function httpGet(theUrl) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", theUrl, false); // false for synchronous request
xmlHttp.send(null);
return xmlHttp.responseText;
}
function home() {
if (navigator.geolocation) {
setTimeout(function () {
navigator.geolocation.getCurrentPosition(showPosition);
}, 200);
} else {
console.warn("Geolocation of user could not be fetched");
}
}
home();
function addPin(item, index) {
var marker = new theMarker([item.lat, item.lng], {
id: item.id,
}).on("click", onClick);
// var marker = new L.Marker()
//marker.addTo(mymap).on('click', onClick);
markers.on("clusterclick", function (a) {
//alert('cluster ' + a.layer.getAllChildMarkers().length);
});
markers.addLayer(marker);
}
// Start getting the elevators
response = httpGet(
"/api/getElevatorLocation?lan=" +
mymap.getCenter.lng +
"&lat=" +
mymap.getCenter.lat +
"&radius=" +
mymap.getZoom()
);
response = JSON.parse(response);
if (response.state == "Ok") {
response.results.forEach(addPin);
mymap.addLayer(markers);
} else {
// DONT FORGET TO SHOW POPUP OR SOMETHING
console.log("Request failed with " + response.message);
console.log(response);
alert("Loading of the map pins failed");
}
// Next/previous controls
function plusSlides(n) {
showSlides((slideIndex += n));
}
// Thumbnail image controls
function currentSlide(n) {
showSlides((slideIndex = n));
}
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides");
var dots = document.getElementsByClassName("dot");
if (n > slides.length) {
slideIndex = 1;
}
if (n < 1) {
slideIndex = slides.length;
}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex - 1].style.display = "block";
dots[slideIndex - 1].className += " active";
}

View File

@@ -0,0 +1,60 @@
.marker-cluster-small {
background-color: rgba(181, 226, 140, 0.6);
}
.marker-cluster-small div {
background-color: rgba(110, 204, 57, 0.6);
}
.marker-cluster-medium {
background-color: rgba(241, 211, 87, 0.6);
}
.marker-cluster-medium div {
background-color: rgba(240, 194, 12, 0.6);
}
.marker-cluster-large {
background-color: rgba(253, 156, 115, 0.6);
}
.marker-cluster-large div {
background-color: rgba(241, 128, 23, 0.6);
}
/* IE 6-8 fallback colors */
.leaflet-oldie .marker-cluster-small {
background-color: rgb(181, 226, 140);
}
.leaflet-oldie .marker-cluster-small div {
background-color: rgb(110, 204, 57);
}
.leaflet-oldie .marker-cluster-medium {
background-color: rgb(241, 211, 87);
}
.leaflet-oldie .marker-cluster-medium div {
background-color: rgb(240, 194, 12);
}
.leaflet-oldie .marker-cluster-large {
background-color: rgb(253, 156, 115);
}
.leaflet-oldie .marker-cluster-large div {
background-color: rgb(241, 128, 23);
}
.marker-cluster {
background-clip: padding-box;
border-radius: 20px;
}
.marker-cluster div {
width: 30px;
height: 30px;
margin-left: 5px;
margin-top: 5px;
text-align: center;
border-radius: 15px;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
}
.marker-cluster span {
line-height: 30px;
}

View File

@@ -0,0 +1,14 @@
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
-webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
-moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
-o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
}
.leaflet-cluster-spider-leg {
/* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
-webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
-moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
-o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
}

View File

@@ -0,0 +1,5 @@
We don't ship the .js files in the git master branch.
They are only present in version tags and in npm.
See how to get the JS files here: https://github.com/Leaflet/Leaflet.markercluster#using-the-plugin
Or how to build them: https://github.com/Leaflet/Leaflet.markercluster#building-testing-and-linting-scripts

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -12,5 +12,7 @@
<b>Type:</b> #TYPE <br>
<b>Max. Passerngers:</b> #MAXPASS / #MASSWEIGH (kg) <br>
<b>Visitable:</b> #VISIT
<b>Visitable:</b> #VISIT <br>
<i>Created by: #CREATOR</i>
</center>

View File

@@ -158,221 +158,11 @@
<aside>
<i style="color: black; cursor: pointer" class="fas fa-map-marker-alt" onclick="home()"></i>
</aside>
<script type="text/javascript">
function noRestore() {
off();
Cookies.remove("tempStore")
}
function restoreFunc() {
try {
dataBlock = JSON.parse(dataBlock)
document.getElementById("lat").value = dataBlock["lat"]
document.getElementById("lng").value = dataBlock["lng"]
document.getElementById("type").value = dataBlock["type"]
document.getElementById("visit").value = dataBlock["visit"]
document.getElementById("pepl").value = dataBlock["pepl"]
document.getElementById("weig").value = dataBlock["weig"]
document.getElementById("manuf").value = dataBlock["manuf"]
document.getElementById("model").value = dataBlock["model"]
document.getElementById("flor").value = dataBlock["flor"]
document.getElementById("description").value = dataBlock["description"]
} catch (ex) {
dataBlock = {}
}
off()
}
dataBlock = Cookies.get("tempStore");
if (dataBlock == undefined) {
dataBlock = {};
} else {
on()
}
var currentPage = 0;
function saveValues() {
dataBlock["lat"] = document.getElementById("lat").value
dataBlock["lng"] = document.getElementById("lng").value
dataBlock["type"] = document.getElementById("type").value
dataBlock["visit"] = document.getElementById("visit").value
dataBlock["pepl"] = document.getElementById("pepl").value
dataBlock["weig"] = document.getElementById("weig").value
dataBlock["manuf"] = document.getElementById("manuf").value
dataBlock["model"] = document.getElementById("model").value
dataBlock["flor"] = document.getElementById("flor").value
dataBlock["description"] = document.getElementById("description").value
Cookies.set('tempStore', JSON.stringify(dataBlock))
}
function submit() {
currentPage = 6;
updateDialog()
saveValues()
const options = {
method: 'POST',
body: JSON.stringify({})
};
fetch('/api/saveNewElevatorMeta', options)
.then(response => response.json())
.then(response => {
console.warn("!!!!!!!!!!!!", response)
document.getElementById("imageUploadInfo").style.display = 'block';
var filesToSend = $('#myFile').prop('files').length;
var i = 0;
while (i < filesToSend) {
document.getElementById("imageUploadInfo").innerHTML = "Uploading image " + String(i) + "/" + String(filesToSend)
console.log("Files left to send: ", filesToSend - i)
var file_data = $('#myFile').prop('files')[i];
var form_data = new FormData();
form_data.append('file', file_data);
console.log(file_data)
if (String(file_data.type).includes("image/")) {
$.ajax({
url: '/api/uploadImage?id=' + response.id,
dataType: 'json',
cache: false,
contentType: false,
processData: false,
data: form_data,
type: 'post',
success: function (data) {
alert(data);
}
});
} else {
console.log("Skipping nonimage file")
}
i++;
}
});
}
function updateDialog() {
if (currentPage == 0) {
document.getElementById("step1").style.display = 'block';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
}
if (currentPage == 1) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'block';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
}
if (currentPage == 2) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'block';
document.getElementById("step4").style.display = 'none';
}
if (currentPage == 3) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'block';
document.getElementById("step5").style.display = 'none';
document.getElementById("missingAlert").style.display = 'none';
}
if (currentPage == 4) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
containsEmpt = false;
for (const [key, value] of Object.entries(dataBlock)) {
if (value == "" || value == undefined) {
console.log("hi")
console.log(key, value)
containsEmpt = true;
document.getElementById("missingAlert").style.display = 'block';
}
}
if (containsEmpt == false) {
document.getElementById("missingAlert").style.display = 'none';
currentPage = 5;
}
console.log(containsEmpt);
}
if (currentPage == 5) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
document.getElementById("step5").style.display = 'block';
document.getElementById("missingAlert").style.display = 'none';
}
if (currentPage == 6) {
document.getElementById("step1").style.display = 'none';
document.getElementById("step2").style.display = 'none';
document.getElementById("step3").style.display = 'none';
document.getElementById("step4").style.display = 'none';
document.getElementById("step5").style.display = 'none';
document.getElementById("step6").style.display = 'block';
document.getElementById("missingAlert").style.display = 'none';
}
saveValues();
console.log(dataBlock)
}
function nextDialogePage() {
currentPage++;
updateDialog();
}
function prevPage() {
currentPage--;
updateDialog();
}
</script>
<!-- End Document
-->
<script src="./js/createElevator.js"></script>
<script type="text/javascript">
var latElm = document.getElementById("lat");
var lngElm = document.getElementById("lng");
latElm.addEventListener('input', function (evt) {
markers.clearLayers();
console.log(evt.target.value)
const lat = evt.target.value;
const lng = lngElm.value;
var marker = new theMarker([lat, lng])
//marker.addTo(mymap)
markers.addLayer(marker);
markers.addTo(mymap);
});
lngElm.addEventListener('input', function (evt) {
markers.clearLayers();
console.log(evt.target.value)
const lat = latElm.value;
const lng = evt.target.value;
var marker = new theMarker([lat, lng])
//marker.addTo(mymap)
markers.addLayer(marker);
markers.addTo(mymap);
});
var amountOfImages = 0;
var markers = L.markerClusterGroup();
slideIndex = 1;
var mymap = L.map("map").setView([51.505, -0.09], 50);
L.tileLayer(
"https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=<%= it.mapboxAccessToken %>",
{
@@ -385,51 +175,6 @@
accessToken: "<%= it.mapboxAccessToken %>",
}
).addTo(mymap);
theMarker = L.Marker.extend({
options: {
id: "-1",
},
});
function showPosition(position) {
console.log(position.coords);
mymap.setView(
new L.LatLng(position.coords.latitude, position.coords.longitude),
10
);
// mymap.setView(new L.LatLng(10.737, -73.923), 8);
}
home()
mymap.on('click', function (e) {
markers.clearLayers();
var coord = e.latlng;
var lat = coord.lat;
var lng = coord.lng;
var marker = new theMarker([lat, lng])
//marker.addTo(mymap)
markers.addLayer(marker);
markers.addTo(mymap);
document.getElementById("lat").value = lat
document.getElementById("lng").value = lng
console.log("You clicked the map at latitude: " + lat + " and longitude: " + lng);
});
function addPin(item, index) {
var marker = new theMarker([item.lat, item.lng], {
id: item.id,
}).on("click", onClick);
// var marker = new L.Marker()
//marker.addTo(mymap).on('click', onClick);
markers.on("clusterclick", function (a) {
//alert('cluster ' + a.layer.getAllChildMarkers().length);
});
markers.addLayer(marker);
}
</script>
</body>

View File

@@ -50,6 +50,7 @@
<h5>
Check it out now <br>
<a href= "/map" ><button class="button-primary">Visit the map <i class="fas fa-arrow-right"></i> </button></a>
<a href= "/createElevator" ><button class="button-primary">Create a new elevator <i class="fas fa-arrow-right"></i> </button></a>
</h5>
</div>

View File

@@ -88,7 +88,7 @@
Dont have an account?
</span>
<a class="txt2" href="#">
<a class="txt2" href="/register">
Sign Up
</a>
</div>

View File

@@ -72,207 +72,28 @@
class="fas fa-map-marker-alt"
onclick="home()"
></i>
<i style="color: black; cursor: pointer" class="fas fas fa-plus" onclick="createNewElev()"></i>
</aside>
<!-- End Document
-->
<script type="text/javascript">
var amountOfImages = 0;
var markers = L.markerClusterGroup();
slideIndex = 1;
<script>
var mymap = L.map("map").setView([51.505, -0.09], 50);
var mymap = L.map("map").setView([51.505, -0.09], 50);
L.tileLayer(
"https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=<%= it.mapboxAccessToken %>",
{
attribution:
'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
maxZoom: 18,
id: "mapbox/streets-v11",
tileSize: 512,
zoomOffset: -1,
accessToken: "<%= it.mapboxAccessToken %>",
}
).addTo(mymap);
theMarker = L.Marker.extend({
options: {
id: "-1",
},
});
function showPosition(position) {
console.log(position.coords);
mymap.setView(
new L.LatLng(position.coords.latitude, position.coords.longitude),
10
);
// mymap.setView(new L.LatLng(10.737, -73.923), 8);
}
function onClick(e) {
slideIndex = 1;
document.getElementById("inspector").innerHTML =
'<br><br><center><div class="lds-ripple"><div></div><div></div></div></center>';
res = JSON.parse(httpGet("/api/getElevatorById?id=" + this.options.id));
if (res.state == "Ok") {
visitStates = [
"Test elevator",
"Public",
"On private property",
"Public but locked",
];
typeStates = [
"Hydraulic",
"Wiredriven, motor in shaft",
"Wiredriven, motor in motorroom",
];
// Prepare the template
inspector = httpGet("/templates/inspectorContent.html");
inspector = inspector.replace("#MODELL", res.results[0].modell);
inspector = inspector.replace("#MANUF", res.results[0].manufacturer);
inspector = inspector.replace("#DESC", res.results[0].info);
inspector = inspector.replace(
"#TYPE",
typeStates[res.results[0].technology]
);
inspector = inspector.replace(
"#MAXPASS",
res.results[0].maxPassangers
);
inspector = inspector.replace("#MASSWEIGH", res.results[0].maxWeight);
inspector = inspector.replace(
"#VISIT",
visitStates[res.results[0].visitabilty]
);
document.getElementById("inspector").innerHTML = inspector;
// Make gallery
document.getElementById("imageGallery").innerHTML =
"<div class='slideshow-container'>";
imgs = JSON.parse(res.results[0].images);
amountOfImages = imgs.images.length;
console.log(imgs);
var iH = 0;
while (amountOfImages > iH) {
newBox =
"<center><div class='mySlides fade'><div class='numbertext'>";
newBox += iH + 1;
newBox += "/";
newBox += amountOfImages;
newBox += "</div><img src='";
newBox += imgs.images[iH].path;
newBox +=
"' alt='" +
imgs.images[iH].alt +
"' class=\"elevatorPhoto\"><div class='text'> </div></div></center>";
document.getElementById("imageGallery").innerHTML += newBox;
iH++;
}
document.getElementById("imageGallery").innerHTML +=
"<br><a class='prev' onclick='plusSlides(-1)''>&#10094;</a><a class='next' onclick='plusSlides(1)'>&#10095;</a></div><br><div style='text-align:center'></div><br><br><br>";
showSlides(1);
} else {
document.getElementById("inspector").innerHTML =
' \
<center> \
<h1><i style="color: red;" class="fas fa-exclamation-triangle"></i></h1> \
<h1>Oh no!</h1> \
The website failed to fetch the information about this elevator. It responded with the error code: \
<br><code> \
' +
res.message +
"</code><center>";
}
console.log(res);
}
function httpGet(theUrl) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", theUrl, false); // false for synchronous request
xmlHttp.send(null);
return xmlHttp.responseText;
}
function home() {
if (navigator.geolocation) {
setTimeout(function () {
navigator.geolocation.getCurrentPosition(showPosition);
}, 200);
} else {
console.warn("Geolocation of user could not be fetched");
}
}
home();
function addPin(item, index) {
var marker = new theMarker([item.lat, item.lng], {
id: item.id,
}).on("click", onClick);
// var marker = new L.Marker()
//marker.addTo(mymap).on('click', onClick);
markers.on("clusterclick", function (a) {
//alert('cluster ' + a.layer.getAllChildMarkers().length);
});
markers.addLayer(marker);
}
// Start getting the elevators
response = httpGet(
"/api/getElevatorLocation?lan=" +
mymap.getCenter.lng +
"&lat=" +
mymap.getCenter.lat +
"&radius=" +
mymap.getZoom()
);
response = JSON.parse(response);
if (response.state == "Ok") {
response.results.forEach(addPin);
mymap.addLayer(markers);
} else {
// DONT FORGET TO SHOW POPUP OR SOMETHING
console.log("Request failed with " + response.message);
console.log(response);
alert("Loading of the map pins failed");
}
// Next/previous controls
function plusSlides(n) {
showSlides((slideIndex += n));
}
// Thumbnail image controls
function currentSlide(n) {
showSlides((slideIndex = n));
}
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides");
var dots = document.getElementsByClassName("dot");
if (n > slides.length) {
slideIndex = 1;
}
if (n < 1) {
slideIndex = slides.length;
}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex - 1].style.display = "block";
dots[slideIndex - 1].className += " active";
}
</script>
L.tileLayer(
"https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=<%= it.mapboxAccessToken %>",
{
attribution:
'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
maxZoom: 100,
minZoom: 2,
id: "mapbox/streets-v11",
tileSize: 512,
zoomOffset: -1,
accessToken: "<%= it.mapboxAccessToken %>",
}
).addTo(mymap);
</script>
<script src="./js/map.js"></script>
</body>
</html>