-
Notifications
You must be signed in to change notification settings - Fork 6
Changes from 17 commits
c8e28c3
114fd6c
f32f312
5e60afa
6130a20
26a90ea
2e3061d
eca0a27
0f14479
8a36f8f
2733b98
b9dab0c
e4c05fa
5997abe
71f5915
be0e18d
a047889
3e9040e
25c3643
0a80382
9b2db41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
{ | ||
"extends": "airbnb-base" | ||
} | ||
"extends": "airbnb-base", | ||
"env": { | ||
"browser": true, | ||
"node": true | ||
} | ||
} |
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> |
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> |
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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing period at end of sentence? |
||
{% include 'footer' %} |
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' %} |
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> |
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> |
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> |
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); | ||
} | ||
} |
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('&')); | ||
}); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
User-agent: * | ||
Disallow: / |
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 -%} |
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> |
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> |
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 -%} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,10 @@ class Api { | |
this.validator = new Validator(); | ||
} | ||
|
||
static isSingleEmail(email) { | ||
return !/[,\s<>]/.test(email); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There appear to be a few modules out there that validate email address syntax according to the RFC. Would it be more appropriate to sanity check that the address syntax is truly valid before we attempt to send an email? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah there's some controversy around this topic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Found this site: https://emailregex.com There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should have clarified my concern better. I was thinking of the non-malicious case where a user simply entered a malformed email address and had to wait for us to actually try to send the email before receiving an error message indicating the address is invalid. I forgot to check the client side, and now see that you're using an But a better regex is still an improvement for our (future) REST API users. 😄 |
||
} | ||
|
||
async registrationMiddleware(req, res, next) { | ||
const errors = []; | ||
await Promise.all([req.body.email1, req.body.email2].map(email => ( | ||
|
@@ -124,13 +128,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.'], | ||
}); | ||
} | ||
} | ||
|
@@ -149,7 +153,14 @@ class Api { | |
|
||
static verifyEmailParam(req, res, next) { | ||
if (typeof req.body.email === 'string') { | ||
next(); | ||
if (Api.isSingleEmail(req.body.email)) { | ||
next(); | ||
} else { | ||
res.status(422).json({ | ||
status: 'Error', | ||
errors: ['Email has invalid format'], | ||
}); | ||
} | ||
} else { | ||
res.status(422).json({ | ||
status: 'Error', | ||
|
@@ -164,8 +175,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 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to consider having different ESLint configurations for the back-end and front-end sources so it detects incorrect Node usage in front-end code and vice versa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably the best option, I was just unsure on how to achieve that. I'll have a look