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;