Created login system

This commit is contained in:
TheGreyDiamond
2021-05-30 00:42:03 +02:00
parent 6e3104eef3
commit 14189365e0
8 changed files with 744 additions and 147 deletions

491
index.js
View File

@ -5,10 +5,12 @@ 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 bcrypt = require("bcrypt");
const { verify } = require("hcaptcha");
const csp = require(`helmet`);
const session = require("express-session");
const nodemailer = require("nodemailer");
const crypto = require("crypto");
// Inting the logger
const logger = winston.createLogger({
@ -55,11 +57,13 @@ app.use(csp.contentSecurityPolicy({
},
}))
*/
// Settings
const port = 3000;
const startUpTime = Math.floor(new Date().getTime() / 1000);
const saltRounds = 10;
// Load config
try {
@ -77,12 +81,42 @@ try {
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 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, success) {
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";
@ -99,6 +133,44 @@ var con = mysql.createConnection({
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", "<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
});*/
}
function checkIfMySQLStructureIsReady() {
if (mysqlIsUpAndOkay) {
// Only if MySQL is ready
@ -118,7 +190,11 @@ function checkIfMySQLStructureIsReady() {
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' , PRIMARY KEY (`id`)) ENGINE = InnoDB;";
"`.`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");
@ -128,6 +204,11 @@ function checkIfMySQLStructureIsReady() {
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(
@ -170,38 +251,104 @@ app.get("/", function (req, res) {
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 data = fs.readFileSync("templates/genericError.html", "utf8");
var displayText =
"There is no error";
// 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.id = result[0].id;
sess.mail = result[0].email;
const data = fs.readFileSync("templates/redirect.html", "utf8");
res.send(
Eta.render(data, {
author: author,
desc: desc,
siteTitel: sitePrefix + "Ok",
fontawesomeKey: fontawesomeKey,
url: "/profile",
})
);
} 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,
displayText: displayText,
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)
}
} 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,
})
);
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,
})
);
}
});
@ -241,9 +388,68 @@ app.get("/login", function (req, res) {
}
});
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) {
@ -254,7 +460,7 @@ app.get("/register", function (req, res) {
desc: desc,
siteTitel: sitePrefix + "Register",
fontawesomeKey: fontawesomeKey,
sitekey: hCaptcha.sitekey
sitekey: hCaptcha.sitekey,
})
);
} else {
@ -282,43 +488,156 @@ app.get("/register", function (req, res) {
}
});
app.post("/register", function (req, res) {
if (mysqlIsUpAndOkay) {
console.log(req.body)
var sess = req.session;
var resu;
verify(hCaptcha.secret, req.body["g-recaptcha-response"])
.then((data) => resu = data)
.catch(setTimeout(() => {
const data = fs.readFileSync("templates/genericError.html", "utf8");
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, token],
(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, hash],
(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 + "Error",
siteTitel: sitePrefix + "Register",
fontawesomeKey: fontawesomeKey,
displayText: "There was an issue with the Captcha",
})
);
}, 0));
setTimeout(() => {
console.log(resu)
if(resu.success == true){
res.send("OK")
}else{
const data = fs.readFileSync("templates/genericError.html", "utf8");
res.send(
Eta.render(data, {
author: author,
desc: desc,
siteTitel: sitePrefix + "Error",
fontawesomeKey: fontawesomeKey,
displayText: "The captcha returned a negativ result",
sitekey: hCaptcha.sitekey,
error: true,
errorMessage: "The password have to match up.",
})
);
}
}, 200);
} else {
const data = fs.readFileSync("templates/dbError.html", "utf8");
var displayText =
@ -344,8 +663,70 @@ app.post("/register", function (req, res) {
}
});
// 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) {

View File

@ -22,9 +22,11 @@
"body-parser": "^1.19.0",
"eta": "^1.12.1",
"express": "^4.17.1",
"express-session": "^1.17.2",
"hcaptcha": "0.0.2",
"helmet": "^4.6.0",
"mysql": "^2.18.1",
"nodemailer": "^6.6.1",
"winston": "^3.3.3"
}
}

View File

@ -476,3 +476,24 @@ there.
/* Larger than tablet */
@media (min-width: 1200px) {}
header {
margin: 0px;
height: 20px;
width: 100%;
position: fixed;
top: 0px;
left: 0px;
border-bottom: solid 1px rgba(73, 73, 73, 0.473)
}
.rightMount {
position: fixed;
right: 10px;
}
/*
* {
border: 1px red solid;
}
*/

View File

@ -40,6 +40,7 @@
margin: 0px;
padding: 0px;
box-sizing: border-box;
transition: 0.2s;
}
body, html {
@ -107,6 +108,8 @@ textarea:focus, input:focus {
border-color: transparent !important;
}
input:focus::-webkit-input-placeholder { color:transparent; }
input:focus:-moz-placeholder { color:transparent; }
input:focus::-moz-placeholder { color:transparent; }
@ -495,5 +498,25 @@ iframe {
}
}
.alert {
padding: 20px;
background-color: #f44336;
color: white;
transition: 0.5s;
}
.closebtn {
margin-left: 15px;
color: white;
font-weight: bold;
float: right;
font-size: 22px;
line-height: 20px;
cursor: pointer;
transition: 0.5s;
}
.closebtn:hover {
color: black;
transition: 0.5s;
}

View File

@ -36,6 +36,19 @@
<div class="container-login100">
<div class="wrap-login100">
<% if(it.error == true) { %>
<div class="alert" id="alert">
<span class="closebtn" id="closeBtn">&times;</span>
<%= it.errorMessage %>
</div>
<script>
$("#closeBtn").click(function () {
$("#alert").fadeOut("slow", function () {
// Animation complete.
});
});
</script>
<% } %>
<div>
<a href="/"><i class="fas fa-arrow-left"></i> Back</a>

56
templates/profile.html Normal file
View File

@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Basic Page Needs
-->
<meta charset="utf-8">
<title><%= it.siteTitel %></title>
<meta name="description" content="<%= it.desc %>">
<meta name="author" content="<%= it.author %>">
<!-- Mobile Specific Metas
-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- FONT
-->
<!--<link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">-->
<!-- CSS
-->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/barebones.css">
<!-- Favicon
-->
<link rel="icon" type="image/png" href="images/favicon-16.png">
<script async defer src="/js/site.js"></script>
<script src="https://kit.fontawesome.com/<%= it.fontawesomeKey %>.js" crossorigin="anonymous"></script>
</head>
<body>
<!-- Primary Page Layout
-->
<header>
<a class="rightMount" href="/logout"><i class="fas fa-sign-out-alt"></i></a>
</header>
<div class="grid-container thirds" style="margin-top: 5%;">
<div></div>
<div>
<img src="<%= it.gravatarURL %>">
<h1><%= it.greeting %></h1>
<p>This site is still a bit of a placeholder. But it exists.</p>
</div>
</div>
</div>
</div>
<!-- End Document
-->
</body>
</html>

63
templates/redirect.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Basic Page Needs
-->
<meta charset="utf-8">
<title><%= it.siteTitel %></title>
<meta name="description" content="<%= it.desc %>">
<meta name="author" content="<%= it.author %>">
<!-- Mobile Specific Metas
-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSS
-->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/barebones.css">
<!-- Favicon
-->
<link rel="icon" type="image/png" href="images/favicon-16.png">
<script async defer src="/js/site.js"></script>
<script src="https://kit.fontawesome.com/<%= it.fontawesomeKey %>.js" crossorigin="anonymous"></script>
</head>
<body>
<!-- Primary Page Layout
-->
<div class="grid-container thirds" style="margin-top: 5%;">
<div></div>
<div>
<h1><i class="fas fas fa-arrow-right"></i></h1>
</div>
</div>
</div>
<div class="grid-container thirds" style="margin-top: 5%;">
<div></div>
<div>
<h1>You are being redirected</h1>
<script>
window.location.replace("<%= it.url %>");
</script>
<a href="<%= it.url %>">Click here if you are not being redirected.</a>
<br>
<a href="/"><button class="button-primary">
Return to the startpage
</button>
</a>
</p>
</div>
</div>
</div>
<!-- End Document
-->
</body>
</html>

View File

@ -1,110 +1,148 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Basic Page Needs
<head>
<!-- Basic Page Needs
-->
<meta charset="utf-8">
<title><%= it.siteTitel %></title>
<meta name="description" content="<%= it.desc %>">
<meta name="author" content="<%= it.author %>">
<meta charset="utf-8" />
<title><%= it.siteTitel %></title>
<meta name="description" content="<%= it.desc %>" />
<meta name="author" content="<%= it.author %>" />
<!-- Mobile Specific Metas
<!-- Mobile Specific Metas
-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- CSS
<!-- CSS
-->
<link rel="stylesheet" type="text/css" href="vendor/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="vendor/animate/animate.css">
<link rel="stylesheet" type="text/css" href="vendor/css-hamburgers/hamburgers.min.css">
<link rel="stylesheet" type="text/css" href="vendor/animsition/css/animsition.min.css">
<link rel="stylesheet" type="text/css" href="vendor/select2/select2.min.css">
<link rel="stylesheet" type="text/css" href="css/util.css">
<link rel="stylesheet" type="text/css" href="css/main.css">
<link
rel="stylesheet"
type="text/css"
href="vendor/bootstrap/css/bootstrap.min.css"
/>
<link rel="stylesheet" type="text/css" href="vendor/animate/animate.css" />
<link
rel="stylesheet"
type="text/css"
href="vendor/css-hamburgers/hamburgers.min.css"
/>
<link
rel="stylesheet"
type="text/css"
href="vendor/animsition/css/animsition.min.css"
/>
<link
rel="stylesheet"
type="text/css"
href="vendor/select2/select2.min.css"
/>
<link rel="stylesheet" type="text/css" href="css/util.css" />
<link rel="stylesheet" type="text/css" href="css/main.css" />
<!-- Favicon
<!-- Favicon
-->
<link rel="icon" type="image/png" href="images/favicon-16.png">
<link rel="icon" type="image/png" href="images/favicon-16.png" />
<script src="https://kit.fontawesome.com/<%= it.fontawesomeKey %>.js" crossorigin="anonymous"></script>
<script
src="https://kit.fontawesome.com/<%= it.fontawesomeKey %>.js"
crossorigin="anonymous"
></script>
<script src="vendor/jquery/jquery-3.2.1.min.js"></script>
</head>
<body>
<div class="limiter">
<div class="container-login100">
<div class="wrap-login100">
<% if(it.error == true) { %>
<div class="alert" id="alert">
<span class="closebtn" id="closeBtn">&times;</span>
<%= it.errorMessage %>
</div>
<script>
$("#closeBtn").click(function () {
$("#alert").fadeOut("slow", function () {
// Animation complete.
});
});
</script>
<% } %>
<div>
<a href="/"><i class="fas fa-arrow-left"></i> Back</a>
</div>
<form class="login100-form validate-form" method="post">
<span class="login100-form-title p-b-26"> Welcome </span>
<span class="login100-form-title p-b-48">
<i class="zmdi zmdi-font"></i>
</span>
</head>
<body>
<div class="limiter">
<div class="container-login100">
<div class="wrap-login100">
<div>
<a href="/"><i class="fas fa-arrow-left"></i> Back</a>
<div class="wrap-input100">
<input class="input100" type="text" name="username" />
<span class="focus-input100" data-placeholder="Username"></span>
</div>
<div
class="wrap-input100 validate-input"
data-validate="Valid email is: john@test.com"
>
<input class="input100" type="text" name="email" />
<span class="focus-input100" data-placeholder="Email"></span>
</div>
<div
class="wrap-input100 validate-input"
data-validate="Enter password"
>
<span class="btn-show-pass">
<i class="zmdi zmdi-eye"></i>
</span>
<input class="input100" type="password" name="pass" />
<span class="focus-input100" data-placeholder="Password"></span>
</div>
<div
class="wrap-input100 validate-input"
data-validate="Repeat password"
>
<span class="btn-show-pass">
<i class="zmdi zmdi-eye"></i>
</span>
<input class="input100" type="password" name="pass2" />
<span
class="focus-input100"
data-placeholder="Repeat Password"
></span>
</div>
<div class="h-captcha" data-sitekey="<%= it.sitekey %>"></div>
<script
src="https://hcaptcha.com/1/api.js"
async
defer
unsafe-eval
></script>
<div class="container-login100-form-btn">
<div class="wrap-login100-form-btn">
<div class="login100-form-bgbtn"></div>
<button class="login100-form-btn">Create a new account</button>
</div>
</div>
</form>
</div>
<form class="login100-form validate-form" method="post">
<span class="login100-form-title p-b-26">
Welcome
</span>
<span class="login100-form-title p-b-48">
<i class="zmdi zmdi-font"></i>
</span>
</div>
</div>
<div id="dropDownSelect1"></div>
<div class="wrap-input100">
<input class="input100" type="text" name="username">
<span class="focus-input100" data-placeholder="Username"></span>
</div>
<script src="vendor/animsition/js/animsition.min.js"></script>
<script src="vendor/bootstrap/js/popper.js"></script>
<script src="vendor/bootstrap/js/bootstrap.min.js"></script>
<script src="vendor/select2/select2.min.js"></script>
<script src="vendor/daterangepicker/moment.min.js"></script>
<script src="js/main.js"></script>
<div class="wrap-input100 validate-input" data-validate = "Valid email is: john@test.com">
<input class="input100" type="text" name="email">
<span class="focus-input100" data-placeholder="Email"></span>
</div>
<div class="wrap-input100 validate-input" data-validate="Enter password">
<span class="btn-show-pass">
<i class="zmdi zmdi-eye"></i>
</span>
<input class="input100" type="password" name="pass">
<span class="focus-input100" data-placeholder="Password"></span>
</div>
<div class="wrap-input100 validate-input" data-validate="Repeat password">
<span class="btn-show-pass">
<i class="zmdi zmdi-eye"></i>
</span>
<input class="input100" type="password" name="pass2">
<span class="focus-input100" data-placeholder="Repeat Password"></span>
</div>
<div class="h-captcha" data-sitekey="<%= it.sitekey %>"></div>
<script src="https://hcaptcha.com/1/api.js" async defer unsafe-eval></script>
<div class="container-login100-form-btn">
<div class="wrap-login100-form-btn">
<div class="login100-form-bgbtn"></div>
<button class="login100-form-btn">
Create a new account
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div id="dropDownSelect1"></div>
<script src="vendor/jquery/jquery-3.2.1.min.js"></script>
<script src="vendor/animsition/js/animsition.min.js"></script>
<script src="vendor/bootstrap/js/popper.js"></script>
<script src="vendor/bootstrap/js/bootstrap.min.js"></script>
<script src="vendor/select2/select2.min.js"></script>
<script src="vendor/daterangepicker/moment.min.js"></script>
<script src="js/main.js"></script>
<!-- End Document
<!-- End Document
-->
</body>
</body>
</html>