Skip to content
This repository has been archived by the owner on Feb 26, 2023. It is now read-only.

Add Front-End #7

Merged
merged 21 commits into from
Nov 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"extends": "airbnb-base"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"body-parser": "^1.18.3",
"express": "^4.16.4",
"express-async-errors": "^3.1.1",
"liquidjs": "^6.1.1",
"nconf": "^0.10.0",
"nodemailer": "^4.7.0",
"pg-promise": "^8.5.2",
Expand Down
7 changes: 7 additions & 0 deletions public/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "airbnb-base",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for future reference... ESLint has a cascading and hierarchical model for configurations. So, if in the future, you want to share a more complicated configuration between back-end and front-end code, it should Just Work in the current project layout.

TL;DR: This line shouldn't be necessary as it should be picked up from the parent configuration (unless there's something special about the extends property I don't know about).

"env": {
"browser": true,
"node": false
}
}
26 changes: 26 additions & 0 deletions public/email-templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>{{ subject }}</title>
<style media="screen">
a.big {
text-align: center;
}
a.big:link, a.big:visited {
background-color: #B8242B;
color: white;
padding: 14px 25px;
text-align: center;
text-decoration: none;
display: inline-block;
}
a.big:hover, a.big:active {
background-color: #8D1C21;
}
</style>
</head>
<body>
{% block -%}{%- endblock %}
</body>
</html>
5 changes: 5 additions & 0 deletions public/email-templates/footer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<br>
<footer>
<p>This email was sent automatically, if it wasn't you who requested this email, please ignore it.</p>
<p>You can always unsubscribe <a href="{{ unsub }}">here</a>.</p>
</footer>
10 changes: 10 additions & 0 deletions public/email-templates/verify-dice.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% layout 'base' %}
<h1>The dice have been cast!</h1>
<p>Roll-Time: {{ date }}</p>
<p>Results: {{ dice }}</p>
<br>
<p>You can click below to verify your results:</p>
<a class="big" href="{{ url }}">Verify!</a>
<br>
<p>Note we might not be able to correctly verify integrity for very old (1+ years) dice rolls.</p>
{% include 'footer' %}
7 changes: 7 additions & 0 deletions public/email-templates/verify-email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% layout 'base' %}
<h1>Verify your Email on {{ host }}</h1>
<p>You (or someone else prententing to be you) recently registered this email to be used for the dice rolling service.</p>
<p>In order to allow you to use this service you need to click 'Confirm!' below:</p>
<a class="big" href="{{ url }}">Confirm!</a>
<p>Note: The link expires after 24 hours or when a new confirmation email is sent.</p>
{% include 'footer' %}
13 changes: 13 additions & 0 deletions public/partials/form-ajaxify.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% include 'submit-button' %}
<span id="error-display" style="display: none; text-align: center;"></span>
<script type="text/javascript" src="./js/ajax-form.js"></script>
<script type="text/javascript">
registerForm(
'{{ formId }}',
'submit-button',
'error-display',
'{{ method }}',
'{{ url }}',
'{{ buttonText }}',
'Success!');
</script>
14 changes: 14 additions & 0 deletions public/partials/layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>{{title}} | TripleA Dice Server</title>
<link rel="stylesheet" type="text/css" href="./css/base.css">
</head>
<body>
<div class="center">
<h1>{{title}}</h1>
{% block -%}{%- endblock %}
</div>
</body>
</html>
5 changes: 5 additions & 0 deletions public/partials/submit-button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="extended">
<div class="button-center">
<button id="submit-button" type="submit" name="button">{{ buttonText }}</button>
</div>
</div>
64 changes: 64 additions & 0 deletions public/static/css/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
button {
background-color: #B8242B;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
}
button:disabled {
background-color: #B8242B7F;
}
button:hover:not(:disabled) {
background-color: #8D1C21;
color: #EEE;
}
input[type=text], input[type=email]{
width: 100%;
padding: 12px 20px;
margin: 8px 0;
box-sizing: border-box;
}
.center {
margin: auto;
width: 50%;
padding: 10%;
}
.button-center {
margin: auto;
width: 50%;
text-align: center;
}
h1 {
width: 100%;
text-align: center;
}
.extended {
width: 100%;
}
.lds-dual-ring {
display: inline-block;
width: 32px;
height: 32px;
}
.lds-dual-ring:after {
content: " ";
display: block;
width: 23px;
height: 23px;
margin: 1px;
border-radius: 50%;
border: 5px solid #fff;
border-color: #fff transparent #fff transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
@keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Binary file added public/static/favicon.ico
Binary file not shown.
30 changes: 30 additions & 0 deletions public/static/js/ajax-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
window.registerForm = (formId, buttonId, errorDisplayId, method, url, text, successText) => {
const form = document.getElementById(formId);
const button = document.getElementById(buttonId);
const errorDisplay = document.getElementById(errorDisplayId);
form.addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(form);
button.disabled = true;
button.value = '';
button.innerHTML = '<div class="lds-dual-ring"></div>';
const request = new XMLHttpRequest();
request.addEventListener('load', (serverResponse) => {
const response = JSON.parse(serverResponse.target.responseText);
if (response.status === 'OK') {
errorDisplay.style.display = 'none';
button.innerHTML = successText;
button.disabled = false;
} else {
errorDisplay.style.display = 'block';
errorDisplay.innerHTML = response.errors.join('<br>');
button.disabled = false;
button.innerHTML = text;
}
});
request.open(method, url);
// urlencode the FormData
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.send([...formData.entries()].map(e => `${encodeURIComponent(e[0])}=${encodeURIComponent(e[1])}`).join('&'));
});
};
2 changes: 2 additions & 0 deletions public/static/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-agent: *
Disallow: /
12 changes: 12 additions & 0 deletions public/views/confirm-register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{%- layout 'layout', title: 'Confirm Registration' -%}
{%- if email and token -%}
{%- capture url %}./api/register/{{ token }}{% endcapture -%}
{%- assign method = 'POST' -%}
{%- assign formId = 'form' -%}
<form id="{{ formId }}" action="{{ url }}" method="{{ method }}">
<input type="hidden" name="email" value="{{ email }}" required>
{% include 'form-ajaxify', buttonText: 'Confirm Registration!' %}
</form>
{%- else -%}
<h2>Invalid Arguments!</h2>
{%- endif -%}
8 changes: 8 additions & 0 deletions public/views/register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{%- layout 'layout', title: 'Register for the Dice-Server' -%}
{%- assign url ='./api/register' -%}
{%- assign method = 'POST' -%}
{%- assign formId = 'form' -%}
<form id="{{ formId }}" action="{{ url }}" method="{{ method }}">
<input type="email" name="email" placeholder="Insert your email adress" required>
{% include 'form-ajaxify', buttonText: 'Register!', formId: 'form' %}
</form>
9 changes: 9 additions & 0 deletions public/views/unregister.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{%- layout 'layout', title: 'Unregister from the dice Server' -%}
{%- assign url ='./api/unregister' -%}
{%- assign method = 'POST' -%}
{%- assign formId = 'form' -%}

<form id="{{ formId }}" action="{{ url }}" method="{{ method }}">
<input type="email" name="email" placeholder="Insert your email adress" value="{{ email }}" required>
{% include 'form-ajaxify', buttonText: 'Unregister!' %}
</form>
15 changes: 15 additions & 0 deletions public/views/verify.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{%- layout 'layout', title: 'Verify your dice' %}
{%- if invalid -%}
<h2>Invalid link!</h2>
{%- elsif token and dice and date -%}
{%- capture url %}./api/verify/{{ token }}{% endcapture -%}
{%- assign method = 'GET' -%}
{%- assign formId = 'form' -%}
<form id="{{ formId }}" action="{{ url }}" method="{{ method }}">
<p>Dice Rolled: {{ dice | join: ", " }}</p>
<p>At {{ date | date: "%a, %b %d, %y" }}</p>
{% include 'form-ajaxify', buttonText: 'Verify!' %}
</form>
{%- else -%}
<h2>Malformed JSON</h2>
{%- endif -%}
22 changes: 17 additions & 5 deletions src/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ const Validator = require('./validator');
const EmailManager = require('./email-manager.js');
const Handler = require('./db-handler');

const emailValidation = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

class Api {
constructor(database) {
this.dbHandler = new Handler(database);
this.emailManager = new EmailManager(this.dbHandler, nconf.get('smtp'), nconf.get('server'), nconf.get('emailsender'));
this.validator = new Validator();
}

static isEmail(email) {
return emailValidation.test(email);
}

async registrationMiddleware(req, res, next) {
const errors = [];
await Promise.all([req.body.email1, req.body.email2].map(email => (
Expand Down Expand Up @@ -124,13 +130,13 @@ class Api {
}

async handleEmailRegisterConfirm(req, res) {
const verified = await this.emailManager.verifyEmail(req.params.email, req.params.token);
const verified = await this.emailManager.verifyEmail(req.body.email, req.params.token);
if (verified) {
res.status(200).json({ status: 'OK' });
} else {
res.status(403).json({
status: 'Error',
errors: 'invalid Token or mail.',
errors: ['Invalid Token or E-Mail.'],
});
}
}
Expand All @@ -149,7 +155,14 @@ class Api {

static verifyEmailParam(req, res, next) {
if (typeof req.body.email === 'string') {
next();
if (Api.isEmail(req.body.email)) {
next();
} else {
res.status(422).json({
status: 'Error',
errors: ['Email has invalid format'],
});
}
} else {
res.status(422).json({
status: 'Error',
Expand All @@ -164,8 +177,7 @@ module.exports = (router, database) => {
router.get('/verify/:token', Api.validateVerifyArgs, api.handleVerify.bind(api));
router.post('/roll', api.registrationMiddleware.bind(api), Api.validateRollArgs, api.handleRoll.bind(api));
router.post('/register', Api.verifyEmailParam, api.handleEmailRegister.bind(api));
// TODO replace with frontend and merge with middleware above
router.get('/register/:email/:token', api.handleEmailRegisterConfirm.bind(api));
router.post('/register/:token', api.handleEmailRegisterConfirm.bind(api));
router.post('/unregister', Api.verifyEmailParam, api.handleEmailUnregister.bind(api));

// express.js behaves differently if no next parameter is used here
Expand Down
Loading