/ api / controller.js
controller.js
  1  const { validationResult } = require("express-validator");
  2  const { isSignatureValid, getToken } = require("./utils");
  3  const Subscribers = require("../models/subscribers");
  4  const Verifications = require("../models/verifications");
  5  const BadRequest = require("./bad-request");
  6  
  7  class Controller {
  8    static subscribe(dappConfig, mailer) {
  9      return async (req, res, next) => {
 10        let {
 11          params: { dappId },
 12          body: { address, email, signature }
 13        } = req;
 14  
 15        const errors = validationResult(req);
 16        if (!errors.isEmpty()) {
 17          return next(new BadRequest(errors.array()));
 18        }
 19  
 20        if (!dappConfig.isDapp(dappId)) {
 21          return next(new BadRequest("DApp not found"));
 22        }
 23  
 24        if (!isSignatureValid(address, email, signature)) {
 25          return next(new BadRequest("Invalid signature"));
 26        }
 27  
 28        // TODO: handle subscriptions to particular events
 29  
 30        address = address.toLowerCase();
 31  
 32        try {
 33          const session = await Subscribers.startSession();
 34          session.startTransaction();
 35  
 36          const subscriber = await Subscribers.findOne({
 37            dappId,
 38            address
 39          });
 40  
 41          const t = getToken();
 42  
 43          if (!subscriber) {
 44            const s = await Subscribers.create({
 45              dappId,
 46              email,
 47              address
 48            });
 49  
 50            await Verifications.create({
 51              ...t,
 52              subscriber: s._id
 53            });
 54          } else if (!subscriber.isVerified) {
 55            const d = new Date(subscriber.lastSignUpAttempt);
 56            d.setMinutes(d.getMinutes() + 5);
 57            if (d > new Date()) {
 58              return next(new BadRequest("You need to wait at least 5 minutes between sign up attempts"));
 59            }
 60  
 61            subscriber.lastSignUpAttempt = d;
 62            subscriber.email = email;
 63  
 64            await subscriber.save();
 65  
 66            await Verifications.create({
 67              ...t,
 68              subscriber: subscriber._id
 69            });
 70          }
 71  
 72          if (!subscriber || !subscriber.isVerified) {
 73            const template = dappConfig.template(dappId, "subscribe");
 74            try {
 75              await mailer.send(
 76                dappConfig.getEmailTemplate(dappId, template),
 77                dappConfig.config(dappId).from,
 78                dappConfig.getVariables(dappId, {
 79                  email,
 80                  token: t.token
 81                })
 82              );
 83              session.commitTransaction();
 84            } catch (err) {
 85              session.abortTransaction();
 86            }
 87          } else {
 88            session.commitTransaction();
 89          }
 90        } catch (err) {
 91          return next(err);
 92        }
 93        return res.status(200).send("OK");
 94      };
 95    }
 96  
 97    static unsubscribe(dappConfig) {
 98      return async (req, res, next) => {
 99        let {
100          params: { dappId },
101          body: { address, signature }
102        } = req;
103  
104        const errors = validationResult(req);
105        if (!errors.isEmpty()) {
106          return next(new BadRequest(errors.array()));
107        }
108  
109        if (!dappConfig.isDapp(dappId)) {
110          return next(new BadRequest("DApp not found"));
111        }
112  
113        if (!isSignatureValid(address, dappId, signature)) {
114          return next(new BadRequest("Invalid signature"));
115        }
116  
117        // TODO: handle unsubscribe to particular events
118  
119        address = address.toLowerCase();
120  
121        try {
122          await Subscribers.deleteOne({
123            dappId,
124            address
125          });
126        } catch (err) {
127          return next(err);
128        }
129  
130        return res.status(200).send("OK");
131      };
132    }
133  
134    static confirm() {
135      return async (req, res, next) => {
136        const {
137          params: { token }
138        } = req;
139  
140        const errors = validationResult(req);
141        if (!errors.isEmpty()) {
142          return next(new BadRequest(errors.array()));
143        }
144  
145        try {
146          const session = await Verifications.startSession();
147          session.startTransaction();
148  
149          const verification = await Verifications.findOne({
150            token
151          }).populate("subscriber");
152  
153          if (verification) {
154            if (verification.expirationTime < new Date()) {
155              return next(new BadRequest("Verification token already expired"));
156            }
157  
158            if (!verification.subscriber.isVerified) {
159              verification.subscriber.isVerified = true;
160              await verification.subscriber.save();
161            }
162  
163            await Verifications.deleteMany({
164              subscriber: verification.subscriber._id
165            });
166  
167            session.commitTransaction();
168          } else {
169            session.abortTransaction();
170            return next(new BadRequest("Invalid verification token"));
171          }
172        } catch (err) {
173          return next(err);
174        }
175  
176        return res.status(200).send("OK");
177      };
178    }
179  
180    static userExists() {
181      return async (req, res, next) => {
182        let {
183          params: { dappId, address }
184        } = req;
185  
186        const errors = validationResult(req);
187        if (!errors.isEmpty()) {
188          return next(new BadRequest(errors.array()));
189        }
190  
191        address = address.toLowerCase();
192  
193        try {
194          const subscriber = await Subscribers.findOne({
195            dappId,
196            address,
197            isVerified: true
198          });
199  
200          return res.status(200).json({ isUser: subscriber ? true : false });
201        } catch (err) {
202          return next(err);
203        }
204      };
205    }
206  }
207  
208  module.exports = Controller;