Extract controller code

This commit is contained in:
Richard Ramos 2019-11-15 10:42:59 -04:00
parent 51fe1c24c0
commit 53adf04b16
4 changed files with 158 additions and 146 deletions

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"tabWidth": 2,
"useTabs": false,
"printWidth": 120
}

144
api/controller.js Normal file
View File

@ -0,0 +1,144 @@
const { validationResult } = require("express-validator");
const { isSignatureValid, getToken } = require("./utils");
const Subscribers = require("../models/subscribers");
const Verifications = require("../models/verifications");
class Controller {
static subscribe(dappConfig, mailer) {
return async (req, res) => {
const {
params: { dappId },
body: { address, email, signature }
} = req;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(404).json({ errors: errors.array() });
}
if (!dappConfig.isDapp(dappId)) {
return res.status(404).send("Invalid dapp");
}
if (!isSignatureValid(address, email, signature)) {
return res.status(404).send("Invalid signature");
}
// TODO: handle subscriptions to particular events
try {
const subscriber = await Subscribers.findOne({
dappId,
address
});
const t = getToken();
if (!subscriber) {
const s = await Subscribers.create({
dappId,
email,
address
});
await Verifications.create({
...t,
subscriber: s._id
});
} else if (!subscriber.isVerified) {
const d = new Date(subscriber.lastSignUpAttempt);
d.setMinutes(d.getMinutes() + 5);
if (d > new Date()) {
return res.status(400).send("You need to wait at least 5 minutes between sign up attempts");
}
subscriber.lastSignUpAttempt = d;
await subscriber.save();
await Verification.create({
...t,
subscriber: subscriber._id
});
}
if (!subscriber || !subscriber.isVerified) {
const template = dappConfig.template(dappId, "sign-up");
mailer.send(dappConfig.getEmailTemplate(dappId, template), dappConfig.config(dappId).from, {
email,
token: t.token
});
}
} catch (err) {
// TODO: add global error handler
return res.status(400).send(err.message);
}
return res.status(200).send("OK");
};
}
static unsubscribe(dappConfig) {
return async (req, res) => {
// TODO:
const {
params: { dappId },
body: { address, signature }
} = req;
if (!dappConfig.isDapp(dappId)) {
return res.status(404).send("Invalid dapp");
}
if (!isSignatureValid(address, dappId, signature)) {
return res.status(404).send("Invalid signature");
}
// TODO: handle unsubscribe to particular events
try {
await Subscribers.deleteOne({
dappId,
address
});
} catch (err) {
// TODO: add global error handler
return res.status(400).send(err.message);
}
return res.status(200).send("OK");
};
}
static confirm() {
return async (req, res) => {
const {
params: { token }
} = req;
const verification = await Verifications.findOne({
token
}).populate("subscriber");
if (verification) {
if (verification.expirationTime < new Date()) {
return res.status(400).send("Verification token already expired");
}
if (!verification.subscriber.isVerified) {
verification.subscriber.isVerified = true;
await verification.subscriber.save();
}
await Verifications.deleteMany({
subscriber: verification.subscriber._id
});
} else {
return res.status(400).send("Invalid verification token");
}
return res.status(200).send("OK");
};
}
}
module.exports = Controller;

View File

@ -1,20 +1,17 @@
const Events = require("events");
const { isSignatureValid, getToken, hexValidator } = require("./utils");
const { hexValidator } = require("./utils");
const express = require("express");
const { check, validationResult } = require("express-validator");
const { check } = require("express-validator");
const cors = require("cors");
const helmet = require("helmet");
const rateLimit = require("../middleware/rate-limit");
const config = require("../config");
const Database = require("../database");
const Subscribers = require("../models/subscribers");
const Verifications = require("../models/verifications");
const Mailer = require("../mail/sendgrid");
const DappConfig = require("../config/dapps");
const Controller = require("./controller");
const events = new Events();
const dappConfig = new DappConfig();
const mailer = new Mailer(config);
const db = new Database(events, config);
@ -47,84 +44,7 @@ events.on("db:connected", () => {
.isEmail(),
check("dappId").exists()
],
async (req, res) => {
const {
params: { dappId },
body: { address, email, signature }
} = req;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(404).json({ errors: errors.array() });
}
if (!dappConfig.isDapp(dappId)) {
return res.status(404).send("Invalid dapp");
}
if (!isSignatureValid(address, email, signature)) {
return res.status(404).send("Invalid signature");
}
// TODO: handle subscriptions to particular events
try {
const subscriber = await Subscribers.findOne({
dappId,
address
});
const t = getToken();
if (!subscriber) {
const s = await Subscribers.create({
dappId,
email,
address
});
await Verifications.create({
...t,
subscriber: s._id
});
} else if (!subscriber.isVerified) {
const d = new Date(subscriber.lastSignUpAttempt);
d.setMinutes(d.getMinutes() + 5);
if (d > new Date()) {
return res
.status(400)
.send(
"You need to wait at least 5 minutes between sign up attempts"
);
}
subscriber.lastSignUpAttempt = d;
await subscriber.save();
await Verification.create({
...t,
subscriber: subscriber._id
});
}
if (!subscriber || !subscriber.isVerified) {
const template = dappConfig.template(dappId, "sign-up");
mailer.send(
dappConfig.getEmailTemplate(dappId, template),
dappConfig.config(dappId).from,
{
email,
token: t.token
}
);
}
} catch (err) {
// TODO: add global error handler
return res.status(400).send(err.message);
}
return res.status(200).send("OK");
}
Controller.subscribe(dappConfig, mailer)
);
app.post(
@ -140,71 +60,14 @@ events.on("db:connected", () => {
.custom(hexValidator),
check("dappId").exists()
],
async (req, res) => {
// TODO:
const {
params: { dappId },
body: { address, signature }
} = req;
if (!dappConfig.isDapp(dappId)) {
return res.status(404).send("Invalid dapp");
}
if (!isSignatureValid(address, dappId, signature)) {
return res.status(404).send("Invalid signature");
}
// TODO: handle unsubscribe to particular events
try {
await Subscribers.deleteOne({
dappId,
address
});
} catch (err) {
// TODO: add global error handler
return res.status(400).send(err.message);
}
return res.status(200).send("OK");
}
Controller.unsubscribe(dappConfig)
);
app.get("/confirm/:token", [check("token").exists()], async (req, res) => {
const {
params: { token }
} = req;
const verification = await Verifications.findOne({
token
}).populate("subscriber");
if (verification) {
if (verification.expirationTime < new Date()) {
return res.status(400).send("Verification token already expired");
}
if (!verification.subscriber.isVerified) {
verification.subscriber.isVerified = true;
await verification.subscriber.save();
}
await Verifications.deleteMany({
subscriber: verification.subscriber._id
});
} else {
return res.status(400).send("Invalid verification token");
}
return res.status(200).send("OK");
});
app.get("/confirm/:token", [check("token").exists()], Controller.confirm());
app.get("/", (req, res) => res.status(200).json({ ok: true }));
app.listen(config.PORT, () =>
console.log(`App listening on port ${config.PORT}!`)
);
app.listen(config.PORT, () => console.log(`App listening on port ${config.PORT}!`));
});
// MVP

View File

@ -20,8 +20,8 @@ const config = {
/* Email */
SENDGRID_API_KEY: secret.SENDGRID_API_KEY,
/* WATCHER */
BLOCK_DELAY: 5, // ~60-70 secs... this could be helpful to avoid reorgs
POLL_SLEEP: 60 // seconds
BLOCK_DELAY: 6, // [Recommended 6 ~80 secs...] this could be helpful to avoid reorgs
POLL_SLEEP: 30 // seconds
};
module.exports = config;